diff options
Diffstat (limited to 'node_modules/@npmcli/arborist/lib/node.js')
-rw-r--r-- | node_modules/@npmcli/arborist/lib/node.js | 176 |
1 files changed, 86 insertions, 90 deletions
diff --git a/node_modules/@npmcli/arborist/lib/node.js b/node_modules/@npmcli/arborist/lib/node.js index 5a7661686..6ed6bfe21 100644 --- a/node_modules/@npmcli/arborist/lib/node.js +++ b/node_modules/@npmcli/arborist/lib/node.js @@ -61,9 +61,6 @@ const _workspaces = Symbol('_workspaces') const relpath = require('./relpath.js') const consistentResolve = require('./consistent-resolve.js') -// for comparing nodes to yarn.lock entries -const mismatch = (a, b) => a && b && a !== b - class Node { constructor (options) { // NB: path can be null if it's a link target @@ -90,6 +87,7 @@ class Node { devOptional = true, peer = true, global = false, + dummy = false, } = options // true if part of a global install @@ -122,8 +120,13 @@ class Node { // since _location is just where the module ended up in the tree, // and _where can be different than the actual root if it's a // meta-dep deeper in the dependency graph. + // + // If we don't have the other oldest indicators of legacy npm, then it's + // probably what we're getting from pacote, which IS trustworthy. + // + // Otherwise, hopefully a shrinkwrap will help us out. const resolved = consistentResolve(pkg._resolved) - if (resolved && !/^file:/.test(resolved)) + if (resolved && !(/^file:/.test(resolved) && pkg._where)) this.resolved = resolved } this.integrity = integrity || pkg._integrity || null @@ -142,11 +145,24 @@ class Node { // a 3-levels-deep dependency of a non-dev dep. If we calc the // flags along the way, then they'll tend to be invalid by the // time we need to look at them. - this.dev = dev - this.optional = optional - this.devOptional = devOptional - this.peer = peer - this.extraneous = extraneous + if (!dummy) { + this.dev = dev + this.optional = optional + this.devOptional = devOptional + this.peer = peer + this.extraneous = extraneous + this.dummy = false + } else { + // true if this is a placeholder for the purpose of serving as a + // fsParent to link targets that get their deps resolved outside + // the root tree folder. + this.dummy = true + this.dev = false + this.optional = false + this.devOptional = false + this.peer = false + this.extraneous = false + } this.edgesIn = new Set() this.edgesOut = new Map() @@ -340,13 +356,13 @@ class Node { this[_refreshLocation]() if (this.top.meta) - this.top.meta.add(this) + this[_refreshTopMeta]() if (this.target && !nullRoot) this.target.root = root - this.children.forEach(c => c.root = root) this.fsChildren.forEach(c => c.root = root) + this.children.forEach(c => c.root = root) /* istanbul ignore next */ dassert(this === root || this.inventory.size === 0) } @@ -448,17 +464,18 @@ class Node { // Almost certainly due to being a linked workspace-style package. this[_fsParent] = fsParent fsParent.fsChildren.add(this) + // refresh the path BEFORE setting root, so meta gets updated properly + this[_refreshPath](fsParent, current && current.path) this.root = fsParent.root this[_reloadEdges](e => !e.to) - this[_refreshPath](fsParent, current && current.path) } // called when we find that we have an fsParent which could account // for some missing edges which are actually fine and not missing at all. [_reloadEdges] (filter) { this.edgesOut.forEach(edge => filter(edge) && edge.reload()) - this.children.forEach(c => c[_reloadEdges](filter)) this.fsChildren.forEach(c => c[_reloadEdges](filter)) + this.children.forEach(c => c[_reloadEdges](filter)) } // is it safe to replace one node with another? check the edges to @@ -485,6 +502,40 @@ class Node { return node.canReplaceWith(this) } + matches (node) { + // if the nodes are literally the same object, obviously a match. + if (node === this) + return true + + // if the names don't match, they're different things, even if + // the package contents are identical. + if (node.name !== this.name) + return false + + // if they're links, they match if the targets match + if (this.isLink) + return node.isLink && this.target.matches(node.target) + + // if they're two root nodes, they're different if the paths differ + if (this.isRoot && node.isRoot) + return this.path === node.path + + // if the integrity matches, then they're the same. + if (this.integrity && node.integrity) + return this.integrity === node.integrity + + // if no integrity, check resolved + if (this.resolved && node.resolved) + return this.resolved === node.resolved + + // if no resolved, check both package name and version + // otherwise, conclude that they are different things + return this.package.name && node.package.name && + this.package.name === node.package.name && + this.package.version && node.package.version && + this.package.version === node.package.version + } + // replace this node with the supplied argument // Useful when mutating an ideal tree, so we can avoid having to call // the parent/root setters more than necessary. @@ -497,8 +548,8 @@ class Node { // pretend to be in the tree, so top/etc refs are not changing for kids. node.parent = null node[_parent] = this[_parent] + this.fsChildren.forEach(c => c.fsParent = node) this.children.forEach(c => c.parent = node) - this.fsChildren.forEach(c=> c.fsParent = node) // now remove the hidden reference, and call parent setter to finalize. node[_parent] = null node.parent = this.parent @@ -565,8 +616,8 @@ class Node { // we are about to change the parent, and thus the top, so we have // to delist from the metadata now to ensure we remove it from the // proper top node metadata if it isn't the root. - this.children.forEach(c => c[_delistFromMeta]()) this.fsChildren.forEach(c => c[_delistFromMeta]()) + this.children.forEach(c => c[_delistFromMeta]()) } // remove from former parent. @@ -597,7 +648,7 @@ class Node { // if the root isn't changing, then this is a no-op. // the root setter is a no-op if the root didn't change, so we have // to manually call the method to update location and metadata - if (this.root === newRoot) + if (!rootChange) this[_refreshLocation]() else this.root = newRoot @@ -625,6 +676,13 @@ class Node { // since loading a parent can add *or change* resolutions, we also // walk the tree from this point reloading all edges. this[_reloadEdges](e => true) + + // have to refresh the location of children and fsChildren at this point, + // because their paths have likely changed, and root may have been set. + if (!rootChange) { + this.children.forEach(c => c[_refreshLocation]()) + this.fsChildren.forEach(c => c[_refreshLocation]()) + } } // called after changing the parent (and thus the top), and after changing @@ -674,9 +732,7 @@ class Node { if (newPath === oldPath) return - if (this.path && this.resolved && /^file:/.test(this.resolved)) - this.resolved = consistentResolve(this.resolved, this.path, newPath) - + this[_delistFromMeta]() this.path = newPath if (!this.isLink) { this.realpath = this.path @@ -687,8 +743,9 @@ class Node { } } - this.children.forEach(c => c[_refreshPath](this, oldPath)) + this[_refreshLocation]() this.fsChildren.forEach(c => c[_refreshPath](this, oldPath)) + this.children.forEach(c => c[_refreshPath](this, oldPath)) } @@ -700,48 +757,8 @@ class Node { this.location = relpath(root.realpath, this.path) root.inventory.add(this) - - // try to get metadata, and write to the root's store if we can - if (root.meta) { - if (this.resolved === null || this.integrity === null) { - const { - resolved, - integrity, - hasShrinkwrap, - } = root.meta.get(this.path) - const pathFixed = !resolved ? null - : !/^file:/.test(resolved) ? resolved - // resolve onto the metadata path, then realpath to there from here - : `file:${relpath(this.path, - resolve(root.realpath, resolved.substr(5)))}` - - // if we have one, only set the other if it matches - // otherwise it could be for a completely different thing. - const resolvedOk = !resolved || !this.resolved || - this.resolved === pathFixed - const integrityOk = !integrity || !this.integrity || - this.integrity === integrity - - if ((resolved || integrity) && resolvedOk && integrityOk) { - this.resolved = this.resolved || pathFixed || null - this.integrity = this.integrity || integrity || null - this.hasShrinkwrap = this.hasShrinkwrap || hasShrinkwrap || false - } else { - // try to read off the package or node itself. - const { - resolved, - integrity, - hasShrinkwrap, - } = Shrinkwrap.metaFromNode(this, this.path) - this.resolved = this.resolved || resolved || null - this.integrity = this.integrity || integrity || null - this.hasShrinkwrap = this.hasShrinkwrap || hasShrinkwrap || false - } - } - - // add to the root meta so we don't do this dance more than once. + if (root.meta) root.meta.add(this) - } } addEdgeOut (edge) { @@ -750,41 +767,20 @@ class Node { addEdgeIn (edge) { this.edgesIn.add(edge) - if (!edge.valid) - return - - // try to get metadata from the yarn.lock file if we need it - const needsMeta = !this.resolved || !this.integrity - if (!needsMeta) - return - - const yarnLock = this.root.meta && this.root.meta.yarnLock - if (!yarnLock || !yarnLock.entries || !yarnLock.entries.size) - return - - const pathFixed = !this.resolved ? null - : !/file:/.test(this.resolved) ? this.resolved - : consistentResolve(this.resolved, this.path, this.root.meta.path) - const entry = yarnLock.entries.get(`${this.name}@${edge.spec}`) - // skip any entries that don't appear to be referring to this thing - if (!entry || - mismatch(this.package.version, entry.version) || - mismatch(this.integrity, entry.integrity) || - mismatch(pathFixed, entry.resolved)) - return - - // ok, this is probably it! get what we can from it. - this.integrity = this.integrity || entry.integrity || null - this.resolved = this.resolved || - consistentResolve(entry.resolved, this.root.meta.path, this.path) || - null + // try to get metadata from the yarn.lock file + if (this.root.meta) + this.root.meta.addEdge(edge) } [_reloadNamedEdges] (name, root) { // either it's the node in question, or it's going to block it anyway - if (this.name === name && !this.isTop) + if (this.name === name && !this.isTop) { + // reload the edges in so that anything that SHOULD be blocked + // by this node actually will be. + this.edgesIn.forEach(e => e.reload()) return + } const edge = this.edgesOut.get(name) // if we don't have an edge, do nothing, but keep descending |