Skip to content

Commit 0bbdecb

Browse files
committed
feat: add --replace-registry-host=<npmjs|always|never>
1 parent 21b823e commit 0bbdecb

File tree

11 files changed

+374
-2
lines changed

11 files changed

+374
-2
lines changed

docs/content/using-npm/config.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1359,6 +1359,22 @@ The base URL of the npm registry.
13591359
<!-- automatically generated, do not edit manually -->
13601360
<!-- see lib/utils/config/definitions.js -->
13611361

1362+
#### `replace-registry-host`
1363+
1364+
* Default: "npmjs"
1365+
* Type: "npmjs", "never", or "always"
1366+
1367+
Defines behavior for replacing the registry host in a lockfile with the
1368+
configured registry.
1369+
1370+
The default behavior is to replace package dist URLs from the default
1371+
registry (https://registry.npmjs.org) to the configured registry. If set to
1372+
"never", then use the registry value. If set to "always", then replace the
1373+
registry host with the configured host every time.
1374+
1375+
<!-- automatically generated, do not edit manually -->
1376+
<!-- see lib/utils/config/definitions.js -->
1377+
13621378
#### `save`
13631379

13641380
* Default: `true` unless when using `npm update` where it defaults to `false`

lib/utils/config/definitions.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1618,6 +1618,22 @@ define('registry', {
16181618
flatten,
16191619
})
16201620

1621+
define('replace-registry-host', {
1622+
default: 'npmjs',
1623+
hint: '<npmjs|never|always>',
1624+
type: ['npmjs', 'never', 'always'],
1625+
description: `
1626+
Defines behavior for replacing the registry host in a lockfile with the
1627+
configured registry.
1628+
1629+
The default behavior is to replace package dist URLs from the default
1630+
registry (https://registry.npmjs.org) to the configured registry. If set to
1631+
"never", then use the registry value. If set to "always", then replace the
1632+
registry host with the configured host every time.
1633+
`,
1634+
flatten,
1635+
})
1636+
16211637
define('save', {
16221638
default: true,
16231639
defaultDescription: `\`true\` unless when using \`npm update\` where it

tap-snapshots/test/lib/commands/config.js.test.cjs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ exports[`test/lib/commands/config.js TAP config list --json > output matches sna
120120
"read-only": false,
121121
"rebuild-bundle": true,
122122
"registry": "https://registry.npmjs.org/",
123+
"replace-registry-host": "npmjs",
123124
"save": true,
124125
"save-bundle": false,
125126
"save-dev": false,
@@ -275,6 +276,7 @@ proxy = null
275276
read-only = false
276277
rebuild-bundle = true
277278
registry = "https://registry.npmjs.org/"
279+
replace-registry-host = "npmjs"
278280
save = true
279281
save-bundle = false
280282
save-dev = false

tap-snapshots/test/lib/utils/config/definitions.js.test.cjs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ Array [
115115
"read-only",
116116
"rebuild-bundle",
117117
"registry",
118+
"replace-registry-host",
118119
"save",
119120
"save-bundle",
120121
"save-dev",
@@ -1439,6 +1440,21 @@ exports[`test/lib/utils/config/definitions.js TAP > config description for regis
14391440
The base URL of the npm registry.
14401441
`
14411442

1443+
exports[`test/lib/utils/config/definitions.js TAP > config description for replace-registry-host 1`] = `
1444+
#### \`replace-registry-host\`
1445+
1446+
* Default: "npmjs"
1447+
* Type: "npmjs", "never", or "always"
1448+
1449+
Defines behavior for replacing the registry host in a lockfile with the
1450+
configured registry.
1451+
1452+
The default behavior is to replace package dist URLs from the default
1453+
registry (https://registry.npmjs.org) to the configured registry. If set to
1454+
"never", then use the registry value. If set to "always", then replace the
1455+
registry host with the configured host every time.
1456+
`
1457+
14421458
exports[`test/lib/utils/config/definitions.js TAP > config description for save 1`] = `
14431459
#### \`save\`
14441460

tap-snapshots/test/lib/utils/config/describe-all.js.test.cjs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1233,6 +1233,22 @@ The base URL of the npm registry.
12331233
<!-- automatically generated, do not edit manually -->
12341234
<!-- see lib/utils/config/definitions.js -->
12351235
1236+
#### \`replace-registry-host\`
1237+
1238+
* Default: "npmjs"
1239+
* Type: "npmjs", "never", or "always"
1240+
1241+
Defines behavior for replacing the registry host in a lockfile with the
1242+
configured registry.
1243+
1244+
The default behavior is to replace package dist URLs from the default
1245+
registry (https://registry.npmjs.org) to the configured registry. If set to
1246+
"never", then use the registry value. If set to "always", then replace the
1247+
registry host with the configured host every time.
1248+
1249+
<!-- automatically generated, do not edit manually -->
1250+
<!-- see lib/utils/config/definitions.js -->
1251+
12361252
#### \`save\`
12371253
12381254
* Default: \`true\` unless when using \`npm update\` where it defaults to \`false\`

test/lib/npm.js

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
const t = require('tap')
22
const { resolve, dirname, join } = require('path')
3+
const Arborist = require('@npmcli/arborist')
34

45
const { load: loadMockNpm } = require('../fixtures/mock-npm.js')
56
const mockGlobals = require('../fixtures/mock-globals')
@@ -654,3 +655,142 @@ t.test('implicit workspace accept', async t => {
654655
})
655656
await t.rejects(mock.npm.exec('org', []), /.*Usage/)
656657
})
658+
659+
t.test('replaceRegistryHost', async t => {
660+
const mock = await loadMockNpm(t, {
661+
prefixDir: {
662+
packages: {
663+
a: {
664+
'package.json': JSON.stringify({
665+
name: 'a',
666+
version: '1.0.0',
667+
scripts: { test: 'echo test a' },
668+
}),
669+
},
670+
},
671+
'package.json': JSON.stringify({
672+
name: 'root',
673+
version: '1.0.0',
674+
workspaces: ['./packages/a'],
675+
}),
676+
},
677+
globals: {
678+
'process.argv': [
679+
process.execPath,
680+
process.argv[1],
681+
'--color', 'false',
682+
'--workspace', './packages/a',
683+
],
684+
},
685+
})
686+
t.equal(mock.npm.flatOptions.replaceRegistryHost, 'npmjs')
687+
const arb = new Arborist({
688+
...mock.npm.flatOptions,
689+
})
690+
t.equal(arb.options.replaceRegistryHost, 'npmjs')
691+
})
692+
693+
t.test('replaceRegistryHost=garbage', async t => {
694+
const mock = await loadMockNpm(t, {
695+
prefixDir: {
696+
packages: {
697+
a: {
698+
'package.json': JSON.stringify({
699+
name: 'a',
700+
version: '1.0.0',
701+
scripts: { test: 'echo test a' },
702+
}),
703+
},
704+
},
705+
'package.json': JSON.stringify({
706+
name: 'root',
707+
version: '1.0.0',
708+
workspaces: ['./packages/a'],
709+
}),
710+
},
711+
globals: {
712+
'process.argv': [
713+
process.execPath,
714+
process.argv[1],
715+
'--color', 'false',
716+
'--workspace', './packages/a',
717+
'--replace-registry-host', 'garbage',
718+
],
719+
},
720+
})
721+
t.equal(mock.npm.flatOptions.replaceRegistryHost, 'npmjs')
722+
const arb = new Arborist({
723+
...mock.npm.flatOptions,
724+
})
725+
t.equal(arb.options.replaceRegistryHost, 'npmjs')
726+
})
727+
728+
t.test('replaceRegistryHost=never', async t => {
729+
const mock = await loadMockNpm(t, {
730+
prefixDir: {
731+
packages: {
732+
a: {
733+
'package.json': JSON.stringify({
734+
name: 'a',
735+
version: '1.0.0',
736+
scripts: { test: 'echo test a' },
737+
}),
738+
},
739+
},
740+
'package.json': JSON.stringify({
741+
name: 'root',
742+
version: '1.0.0',
743+
workspaces: ['./packages/a'],
744+
}),
745+
},
746+
globals: {
747+
'process.argv': [
748+
process.execPath,
749+
process.argv[1],
750+
'--color', 'false',
751+
'--workspace', './packages/a',
752+
'--replace-registry-host', 'never',
753+
],
754+
},
755+
})
756+
t.equal(mock.npm.flatOptions.replaceRegistryHost, 'never')
757+
const arb = new Arborist({
758+
...mock.npm.flatOptions,
759+
})
760+
t.equal(arb.options.replaceRegistryHost, 'never')
761+
})
762+
763+
t.test('replaceRegistryHost=always', async t => {
764+
const mock = await loadMockNpm(t, {
765+
prefixDir: {
766+
packages: {
767+
a: {
768+
'package.json': JSON.stringify({
769+
name: 'a',
770+
version: '1.0.0',
771+
scripts: { test: 'echo test a' },
772+
}),
773+
},
774+
},
775+
'package.json': JSON.stringify({
776+
name: 'root',
777+
version: '1.0.0',
778+
workspaces: ['./packages/a'],
779+
}),
780+
},
781+
globals: {
782+
'process.argv': [
783+
process.execPath,
784+
process.argv[1],
785+
'--color', 'false',
786+
'--workspace', './packages/a',
787+
'--replace-registry-host', 'always',
788+
],
789+
},
790+
})
791+
t.equal(mock.npm.flatOptions.replaceRegistryHost, 'always')
792+
const arb = new Arborist({
793+
...mock.npm.flatOptions,
794+
})
795+
t.equal(arb.options.replaceRegistryHost, 'always')
796+
})

workspaces/arborist/lib/arborist/index.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,14 @@ class Arborist extends Base {
7474
cache: options.cache || `${homedir()}/.npm/_cacache`,
7575
packumentCache: options.packumentCache || new Map(),
7676
workspacesEnabled: options.workspacesEnabled !== false,
77+
replaceRegistryHost: options.replaceRegistryHost,
7778
lockfileVersion: lockfileVersion(options.lockfileVersion),
7879
}
80+
if (options.replaceRegistryHost !== 'never'
81+
&& options.replaceRegistryHost !== 'always'
82+
) {
83+
this.options.replaceRegistryHost = 'npmjs'
84+
}
7985

8086
this[_workspacesEnabled] = this.options.workspacesEnabled
8187

workspaces/arborist/lib/arborist/reify.js

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -716,8 +716,15 @@ module.exports = cls => class Reifier extends cls {
716716
// ${REGISTRY} or something. This has to be threaded through the
717717
// Shrinkwrap and Node classes carefully, so for now, just treat
718718
// the default reg as the magical animal that it has been.
719-
return resolved && resolved
720-
.replace(/^https?:\/\/registry\.npmjs\.org\//, this.registry)
719+
const resolvedURL = new URL(resolved)
720+
if (resolved
721+
&& ((this.options.replaceRegistryHost === 'npmjs'
722+
&& resolvedURL.origin === 'https://registry.npmjs.org')
723+
|| this.options.replaceRegistryHost === 'always')
724+
) {
725+
return new URL(resolvedURL.pathname, this.registry).href
726+
}
727+
return resolved
721728
}
722729

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

workspaces/arborist/test/arborist/index.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,3 +236,12 @@ t.test('lockfileVersion config validation', async t => {
236236
message: 'Invalid lockfileVersion config: banana',
237237
})
238238
})
239+
240+
t.test('valid replaceRegistryHost values', t => {
241+
t.equal(new Arborist({ replaceRegistryHost: 'garbage' }).options.replaceRegistryHost, 'npmjs')
242+
t.equal(new Arborist({ replaceRegistryHost: 'npmjs' }).options.replaceRegistryHost, 'npmjs')
243+
t.equal(new Arborist({ replaceRegistryHost: undefined }).options.replaceRegistryHost, 'npmjs')
244+
t.equal(new Arborist({ replaceRegistryHost: 'always' }).options.replaceRegistryHost, 'always')
245+
t.equal(new Arborist({ replaceRegistryHost: 'never' }).options.replaceRegistryHost, 'never')
246+
t.end()
247+
})

0 commit comments

Comments
 (0)