Skip to content

Commit

Permalink
feat: add --replace-registry-host=<npmjs|always|never>|<hostname>
Browse files Browse the repository at this point in the history
  • Loading branch information
fritzy committed Aug 2, 2022
1 parent f9abee7 commit 17721c6
Show file tree
Hide file tree
Showing 10 changed files with 240 additions and 2 deletions.
18 changes: 18 additions & 0 deletions docs/content/using-npm/config.md
Expand Up @@ -1393,6 +1393,24 @@ The base URL of the npm registry.
<!-- automatically generated, do not edit manually -->
<!-- see lib/utils/config/definitions.js -->

#### `replace-registry-host`

* Default: "npmjs"
* Type: "npmjs", "never", "always", or String

Defines behavior for replacing the registry host in a lockfile with the
configured registry.

The default behavior is to replace package dist URLs from the default
registry (https://registry.npmjs.org) to the configured registry. If set to
"never", then use the registry value. If set to "always", then replace the
registry host with the configured host every time.

You may also specify a bare hostname (e.g., "registry.npmjs.org").

<!-- automatically generated, do not edit manually -->
<!-- see lib/utils/config/definitions.js -->

#### `save`

* Default: `true` unless when using `npm update` where it defaults to `false`
Expand Down
18 changes: 18 additions & 0 deletions lib/utils/config/definitions.js
Expand Up @@ -1649,6 +1649,24 @@ define('registry', {
flatten,
})

define('replace-registry-host', {
default: 'npmjs',
hint: '<npmjs|never|always> | hostname',
type: ['npmjs', 'never', 'always', String],
description: `
Defines behavior for replacing the registry host in a lockfile with the
configured registry.
The default behavior is to replace package dist URLs from the default
registry (https://registry.npmjs.org) to the configured registry. If set to
"never", then use the registry value. If set to "always", then replace the
registry host with the configured host every time.
You may also specify a bare hostname (e.g., "registry.npmjs.org").
`,
flatten,
})

define('save', {
default: true,
defaultDescription: `\`true\` unless when using \`npm update\` where it
Expand Down
2 changes: 2 additions & 0 deletions tap-snapshots/test/lib/commands/config.js.test.cjs
Expand Up @@ -121,6 +121,7 @@ exports[`test/lib/commands/config.js TAP config list --json > output matches sna
"read-only": false,
"rebuild-bundle": true,
"registry": "https://registry.npmjs.org/",
"replace-registry-host": "npmjs",
"save": true,
"save-bundle": false,
"save-dev": false,
Expand Down Expand Up @@ -277,6 +278,7 @@ proxy = null
read-only = false
rebuild-bundle = true
registry = "https://registry.npmjs.org/"
replace-registry-host = "npmjs"
save = true
save-bundle = false
save-dev = false
Expand Down
18 changes: 18 additions & 0 deletions tap-snapshots/test/lib/utils/config/definitions.js.test.cjs
Expand Up @@ -116,6 +116,7 @@ Array [
"read-only",
"rebuild-bundle",
"registry",
"replace-registry-host",
"save",
"save-bundle",
"save-dev",
Expand Down Expand Up @@ -1460,6 +1461,23 @@ exports[`test/lib/utils/config/definitions.js TAP > config description for regis
The base URL of the npm registry.
`

exports[`test/lib/utils/config/definitions.js TAP > config description for replace-registry-host 1`] = `
#### \`replace-registry-host\`
* Default: "npmjs"
* Type: "npmjs", "never", "always", or String
Defines behavior for replacing the registry host in a lockfile with the
configured registry.
The default behavior is to replace package dist URLs from the default
registry (https://registry.npmjs.org) to the configured registry. If set to
"never", then use the registry value. If set to "always", then replace the
registry host with the configured host every time.
You may also specify a bare hostname (e.g., "registry.npmjs.org").
`

exports[`test/lib/utils/config/definitions.js TAP > config description for save 1`] = `
#### \`save\`
Expand Down
18 changes: 18 additions & 0 deletions tap-snapshots/test/lib/utils/config/describe-all.js.test.cjs
Expand Up @@ -1266,6 +1266,24 @@ The base URL of the npm registry.
<!-- automatically generated, do not edit manually -->
<!-- see lib/utils/config/definitions.js -->
#### \`replace-registry-host\`
* Default: "npmjs"
* Type: "npmjs", "never", "always", or String
Defines behavior for replacing the registry host in a lockfile with the
configured registry.
The default behavior is to replace package dist URLs from the default
registry (https://registry.npmjs.org) to the configured registry. If set to
"never", then use the registry value. If set to "always", then replace the
registry host with the configured host every time.
You may also specify a bare hostname (e.g., "registry.npmjs.org").
<!-- automatically generated, do not edit manually -->
<!-- see lib/utils/config/definitions.js -->
#### \`save\`
* Default: \`true\` unless when using \`npm update\` where it defaults to \`false\`
Expand Down
4 changes: 4 additions & 0 deletions workspaces/arborist/lib/arborist/index.js
Expand Up @@ -74,8 +74,12 @@ class Arborist extends Base {
cache: options.cache || `${homedir()}/.npm/_cacache`,
packumentCache: options.packumentCache || new Map(),
workspacesEnabled: options.workspacesEnabled !== false,
replaceRegistryHost: options.replaceRegistryHost,
lockfileVersion: lockfileVersion(options.lockfileVersion),
}
this.replaceRegistryHost = this.options.replaceRegistryHost =
(!this.options.replaceRegistryHost || this.options.replaceRegistryHost === 'npmjs') ?
'registry.npmjs.org' : this.options.replaceRegistryHost

this[_workspacesEnabled] = this.options.workspacesEnabled

Expand Down
11 changes: 9 additions & 2 deletions workspaces/arborist/lib/arborist/reify.js
Expand Up @@ -717,8 +717,15 @@ 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.
return resolved && resolved
.replace(/^https?:\/\/registry\.npmjs\.org\//, this.registry)
const resolvedURL = new URL(resolved)
if (resolved
&& ((this.options.replaceRegistryHost === resolvedURL.hostname)
|| this.options.replaceRegistryHost === 'always')
) {
// this.registry always has a trailing slash
resolved = `${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
9 changes: 9 additions & 0 deletions workspaces/arborist/test/arborist/index.js
Expand Up @@ -236,3 +236,12 @@ t.test('lockfileVersion config validation', async t => {
message: 'Invalid lockfileVersion config: banana',
})
})

t.test('valid replaceRegistryHost values', t => {
t.equal(new Arborist({ replaceRegistryHost: 'registry.garbage.com' }).options.replaceRegistryHost, 'registry.garbage.com')
t.equal(new Arborist({ replaceRegistryHost: 'npmjs' }).options.replaceRegistryHost, 'registry.npmjs.org')
t.equal(new Arborist({ replaceRegistryHost: undefined }).options.replaceRegistryHost, 'registry.npmjs.org')
t.equal(new Arborist({ replaceRegistryHost: 'always' }).options.replaceRegistryHost, 'always')
t.equal(new Arborist({ replaceRegistryHost: 'never' }).options.replaceRegistryHost, 'never')
t.end()
})
130 changes: 130 additions & 0 deletions workspaces/arborist/test/arborist/reify.js
Expand Up @@ -2,6 +2,7 @@ const { join, resolve, basename } = require('path')
const t = require('tap')
const runScript = require('@npmcli/run-script')
const localeCompare = require('@isaacs/string-locale-compare')('en')
const tnock = require('../fixtures/tnock')

// mock rimraf so we can make it fail in rollback tests
const realRimraf = require('rimraf')
Expand Down Expand Up @@ -2923,3 +2924,132 @@ t.test('installLinks', (t) => {

t.end()
})

t.only('should preserve exact ranges, missing actual tree', async (t) => {
const Arborist = require('../../lib/index.js')
const abbrev = resolve(__dirname,
'../fixtures/registry-mocks/content/abbrev/-/abbrev-1.1.1.tgz')
const abbrevTGZ = fs.readFileSync(abbrev)

const abbrevPackument = 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://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz',
},
},
},
})

const abbrevPackument2 = 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://registry.garbage.org/abbrev/-/abbrev-1.1.1.tgz',
},
},
},
})

t.only('host should not be replaced replaceRegistryHost=never', async (t) => {
const testdir = t.testdir({
project: {
'package.json': JSON.stringify({
name: 'myproject',
version: '1.0.0',
dependencies: {
abbrev: '1.1.1',
},
}),
},
})

tnock(t, 'https://registry.github.com')
.get('/abbrev')
.reply(200, abbrevPackument)

tnock(t, 'https://registry.npmjs.org')
.get('/abbrev/-/abbrev-1.1.1.tgz')
.reply(200, abbrevTGZ)

const arb = new Arborist({
path: resolve(testdir, 'project'),
registry: 'https://registry.github.com',
cache: resolve(testdir, 'cache'),
replaceRegistryHost: 'never',
})
await arb.reify()
})

t.only('host should be replaced replaceRegistryHost=npmjs', async (t) => {
const testdir = t.testdir({
project: {
'package.json': JSON.stringify({
name: 'myproject',
version: '1.0.0',
dependencies: {
abbrev: '1.1.1',
},
}),
},
})

tnock(t, 'https://registry.github.com')
.get('/abbrev')
.reply(200, abbrevPackument)

tnock(t, 'https://registry.github.com')
.get('/abbrev/-/abbrev-1.1.1.tgz')
.reply(200, abbrevTGZ)

const arb = new Arborist({
path: resolve(testdir, 'project'),
registry: 'https://registry.github.com',
cache: resolve(testdir, 'cache'),
replaceRegistryHost: 'npmjs',
})
await arb.reify()
})

t.only('host should be always replaceRegistryHost=always', async (t) => {
const testdir = t.testdir({
project: {
'package.json': JSON.stringify({
name: 'myproject',
version: '1.0.0',
dependencies: {
abbrev: '1.1.1',
},
}),
},
})

tnock(t, 'https://registry.github.com')
.get('/abbrev')
.reply(200, abbrevPackument2)

tnock(t, 'https://registry.github.com')
.get('/abbrev/-/abbrev-1.1.1.tgz')
.reply(200, abbrevTGZ)

const arb = new Arborist({
path: resolve(testdir, 'project'),
registry: 'https://registry.github.com',
cache: resolve(testdir, 'cache'),
replaceRegistryHost: 'always',
})
await arb.reify()
})
})
14 changes: 14 additions & 0 deletions workspaces/arborist/test/fixtures/tnock.js
@@ -0,0 +1,14 @@
'use strict'

const nock = require('nock')

module.exports = tnock
function tnock (t, host) {
const server = nock(host)
nock.disableNetConnect()
t.teardown(function () {
nock.enableNetConnect()
server.done()
})
return server
}

0 comments on commit 17721c6

Please sign in to comment.