Skip to content

Commit 885accd

Browse files
fix(arborist): only replace hostname for resolved URL (#8185)
Related to #6110 There is an edge case where if an npm project is using a registry with a path the `replace-registry-host` is including the path from the registry url. Current Behavior: - Inputs: - `registry=https://internal.mycompany.com/artifactory/api/npm/npm-all` - `replace-registry-host=always` - resolved: "https://external.mycompany.com/artifactory/api/npm/npm-all" (package-lock.json) - Output: "https://internal.mycompany.com/artifactory/api/npm/npm-all/api/npm/npm-all" Expected Behavior: - Inputs: - `registry=https://internal.mycompany.com/artifactory/api/npm/npm-all` - `replace-registry-host=always` - resolved: "https://external.mycompany.com/artifactory/api/npm/npm-all" (package-lock.json) - Output: "https://internal.mycompany.com/artifactory/api/npm/npm-all" This fix resolves this inconsistency by refactoring the logic to construct URL objects from the registry and resolved strings and maps the hostname of the new registry to the hostname of the resolved url instead of doing string parsing. I'm welcome to any feedback on this solution!
1 parent 88a7b52 commit 885accd

File tree

2 files changed

+64
-17
lines changed

2 files changed

+64
-17
lines changed

workspaces/arborist/lib/arborist/reify.js

+14-13
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ const semver = require('semver')
88
const debug = require('../debug.js')
99
const { walkUp } = require('walk-up-path')
1010
const { log, time } = require('proc-log')
11-
const hgi = require('hosted-git-info')
1211
const rpj = require('read-package-json-fast')
1312

1413
const { dirname, resolve, relative, join } = require('node:path')
@@ -833,21 +832,23 @@ module.exports = cls => class Reifier extends cls {
833832
// ${REGISTRY} or something. This has to be threaded through the
834833
// Shrinkwrap and Node classes carefully, so for now, just treat
835834
// the default reg as the magical animal that it has been.
836-
const resolvedURL = hgi.parseUrl(resolved)
837-
838-
if (!resolvedURL) {
835+
try {
836+
const resolvedURL = new URL(resolved)
837+
838+
if ((this.options.replaceRegistryHost === resolvedURL.hostname) ||
839+
this.options.replaceRegistryHost === 'always') {
840+
const registryURL = new URL(this.registry)
841+
// Replace the host with the registry host while keeping the path intact
842+
resolvedURL.hostname = registryURL.hostname
843+
resolvedURL.port = registryURL.port
844+
return resolvedURL.toString()
845+
}
846+
return resolved
847+
} catch (e) {
839848
// if we could not parse the url at all then returning nothing
840849
// here means it will get removed from the tree in the next step
841-
return
850+
return undefined
842851
}
843-
844-
if ((this.options.replaceRegistryHost === resolvedURL.hostname)
845-
|| this.options.replaceRegistryHost === 'always') {
846-
// this.registry always has a trailing slash
847-
return `${this.registry.slice(0, -1)}${resolvedURL.pathname}${resolvedURL.searchParams}`
848-
}
849-
850-
return resolved
851852
}
852853

853854
// bundles are *sort of* like shrinkwraps, in that the branch is defined

workspaces/arborist/test/arborist/reify.js

+50-4
Original file line numberDiff line numberDiff line change
@@ -3171,7 +3171,7 @@ t.test('installLinks', (t) => {
31713171
t.end()
31723172
})
31733173

3174-
t.only('should preserve exact ranges, missing actual tree', async (t) => {
3174+
t.test('should preserve exact ranges, missing actual tree', async (t) => {
31753175
const Pacote = require('pacote')
31763176
const Arborist = t.mock('../../lib/arborist', {
31773177
pacote: {
@@ -3256,7 +3256,7 @@ t.only('should preserve exact ranges, missing actual tree', async (t) => {
32563256
},
32573257
})
32583258

3259-
t.only('host should not be replaced replaceRegistryHost=never', async (t) => {
3259+
t.test('host should not be replaced replaceRegistryHost=never', async (t) => {
32603260
const testdir = t.testdir({
32613261
project: {
32623262
'package.json': JSON.stringify({
@@ -3296,7 +3296,7 @@ t.only('should preserve exact ranges, missing actual tree', async (t) => {
32963296
await arb.reify()
32973297
})
32983298

3299-
t.only('host should be replaced replaceRegistryHost=npmjs', async (t) => {
3299+
t.test('host should be replaced replaceRegistryHost=npmjs', async (t) => {
33003300
const testdir = t.testdir({
33013301
project: {
33023302
'package.json': JSON.stringify({
@@ -3336,7 +3336,7 @@ t.only('should preserve exact ranges, missing actual tree', async (t) => {
33363336
await arb.reify()
33373337
})
33383338

3339-
t.only('host should be always replaceRegistryHost=always', async (t) => {
3339+
t.test('host should be always replaceRegistryHost=always', async (t) => {
33403340
const testdir = t.testdir({
33413341
project: {
33423342
'package.json': JSON.stringify({
@@ -3375,6 +3375,52 @@ t.only('should preserve exact ranges, missing actual tree', async (t) => {
33753375
})
33763376
await arb.reify()
33773377
})
3378+
3379+
t.test('registry with path should only swap hostname', async (t) => {
3380+
const abbrevPackument3 = JSON.stringify({
3381+
_id: 'abbrev',
3382+
_rev: 'lkjadflkjasdf',
3383+
name: 'abbrev',
3384+
'dist-tags': { latest: '1.1.1' },
3385+
versions: {
3386+
'1.1.1': {
3387+
name: 'abbrev',
3388+
version: '1.1.1',
3389+
dist: {
3390+
tarball: 'https://artifactory.example.com/api/npm/npm-all/abbrev/-/abbrev-1.1.1.tgz',
3391+
},
3392+
},
3393+
},
3394+
})
3395+
3396+
const testdir = t.testdir({
3397+
project: {
3398+
'package.json': JSON.stringify({
3399+
name: 'myproject',
3400+
version: '1.0.0',
3401+
dependencies: {
3402+
abbrev: '1.1.1',
3403+
},
3404+
}),
3405+
},
3406+
})
3407+
3408+
tnock(t, 'https://new-host.artifactory.example.com')
3409+
.get('/api/npm/npm-all/abbrev')
3410+
.reply(200, abbrevPackument3)
3411+
3412+
tnock(t, 'https://new-host.artifactory.example.com')
3413+
.get('/api/npm/npm-all/abbrev/-/abbrev-1.1.1.tgz')
3414+
.reply(200, abbrevTGZ)
3415+
3416+
const arb = new Arborist({
3417+
path: resolve(testdir, 'project'),
3418+
registry: 'https://new-host.artifactory.example.com/api/npm/npm-all',
3419+
cache: resolve(testdir, 'cache'),
3420+
replaceRegistryHost: 'always',
3421+
})
3422+
await arb.reify()
3423+
})
33783424
})
33793425

33803426
t.test('install stategy linked', async (t) => {

0 commit comments

Comments
 (0)