diff --git a/lib/node.js b/lib/node.js index 45c288bcf..f8b4964ff 100644 --- a/lib/node.js +++ b/lib/node.js @@ -522,6 +522,18 @@ class Node { return this === this.root || this === this.root.target } + get isRegistryDependency () { + if (this.edgesIn.size === 0) { + return false + } + for (const edge of this.edgesIn) { + if (!npa(edge.spec).registry) { + return false + } + } + return true + } + * ancestry () { for (let anc = this; anc; anc = anc.resolveParent) { yield anc diff --git a/lib/shrinkwrap.js b/lib/shrinkwrap.js index 2f0c0877c..16bc7bc9d 100644 --- a/lib/shrinkwrap.js +++ b/lib/shrinkwrap.js @@ -265,7 +265,7 @@ class Shrinkwrap { return s } - static metaFromNode (node, path) { + static metaFromNode (node, path, options = {}) { if (node.isLink) { return { resolved: relpath(path, node.realpath), @@ -299,7 +299,9 @@ class Shrinkwrap { }) const resolved = consistentResolve(node.resolved, node.path, path, true) - if (resolved) { + // hide resolved from registry dependencies. + if (resolved && + !(options.omitLockfileRegistryResolved && node.isRegistryDependency)) { meta.resolved = resolved } @@ -331,6 +333,7 @@ class Shrinkwrap { hiddenLockfile = false, log = procLog, lockfileVersion, + omitLockfileRegistryResolved = false, } = options this.lockfileVersion = hiddenLockfile ? 3 @@ -349,6 +352,7 @@ class Shrinkwrap { this.yarnLock = null this.hiddenLockfile = hiddenLockfile this.loadingError = null + this.resolvedOptions = { omitLockfileRegistryResolved } // only load npm-shrinkwrap.json in dep trees, not package-lock this.shrinkwrapOnly = shrinkwrapOnly } @@ -823,7 +827,7 @@ class Shrinkwrap { resolved, integrity, hasShrinkwrap, - } = Shrinkwrap.metaFromNode(node, this.path) + } = Shrinkwrap.metaFromNode(node, this.path, this.resolvedOptions) node.resolved = node.resolved || resolved || null node.integrity = node.integrity || integrity || null node.hasShrinkwrap = node.hasShrinkwrap || hasShrinkwrap || false @@ -879,7 +883,10 @@ class Shrinkwrap { [_updateWaitingNode] (loc) { const node = this[_awaitingUpdate].get(loc) this[_awaitingUpdate].delete(loc) - this.data.packages[loc] = Shrinkwrap.metaFromNode(node, this.path) + this.data.packages[loc] = Shrinkwrap.metaFromNode( + node, + this.path, + this.resolvedOptions) } commit () { @@ -887,7 +894,10 @@ class Shrinkwrap { if (this.yarnLock) { this.yarnLock.fromTree(this.tree) } - const root = Shrinkwrap.metaFromNode(this.tree.target, this.path) + const root = Shrinkwrap.metaFromNode( + this.tree.target, + this.path, + this.resolvedOptions) this.data.packages = {} if (Object.keys(root).length) { this.data.packages[''] = root @@ -898,7 +908,10 @@ class Shrinkwrap { continue } const loc = relpath(this.path, node.path) - this.data.packages[loc] = Shrinkwrap.metaFromNode(node, this.path) + this.data.packages[loc] = Shrinkwrap.metaFromNode( + node, + this.path, + this.resolvedOptions) } } else if (this[_awaitingUpdate].size > 0) { for (const loc of this[_awaitingUpdate].keys()) { @@ -999,6 +1012,8 @@ class Shrinkwrap { // git, file, dir, or remote, then the resolved value is necessary. if (node.resolved && !node.isLink && + !(node.isRegistryDependency && + this.resolvedOptions.omitLockfileRegistryResolved) && rSpec.type !== 'git' && rSpec.type !== 'file' && rSpec.type !== 'directory' && diff --git a/test/node.js b/test/node.js index ecdf72c22..29f23aafb 100644 --- a/test/node.js +++ b/test/node.js @@ -2842,3 +2842,26 @@ t.test('overrides', (t) => { t.end() }) + +t.test('node with no edges in is not a registry dep', async t => { + const node = new Node({ path: '/foo' }) + t.equal(node.isRegistryDependency, false) +}) + +t.test('node with non registry edge in is not a registry dep', async t => { + const root = new Node({ path: '/some/path', pkg: { dependencies: { registry: '', tar: '' } } }) + const node = new Node({ pkg: { name: 'node', version: '1.0.0' }, parent: root }) + + new Node({ pkg: { name: 'registry', dependencies: { node: '^1.0.0' } }, parent: root }) + new Node({ pkg: { name: 'tar', dependencies: { node: 'file:node' } }, parent: root }) + + t.equal(node.isRegistryDependency, false) +}) + +t.test('node with only registry edges in a registry dep', async t => { + const root = new Node({ path: '/some/path', pkg: { dependencies: { registry: '', tar: '' } } }) + const node = new Node({ pkg: { name: 'node', version: '1.0.0' }, parent: root }) + new Node({ pkg: { name: 'registry', dependencies: { node: '^1.0.0' } }, parent: root }) + + t.equal(node.isRegistryDependency, true) +}) diff --git a/test/shrinkwrap.js b/test/shrinkwrap.js index 2bf43fcf9..ed8b3b9ca 100644 --- a/test/shrinkwrap.js +++ b/test/shrinkwrap.js @@ -227,6 +227,76 @@ t.test('throws when attempting to access data before loading', t => { t.end() }) +t.test('omitLockfileRegistryResolved', async t => { + const url = 'https://registry.npmjs.org/registry/-/registry-1.2.3.tgz' + const getData = async (omitLockfileRegistryResolved) => { + const dir = t.testdir() + const meta = await Shrinkwrap.load({ + path: dir, + omitLockfileRegistryResolved}) + + const root = new Node({ + pkg: { + name: 'root', + dependencies: { + registry: '^1.0.0', + tar: url, + }, + }, + path: dir, + realpath: dir, + meta, + }) + + const registry = new Node({ + pkg: {name: 'registry', version: '1.2.3' }, + resolved: url, + integrity: 'sha512-registry', + parent: root, + }) + + const tar = new Node({ + pkg: {name: 'tar', version: '1.2.3'}, + resolved: url, + integrity: 'sha512-registry', + parent: root, + }) + + calcDepFlags(root) + meta.add(root) + return { data: meta.commit(), registry, tar, root } + } + + await t.test('true', async t => { + const { data } = await getData(true) + // registry dependencies in v2 packages and v1 dependencies should + // have resolved stripped. + t.strictSame(data.packages['node_modules/registry'].resolved, undefined) + t.strictSame(data.dependencies.registry.resolved, undefined) + + // tar should have resolved because it is not a registry dep. + t.strictSame(data.packages['node_modules/tar'].resolved, url) + // v1 url dependencies never have resolved. + t.strictSame(data.dependencies.tar.resolved, undefined) + }) + + await t.test('false', async t => { + const { data } = await getData(false) + t.strictSame(data.packages['node_modules/registry'].resolved, url) + t.strictSame(data.dependencies.registry.resolved, url) + + t.strictSame(data.packages['node_modules/tar'].resolved, url) + // v1 url dependencies never have resolved. + t.strictSame(data.dependencies.tar.resolved, undefined) + }) + + t.test('metaFromNode default', async t => { + // test to cover options default. + const { registry } = await getData(false) + t.strictSame(Shrinkwrap.metaFromNode(registry, '').resolved, url) + }) +}) + t.test('construct metadata from node and package data', t => { const meta = new Shrinkwrap({ path: '/home/user/projects/root' }) // fake load