summaryrefslogtreecommitdiff
path: root/deps/npm/node_modules/@npmcli/arborist/lib/arborist/load-actual.js
diff options
context:
space:
mode:
Diffstat (limited to 'deps/npm/node_modules/@npmcli/arborist/lib/arborist/load-actual.js')
-rw-r--r--deps/npm/node_modules/@npmcli/arborist/lib/arborist/load-actual.js213
1 files changed, 105 insertions, 108 deletions
diff --git a/deps/npm/node_modules/@npmcli/arborist/lib/arborist/load-actual.js b/deps/npm/node_modules/@npmcli/arborist/lib/arborist/load-actual.js
index 6c3f917c6a..8c4e148464 100644
--- a/deps/npm/node_modules/@npmcli/arborist/lib/arborist/load-actual.js
+++ b/deps/npm/node_modules/@npmcli/arborist/lib/arborist/load-actual.js
@@ -4,7 +4,7 @@ const { relative, dirname, resolve, join, normalize } = require('path')
const rpj = require('read-package-json-fast')
const { readdirScoped } = require('@npmcli/fs')
-const walkUp = require('walk-up-path')
+const { walkUp } = require('walk-up-path')
const ancestorPath = require('common-ancestor-path')
const treeCheck = require('../tree-check.js')
@@ -17,28 +17,31 @@ const realpath = require('../realpath.js')
// public symbols
const _changePath = Symbol.for('_changePath')
const _global = Symbol.for('global')
-const _loadWorkspaces = Symbol.for('loadWorkspaces')
+const _setWorkspaces = Symbol.for('setWorkspaces')
const _rpcache = Symbol.for('realpathCache')
const _stcache = Symbol.for('statCache')
-// private symbols
-const _actualTree = Symbol('actualTree')
-const _actualTreeLoaded = Symbol('actualTreeLoaded')
-const _actualTreePromise = Symbol('actualTreePromise')
-const _cache = Symbol('nodeLoadingCache')
-const _filter = Symbol('filter')
-const _findMissingEdges = Symbol('findMissingEdges')
-const _loadActual = Symbol('loadActual')
-const _loadFSChildren = Symbol('loadFSChildren')
-const _loadFSNode = Symbol('loadFSNode')
-const _loadFSTree = Symbol('loadFSTree')
-const _newLink = Symbol('newLink')
-const _newNode = Symbol('newNode')
-const _topNodes = Symbol('linkTargets')
-const _transplant = Symbol('transplant')
-const _transplantFilter = Symbol('transplantFilter')
-
module.exports = cls => class ActualLoader extends cls {
+ #actualTree
+ // ensure when walking the tree that we don't call loadTree on the same
+ // actual node more than one time.
+ #actualTreeLoaded = new Set()
+ #actualTreePromise
+
+ // cache of nodes when loading the actualTree, so that we avoid loaded the
+ // same node multiple times when symlinks attack.
+ #cache = new Map()
+ #filter
+
+ // cache of link targets for setting fsParent links
+ // We don't do fsParent as a magic getter/setter, because it'd be too costly
+ // to keep up to date along the walk.
+ // And, we know that it can ONLY be relevant when the node is a target of a
+ // link, otherwise it'd be in a node_modules folder, so take advantage of
+ // that to limit the scans later.
+ #topNodes = new Set()
+ #transplantFilter
+
constructor (options) {
super(options)
@@ -47,27 +50,11 @@ module.exports = cls => class ActualLoader extends cls {
// the tree of nodes on disk
this.actualTree = options.actualTree
- // ensure when walking the tree that we don't call loadTree on the
- // same actual node more than one time.
- this[_actualTreeLoaded] = new Set()
-
// caches for cached realpath calls
const cwd = process.cwd()
// assume that the cwd is real enough for our purposes
this[_rpcache] = new Map([[cwd, cwd]])
this[_stcache] = new Map()
-
- // cache of nodes when loading the actualTree, so that we avoid
- // loaded the same node multiple times when symlinks attack.
- this[_cache] = new Map()
-
- // cache of link targets for setting fsParent links
- // We don't do fsParent as a magic getter/setter, because
- // it'd be too costly to keep up to date along the walk.
- // And, we know that it can ONLY be relevant when the node
- // is a target of a link, otherwise it'd be in a node_modules
- // folder, so take advantage of that to limit the scans later.
- this[_topNodes] = new Set()
}
// public method
@@ -81,12 +68,12 @@ module.exports = cls => class ActualLoader extends cls {
if (this.actualTree) {
return this.actualTree
}
- if (!this[_actualTreePromise]) {
+ if (!this.#actualTreePromise) {
// allow the user to set options on the ctor as well.
// XXX: deprecate separate method options objects.
options = { ...this.options, ...options }
- this[_actualTreePromise] = this[_loadActual](options)
+ this.#actualTreePromise = this.#loadActual(options)
.then(tree => {
// reset all deps to extraneous prior to recalc
if (!options.root) {
@@ -102,14 +89,15 @@ module.exports = cls => class ActualLoader extends cls {
return this.actualTree
})
}
- return this[_actualTreePromise]
+ return this.#actualTreePromise
}
+
// return the promise so that we don't ever have more than one going at the
// same time. This is so that buildIdealTree can default to the actualTree
// if no shrinkwrap present, but reify() can still call buildIdealTree and
// loadActual in parallel safely.
- async [_loadActual] (options) {
+ async #loadActual (options) {
// mostly realpath to throw if the root doesn't exist
const {
global = false,
@@ -119,8 +107,8 @@ module.exports = cls => class ActualLoader extends cls {
ignoreMissing = false,
forceActual = false,
} = options
- this[_filter] = filter
- this[_transplantFilter] = transplantFilter
+ this.#filter = filter
+ this.#transplantFilter = transplantFilter
if (global) {
const real = await realpath(this.path, this[_rpcache], this[_stcache])
@@ -132,19 +120,19 @@ module.exports = cls => class ActualLoader extends cls {
loadOverrides: true,
}
if (this.path === real) {
- this[_actualTree] = this[_newNode](params)
+ this.#actualTree = this.#newNode(params)
} else {
- this[_actualTree] = await this[_newLink](params)
+ this.#actualTree = await this.#newLink(params)
}
} else {
// not in global mode, hidden lockfile is allowed, load root pkg too
- this[_actualTree] = await this[_loadFSNode]({
+ this.#actualTree = await this.#loadFSNode({
path: this.path,
real: await realpath(this.path, this[_rpcache], this[_stcache]),
loadOverrides: true,
})
- this[_actualTree].assertRootOverrides()
+ this.#actualTree.assertRootOverrides()
// if forceActual is set, don't even try the hidden lockfile
if (!forceActual) {
@@ -152,48 +140,48 @@ module.exports = cls => class ActualLoader extends cls {
// in the folder, or if any of the entries in the hidden lockfile are
// missing.
const meta = await Shrinkwrap.load({
- path: this[_actualTree].path,
+ path: this.#actualTree.path,
hiddenLockfile: true,
resolveOptions: this.options,
})
if (meta.loadedFromDisk) {
- this[_actualTree].meta = meta
+ this.#actualTree.meta = meta
// have to load on a new Arborist object, so we don't assign
// the virtualTree on this one! Also, the weird reference is because
// we can't easily get a ref to Arborist in this module, without
// creating a circular reference, since this class is a mixin used
// to build up the Arborist class itself.
await new this.constructor({ ...this.options }).loadVirtual({
- root: this[_actualTree],
+ root: this.#actualTree,
})
- await this[_loadWorkspaces](this[_actualTree])
+ await this[_setWorkspaces](this.#actualTree)
- this[_transplant](root)
- return this[_actualTree]
+ this.#transplant(root)
+ return this.#actualTree
}
}
const meta = await Shrinkwrap.load({
- path: this[_actualTree].path,
+ path: this.#actualTree.path,
lockfileVersion: this.options.lockfileVersion,
resolveOptions: this.options,
})
- this[_actualTree].meta = meta
+ this.#actualTree.meta = meta
}
- await this[_loadFSTree](this[_actualTree])
- await this[_loadWorkspaces](this[_actualTree])
+ await this.#loadFSTree(this.#actualTree)
+ await this[_setWorkspaces](this.#actualTree)
// if there are workspace targets without Link nodes created, load
// the targets, so that we know what they are.
- if (this[_actualTree].workspaces && this[_actualTree].workspaces.size) {
+ if (this.#actualTree.workspaces && this.#actualTree.workspaces.size) {
const promises = []
- for (const path of this[_actualTree].workspaces.values()) {
- if (!this[_cache].has(path)) {
+ for (const path of this.#actualTree.workspaces.values()) {
+ if (!this.#cache.has(path)) {
// workspace overrides use the root overrides
- const p = this[_loadFSNode]({ path, root: this[_actualTree], useRootOverrides: true })
- .then(node => this[_loadFSTree](node))
+ const p = this.#loadFSNode({ path, root: this.#actualTree, useRootOverrides: true })
+ .then(node => this.#loadFSTree(node))
promises.push(p)
}
}
@@ -201,32 +189,32 @@ module.exports = cls => class ActualLoader extends cls {
}
if (!ignoreMissing) {
- await this[_findMissingEdges]()
+ await this.#findMissingEdges()
}
// try to find a node that is the parent in a fs tree sense, but not a
// node_modules tree sense, of any link targets. this allows us to
// resolve deps that node will find, but a legacy npm view of the
// world would not have noticed.
- for (const path of this[_topNodes]) {
- const node = this[_cache].get(path)
+ for (const path of this.#topNodes) {
+ const node = this.#cache.get(path)
if (node && !node.parent && !node.fsParent) {
for (const p of walkUp(dirname(path))) {
- if (this[_cache].has(p)) {
- node.fsParent = this[_cache].get(p)
+ if (this.#cache.has(p)) {
+ node.fsParent = this.#cache.get(p)
break
}
}
}
}
- this[_transplant](root)
+ this.#transplant(root)
if (global) {
// need to depend on the children, or else all of them
// will end up being flagged as extraneous, since the
// global root isn't a "real" project
- const tree = this[_actualTree]
+ const tree = this.#actualTree
const actualRoot = tree.isLink ? tree.target : tree
const { dependencies = {} } = actualRoot.package
for (const [name, kid] of actualRoot.children.entries()) {
@@ -235,30 +223,30 @@ module.exports = cls => class ActualLoader extends cls {
}
actualRoot.package = { ...actualRoot.package, dependencies }
}
- return this[_actualTree]
+ return this.#actualTree
}
- [_transplant] (root) {
- if (!root || root === this[_actualTree]) {
+ #transplant (root) {
+ if (!root || root === this.#actualTree) {
return
}
- this[_actualTree][_changePath](root.path)
- for (const node of this[_actualTree].children.values()) {
- if (!this[_transplantFilter](node)) {
+ this.#actualTree[_changePath](root.path)
+ for (const node of this.#actualTree.children.values()) {
+ if (!this.#transplantFilter(node)) {
node.root = null
}
}
- root.replace(this[_actualTree])
- for (const node of this[_actualTree].fsChildren) {
- node.root = this[_transplantFilter](node) ? root : null
+ root.replace(this.#actualTree)
+ for (const node of this.#actualTree.fsChildren) {
+ node.root = this.#transplantFilter(node) ? root : null
}
- this[_actualTree] = root
+ this.#actualTree = root
}
- async [_loadFSNode] ({ path, parent, real, root, loadOverrides, useRootOverrides }) {
+ async #loadFSNode ({ path, parent, real, root, loadOverrides, useRootOverrides }) {
if (!real) {
try {
real = await realpath(path, this[_rpcache], this[_stcache])
@@ -275,7 +263,7 @@ module.exports = cls => class ActualLoader extends cls {
}
}
- const cached = this[_cache].get(path)
+ const cached = this.#cache.get(path)
let node
// missing edges get a dummy node, assign the parent and return it
if (cached && !cached.dummy) {
@@ -306,67 +294,67 @@ module.exports = cls => class ActualLoader extends cls {
// Node which will attach it to its errors array (Link passes it along to
// its target node)
if (normalize(path) === real) {
- node = this[_newNode](params)
+ node = this.#newNode(params)
} else {
- node = await this[_newLink](params)
+ node = await this.#newLink(params)
}
}
- this[_cache].set(path, node)
+ this.#cache.set(path, node)
return node
}
- [_newNode] (options) {
+ #newNode (options) {
// check it for an fsParent if it's a tree top. there's a decent chance
// it'll get parented later, making the fsParent scan a no-op, but better
// safe than sorry, since it's cheap.
const { parent, realpath } = options
if (!parent) {
- this[_topNodes].add(realpath)
+ this.#topNodes.add(realpath)
}
return new Node(options)
}
- async [_newLink] (options) {
+ async #newLink (options) {
const { realpath } = options
- this[_topNodes].add(realpath)
- const target = this[_cache].get(realpath)
+ this.#topNodes.add(realpath)
+ const target = this.#cache.get(realpath)
const link = new Link({ ...options, target })
if (!target) {
// Link set its target itself in this case
- this[_cache].set(realpath, link.target)
+ this.#cache.set(realpath, link.target)
// if a link target points at a node outside of the root tree's
// node_modules hierarchy, then load that node as well.
- await this[_loadFSTree](link.target)
+ await this.#loadFSTree(link.target)
}
return link
}
- async [_loadFSTree] (node) {
- const did = this[_actualTreeLoaded]
+ async #loadFSTree (node) {
+ const did = this.#actualTreeLoaded
if (!did.has(node.target.realpath)) {
did.add(node.target.realpath)
- await this[_loadFSChildren](node.target)
+ await this.#loadFSChildren(node.target)
return Promise.all(
[...node.target.children.entries()]
.filter(([name, kid]) => !did.has(kid.realpath))
- .map(([name, kid]) => this[_loadFSTree](kid))
+ .map(([name, kid]) => this.#loadFSTree(kid))
)
}
}
// create child nodes for all the entries in node_modules
// and attach them to the node as a parent
- async [_loadFSChildren] (node) {
+ async #loadFSChildren (node) {
const nm = resolve(node.realpath, 'node_modules')
try {
const kids = await readdirScoped(nm).then(paths => paths.map(p => p.replace(/\\/g, '/')))
return Promise.all(
// ignore . dirs and retired scoped package folders
kids.filter(kid => !/^(@[^/]+\/)?\./.test(kid))
- .filter(kid => this[_filter](node, kid))
- .map(kid => this[_loadFSNode]({
+ .filter(kid => this.#filter(node, kid))
+ .map(kid => this.#loadFSNode({
parent: node,
path: resolve(nm, kid),
})))
@@ -375,7 +363,7 @@ module.exports = cls => class ActualLoader extends cls {
}
}
- async [_findMissingEdges] () {
+ async #findMissingEdges () {
// try to resolve any missing edges by walking up the directory tree,
// checking for the package in each node_modules folder. stop at the
// root directory.
@@ -385,7 +373,7 @@ module.exports = cls => class ActualLoader extends cls {
// because people sometimes develop in ~/projects/node_modules/...
// so we'd end up loading a massive tree with lots of unrelated junk.
const nmContents = new Map()
- const tree = this[_actualTree]
+ const tree = this.#actualTree
for (const node of tree.inventory.values()) {
const ancestor = ancestorPath(node.realpath, this.path)
@@ -410,28 +398,37 @@ module.exports = cls => class ActualLoader extends cls {
break
}
- const entries = nmContents.get(p) || await readdirScoped(p + '/node_modules')
- .catch(() => []).then(paths => paths.map(p => p.replace(/\\/g, '/')))
- nmContents.set(p, entries)
+ let entries
+ if (!nmContents.has(p)) {
+ entries = await readdirScoped(p + '/node_modules')
+ .catch(() => []).then(paths => paths.map(p => p.replace(/\\/g, '/')))
+ nmContents.set(p, entries)
+ } else {
+ entries = nmContents.get(p)
+ }
+
if (!entries.includes(name)) {
continue
}
- const d = this[_cache].has(p) ? await this[_cache].get(p)
- : new Node({ path: p, root: node.root, dummy: true })
- // not a promise
- this[_cache].set(p, d)
+ let d
+ if (!this.#cache.has(p)) {
+ d = new Node({ path: p, root: node.root, dummy: true })
+ this.#cache.set(p, d)
+ } else {
+ d = this.#cache.get(p)
+ }
if (d.dummy) {
// it's a placeholder, so likely would not have loaded this dep,
// unless another dep in the tree also needs it.
const depPath = normalize(`${p}/node_modules/${name}`)
- const cached = this[_cache].get(depPath)
+ const cached = this.#cache.get(depPath)
if (!cached || cached.dummy) {
- depPromises.push(this[_loadFSNode]({
+ depPromises.push(this.#loadFSNode({
path: depPath,
root: node.root,
parent: d,
- }).then(node => this[_loadFSTree](node)))
+ }).then(node => this.#loadFSTree(node)))
}
}
break