Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/npm/cli.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'node_modules/@npmcli/arborist/lib/node.js')
-rw-r--r--node_modules/@npmcli/arborist/lib/node.js176
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