Skip to content

Commit db5ca7b

Browse files
author
Caleb ツ Everett
committed
feat: add record default registry option
Create shrinkwrap files with resolved urls modified to replace the configured registry with the default registry, https://registry.npmjs.org. The default registry is a magic value meaning the current registry, so recording resolved with the default registry allows users to switch to a different registry without removing their lockfile. The path portion of the acutal resolved url is preserved so this trick only works when the different registries host tarballs at the same relative paths. It's faster than the omitLockfileRegistryResolved option because npm doesn't need to fetch each pacument to resolve the tarball url.
1 parent 7aaa344 commit db5ca7b

File tree

3 files changed

+76
-13
lines changed

3 files changed

+76
-13
lines changed

workspaces/arborist/lib/override-resolves.js

+44-3
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,51 @@
1-
function overrideResolves (resolved, opts = {}) {
2-
const { omitLockfileRegistryResolved = false } = opts
1+
const npa = require('npm-package-arg')
32

4-
if (omitLockfileRegistryResolved) {
3+
function overrideResolves (node, resolved, opts = {}) {
4+
const {
5+
omitLockfileRegistryResolved = false,
6+
recordDefaultRegistry = false,
7+
} = opts
8+
9+
const isRegistryDependency = node.isRegistryDependency
10+
11+
// omit the resolved url of registry dependencies. this makes installs slower
12+
// because npm must resolve the url for each package version but it allows
13+
// users to use a lockfile across registries that host tarballs at different
14+
// paths.
15+
if (isRegistryDependency && omitLockfileRegistryResolved) {
516
return undefined
617
}
718

19+
// replace the configured registry with the default registry. the default
20+
// registry is a magic value meaning the current registry, so recording
21+
// resolved with the default registry allows users to switch to a
22+
// different registry without removing their lockfile. The path portion
23+
// of the resolved url is preserved so this trick only works when the
24+
// different registries host tarballs at the same relative paths.
25+
if (isRegistryDependency && recordDefaultRegistry) {
26+
const scope = npa(node.packageName).scope
27+
const registry = scope && opts[`${scope}:registry`]
28+
? opts[`${scope}:registry`]
29+
: opts.registry
30+
31+
// normalize registry url - strip trailing slash.
32+
// TODO improve normalization for both the configured registry and resolved
33+
// url. consider port, protocol, more path normalization.
34+
const normalized = registry.endsWith('/')
35+
? registry.slice(0, -1)
36+
: registry
37+
38+
// only replace the host if the resolved url is for the configured
39+
// registry. registries may host tarballs on another server. return
40+
// undefined so npm will re-resolve the url from the current registry when
41+
// it reads the lockfile.
42+
if (resolved.startsWith(normalized)) {
43+
return 'https://registry.npmjs.org' + resolved.slice(normalized.length)
44+
} else {
45+
return undefined
46+
}
47+
}
48+
849
return resolved
950
}
1051

workspaces/arborist/lib/shrinkwrap.js

+3-8
Original file line numberDiff line numberDiff line change
@@ -300,13 +300,8 @@ class Shrinkwrap {
300300
})
301301

302302
const resolved = consistentResolve(node.resolved, node.path, path, true)
303-
// hide resolved from registry dependencies.
304-
if (!resolved) {
305-
// no-op
306-
} else if (node.isRegistryDependency) {
307-
meta.resolved = overrideResolves(resolved, options)
308-
} else {
309-
meta.resolved = resolved
303+
if (resolved) {
304+
meta.resolved = overrideResolves(node, resolved, options)
310305
}
311306

312307
if (node.extraneous) {
@@ -1030,7 +1025,7 @@ class Shrinkwrap {
10301025
spec.type !== 'git' &&
10311026
spec.type !== 'file' &&
10321027
spec.type !== 'remote') {
1033-
lock.resolved = overrideResolves(node.resolved, this.resolveOptions)
1028+
lock.resolved = overrideResolves(node, node.resolved, this.resolveOptions)
10341029
}
10351030

10361031
if (node.integrity) {

workspaces/arborist/test/shrinkwrap.js

+29-2
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,7 @@ t.only('resolveOptions', async t => {
247247
dependencies: {
248248
registry: '^1.0.0',
249249
'some-other-registry': '^1.0.0',
250-
'@scoped/some-other-registry': '^1.0.0',
250+
'@scope/some-package': '^1.0.0',
251251
tar: url,
252252
},
253253
},
@@ -271,7 +271,7 @@ t.only('resolveOptions', async t => {
271271
})
272272

273273
const scopedOtherRegistry = new Node({
274-
pkg: { name: '@scope/some-other-registry', version: '1.2.3' },
274+
pkg: { name: '@scope/some-package', version: '1.2.3' },
275275
resolved: someOtherRegistry,
276276
integrity: 'sha512-registry',
277277
parent: root,
@@ -312,6 +312,33 @@ t.only('resolveOptions', async t => {
312312
t.strictSame(data.dependencies.tar.resolved, undefined)
313313
})
314314

315+
await t.test('recordDefaultRegistry: true', async t => {
316+
const { data } = await getData({
317+
recordDefaultRegistry: true,
318+
registry: 'https://private.registry.org/deadbeef',
319+
'@scope:registry': 'https://someother.registry.org',
320+
})
321+
322+
// unscoped packages that resolve to their configured registry should be
323+
// record the default registry
324+
t.strictSame(data.packages['node_modules/registry'].resolved,
325+
'https://registry.npmjs.org/registry/-/registry-1.2.3.tgz')
326+
t.strictSame(data.dependencies.registry.resolved,
327+
'https://registry.npmjs.org/registry/-/registry-1.2.3.tgz')
328+
329+
// scoped packages that resolve to their configured registry should be
330+
// record the default registry
331+
t.strictSame(data.packages['node_modules/@scope/some-package'].resolved,
332+
'https://registry.npmjs.org/registry/-/registry-1.2.3.tgz')
333+
t.strictSame(data.dependencies['@scope/some-package'].resolved,
334+
'https://registry.npmjs.org/registry/-/registry-1.2.3.tgz')
335+
336+
// packages with resolved urls that don't match the configured registry
337+
// should record undefined so npm resolves their url again.
338+
t.strictSame(data.packages['node_modules/some-other-registry'].resolved, undefined)
339+
t.strictSame(data.dependencies['some-other-registry'].resolved, undefined)
340+
})
341+
315342
t.test('metaFromNode default', async t => {
316343
// test to cover options default.
317344
const { registry } = await getData(undefined)

0 commit comments

Comments
 (0)