Skip to content

fix(arborist): only replace hostname for resolved URL #8185

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Mar 26, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 14 additions & 13 deletions workspaces/arborist/lib/arborist/reify.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ const semver = require('semver')
const debug = require('../debug.js')
const { walkUp } = require('walk-up-path')
const { log, time } = require('proc-log')
const hgi = require('hosted-git-info')
const rpj = require('read-package-json-fast')

const { dirname, resolve, relative, join } = require('node:path')
Expand Down Expand Up @@ -833,21 +832,23 @@ module.exports = cls => class Reifier extends cls {
// ${REGISTRY} or something. This has to be threaded through the
// Shrinkwrap and Node classes carefully, so for now, just treat
// the default reg as the magical animal that it has been.
const resolvedURL = hgi.parseUrl(resolved)

if (!resolvedURL) {
try {
const resolvedURL = new URL(resolved)

if ((this.options.replaceRegistryHost === resolvedURL.hostname) ||
this.options.replaceRegistryHost === 'always') {
const registryURL = new URL(this.registry)
// Replace the host with the registry host while keeping the path intact
resolvedURL.hostname = registryURL.hostname
resolvedURL.port = registryURL.port
return resolvedURL.toString()
}
return resolved
} catch (e) {
// if we could not parse the url at all then returning nothing
// here means it will get removed from the tree in the next step
return
return undefined
}

if ((this.options.replaceRegistryHost === resolvedURL.hostname)
|| this.options.replaceRegistryHost === 'always') {
// this.registry always has a trailing slash
return `${this.registry.slice(0, -1)}${resolvedURL.pathname}${resolvedURL.searchParams}`
}

return resolved
}

// bundles are *sort of* like shrinkwraps, in that the branch is defined
Expand Down
54 changes: 50 additions & 4 deletions workspaces/arborist/test/arborist/reify.js
Original file line number Diff line number Diff line change
Expand Up @@ -3171,7 +3171,7 @@ t.test('installLinks', (t) => {
t.end()
})

t.only('should preserve exact ranges, missing actual tree', async (t) => {
t.test('should preserve exact ranges, missing actual tree', async (t) => {
const Pacote = require('pacote')
const Arborist = t.mock('../../lib/arborist', {
pacote: {
Expand Down Expand Up @@ -3256,7 +3256,7 @@ t.only('should preserve exact ranges, missing actual tree', async (t) => {
},
})

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

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

t.only('host should be always replaceRegistryHost=always', async (t) => {
t.test('host should be always replaceRegistryHost=always', async (t) => {
const testdir = t.testdir({
project: {
'package.json': JSON.stringify({
Expand Down Expand Up @@ -3375,6 +3375,52 @@ t.only('should preserve exact ranges, missing actual tree', async (t) => {
})
await arb.reify()
})

t.test('registry with path should only swap hostname', async (t) => {
const abbrevPackument3 = JSON.stringify({
_id: 'abbrev',
_rev: 'lkjadflkjasdf',
name: 'abbrev',
'dist-tags': { latest: '1.1.1' },
versions: {
'1.1.1': {
name: 'abbrev',
version: '1.1.1',
dist: {
tarball: 'https://artifactory.example.com/api/npm/npm-all/abbrev/-/abbrev-1.1.1.tgz',
},
},
},
})

const testdir = t.testdir({
project: {
'package.json': JSON.stringify({
name: 'myproject',
version: '1.0.0',
dependencies: {
abbrev: '1.1.1',
},
}),
},
})

tnock(t, 'https://new-host.artifactory.example.com')
.get('/api/npm/npm-all/abbrev')
.reply(200, abbrevPackument3)

tnock(t, 'https://new-host.artifactory.example.com')
.get('/api/npm/npm-all/abbrev/-/abbrev-1.1.1.tgz')
.reply(200, abbrevTGZ)

const arb = new Arborist({
path: resolve(testdir, 'project'),
registry: 'https://new-host.artifactory.example.com/api/npm/npm-all',
cache: resolve(testdir, 'cache'),
replaceRegistryHost: 'always',
})
await arb.reify()
})
})

t.test('install stategy linked', async (t) => {
Expand Down
Loading