@npmcli/arborist
Advanced tools
Comparing version 0.0.0-pre.4 to 0.0.0-pre.5
@@ -67,2 +67,3 @@ // mixin implementing the buildIdealTree method | ||
const _linkNodes = Symbol('linkNodes') | ||
const _follow = Symbol('follow') | ||
@@ -85,2 +86,4 @@ // used by Reify mixin | ||
this[_follow] = !!options.follow | ||
this[_explicitRequests] = new Set() | ||
@@ -185,2 +188,3 @@ this[_preferDedupe] = false | ||
devOptional: false, | ||
peer: false, | ||
optional: false, | ||
@@ -770,12 +774,13 @@ }) | ||
const loc = relpath(this.path, realpath) | ||
if (/^\.\.[\\\/]/.test(loc)) { | ||
const fromInv = this.idealTree.inventory.get(loc) | ||
if (fromInv && fromInv !== link.target) | ||
link.target = fromInv | ||
const external = /^\.\.\//.test(loc) | ||
if (external && !this[_follow]) { | ||
// outside the root, somebody else's problem, ignore it | ||
// TODO: deep updates somehow, with a flag | ||
continue | ||
} | ||
const fromInv = this.idealTree.inventory.get(loc) | ||
if (fromInv && fromInv !== link.target) | ||
link.target = fromInv | ||
if (!link.target.parent && !link.target.fsParent) { | ||
@@ -789,3 +794,6 @@ // the fsParent MUST be some node in the tree, possibly the root. | ||
const path = parts.slice(0, p).join('/') | ||
const node = !path ? this.idealTree : this.idealTree.inventory.get(path) | ||
if (!path && external) | ||
break | ||
const node = !path ? this.idealTree | ||
: this.idealTree.inventory.get(path) | ||
if (node) { | ||
@@ -798,2 +806,7 @@ link.target.fsParent = node | ||
} | ||
// didn't find a parent for it, but we're filling in external | ||
// link targets, so go ahead and process it. | ||
if (this[_follow] && !link.target.parent && !link.target.fsParent) | ||
this[_depsQueue].push(link.target) | ||
} | ||
@@ -832,2 +845,3 @@ | ||
this.idealTree.devOptional = false | ||
this.idealTree.peer = false | ||
} | ||
@@ -863,2 +877,3 @@ | ||
node.devOptional = true | ||
node.peer = true | ||
node.optional = true | ||
@@ -865,0 +880,0 @@ } |
@@ -140,2 +140,3 @@ // mixin providing the loadVirtual method | ||
node.devOptional = !!(sw.devOptional || sw.dev || sw.optional) | ||
node.peer = !!sw.peer | ||
node.optional = !!sw.optional | ||
@@ -157,2 +158,3 @@ node.dev = !!sw.dev | ||
link.devOptional = target.devOptional | ||
link.peer = target.peer | ||
link.optional = target.optional | ||
@@ -166,2 +168,3 @@ link.dev = target.dev | ||
link.dev = link.target.dev = !!meta.dev | ||
link.peer = link.target.peer = !!meta.peer | ||
link.devOptional = link.target.devOptional = | ||
@@ -168,0 +171,0 @@ !!meta.devOptional || |
@@ -74,3 +74,13 @@ // mixin implementing the reify method | ||
const _copyIdealToActual = Symbol('copyIdealToActual') | ||
const _addOmitsToTrashList = Symbol('addOmitsToTrashList') | ||
const _omitDev = Symbol('omitDev') | ||
const _omitOptional = Symbol('omitOptional') | ||
const _omitPeer = Symbol('omitPeer') | ||
const _global = Symbol('global') | ||
const _ignoreScripts = Symbol('ignoreScripts') | ||
const _scriptShell = Symbol('scriptShell') | ||
const _force = Symbol('force') | ||
// defined by Ideal mixin | ||
@@ -84,3 +94,16 @@ const _idealTreePrune = Symbol.for('idealTreePrune') | ||
this[_savePrefix] = options.savePrefix || '^' | ||
const { | ||
ignoreScripts = false, | ||
global = false, | ||
force = false, | ||
scriptShell, | ||
savePrefix = '^', | ||
} = options | ||
this[_ignoreScripts] = !!ignoreScripts | ||
this[_global] = !!global | ||
this[_force] = !!force | ||
this[_scriptShell] = scriptShell | ||
this[_savePrefix] = savePrefix | ||
this[_diff] = null | ||
@@ -94,3 +117,8 @@ this[_retiredPaths] = {} | ||
// public method | ||
reify (options) { | ||
reify (options = {}) { | ||
const omit = new Set(options.omit || []) | ||
this[_omitDev] = omit.has('dev') | ||
this[_omitOptional] = omit.has('optional') | ||
this[_omitPeer] = omit.has('peer') | ||
// start tracker block | ||
@@ -102,2 +130,3 @@ this.addTracker('reify') | ||
.then(() => this[_createSparseTree]()) | ||
.then(() => this[_addOmitsToTrashList]()) | ||
.then(() => this[_loadShrinkwrapsAndUpdateTrees]()) | ||
@@ -194,2 +223,19 @@ .then(() => this[_loadBundlesAndUpdateTrees]()) | ||
// adding to the trash list will skip reifying, and delete them | ||
// if they are currently in the tree and otherwise untouched. | ||
[_addOmitsToTrashList] () { | ||
if (!this[_omitDev] && !this[_omitOptional] && !this[_omitPeer]) | ||
return | ||
const filter = node => | ||
node.peer && this[_omitPeer] || | ||
node.dev && this[_omitDev] || | ||
node.optional && this[_omitOptional] || | ||
node.devOptional && this[_omitOptional] && this[_omitDev] | ||
for (const node of this.idealTree.inventory.filter(filter)) { | ||
this[_trashList].add(node.path) | ||
} | ||
} | ||
[_createSparseTree] () { | ||
@@ -230,3 +276,4 @@ // if we call this fn again, we look for the previous list | ||
.filter(d => (d.action === 'CHANGE' || d.action === 'ADD') && | ||
d.ideal.hasShrinkwrap && !seen.has(d.ideal)) | ||
d.ideal.hasShrinkwrap && !seen.has(d.ideal) && | ||
!this[_trashList].has(d.ideal.path)) | ||
@@ -260,8 +307,7 @@ if (!shrinkwraps.length) | ||
[_reifyNode] (node) { | ||
this.addTracker('reify', node.name, node.location) | ||
/* istanbul ignore if - pretty sure this is impossible, just cautious */ | ||
if (this[_trashList].has(node.path)) | ||
return node | ||
this.addTracker('reify', node.name, node.location) | ||
const p = this[_checkEngineAndPlatform](node) | ||
@@ -316,3 +362,3 @@ .then(() => this[_extractOrLink](node)) | ||
[_checkPlatform] (node) { | ||
checkPlatform(node.package, this.options.force) | ||
checkPlatform(node.package, this[_force]) | ||
} | ||
@@ -351,4 +397,4 @@ | ||
top: node.isTop, | ||
force: this.options.force, | ||
global: this.options.global, | ||
force: this[_force], | ||
global: this[_global], | ||
}) | ||
@@ -390,3 +436,4 @@ } | ||
const set = (bundlesByDepth.get(depth) || []) | ||
.filter(node => node.root === this.idealTree) | ||
.filter(node => node.root === this.idealTree && | ||
!this[_trashList].has(node.path)) | ||
@@ -429,4 +476,4 @@ if (!set.length) { | ||
top: node.isTop, | ||
force: this.options.force, | ||
global: this.options.global, | ||
force: this[_force], | ||
global: this[_global], | ||
})) | ||
@@ -560,3 +607,3 @@ } | ||
[_runLifecycleScripts] () { | ||
if (this.options.ignoreScripts) | ||
if (this[_ignoreScripts]) | ||
return | ||
@@ -639,6 +686,7 @@ | ||
npm_package_dev: boolEnv(node.dev), | ||
npm_package_peer: boolEnv(node.peer), | ||
npm_package_dev_optional: | ||
boolEnv(node.devOptional && !node.dev && !node.optional), | ||
}, | ||
scriptShell: this.options.scriptShell, | ||
scriptShell: this[_scriptShell], | ||
})) | ||
@@ -669,3 +717,3 @@ })) | ||
// or shrinkwrap file, and any additions or removals to package.json | ||
[_saveIdealTree] (options = {}) { | ||
[_saveIdealTree] (options) { | ||
// the ideal tree is actualized now, hooray! | ||
@@ -672,0 +720,0 @@ // it still contains all the references to optional nodes that were removed |
@@ -7,2 +7,3 @@ const { depth } = require('treeverse') | ||
tree.devOptional = false | ||
tree.peer = false | ||
return depth({ | ||
@@ -28,2 +29,3 @@ tree, | ||
node.target.devOptional = node.devOptional | ||
node.target.peer = node.peer | ||
node.target.extraneous = false | ||
@@ -33,3 +35,3 @@ node = node.target | ||
node.edgesOut.forEach(({type, to}) => { | ||
node.edgesOut.forEach(({peer, optional, dev, to}) => { | ||
// if the dep is missing, then its flags are already maximally unset | ||
@@ -47,9 +49,14 @@ if (!to) | ||
const unsetDevOpt = !node.devOptional && !node.dev && !node.optional && | ||
type !== 'dev' && type !== 'optional' | ||
!dev && !optional | ||
// if we are not in the devOpt tree, then we're also not in | ||
// either the dev or opt trees | ||
const unsetDev = unsetDevOpt || !node.dev && type !== 'dev' | ||
const unsetOpt = unsetDevOpt || !node.optional && type !== 'optional' | ||
const unsetDev = unsetDevOpt || !node.dev && !dev | ||
const unsetOpt = unsetDevOpt || | ||
!node.optional && !optional | ||
const unsetPeer = !node.peer && !peer | ||
if (unsetPeer) | ||
unsetFlag(to, 'peer') | ||
if (unsetDevOpt) | ||
@@ -82,3 +89,3 @@ unsetFlag(to, 'devOptional') | ||
.filter(edge => edge.to && edge.to[flag] && | ||
(edge.type === 'peer' || edge.type === 'prod')) | ||
(flag !== 'peer' && edge.type === 'peer' || edge.type === 'prod')) | ||
.map(edge => edge.to), | ||
@@ -85,0 +92,0 @@ }) |
@@ -107,2 +107,4 @@ // Do not rely on package._fields, so that we don't throw | ||
module.exports = depValid | ||
module.exports = (child, requested, accept, requestor) => | ||
depValid(child, requested, requestor) || | ||
(typeof accept === 'string' ? depValid(child, accept, requestor) : false) |
@@ -9,2 +9,3 @@ // An edge in the dependency graph | ||
const _spec = Symbol('_spec') | ||
const _accept = Symbol('_accept') | ||
const _name = Symbol('_name') | ||
@@ -25,3 +26,3 @@ const _error = Symbol('_error') | ||
constructor (options) { | ||
const { type, name, spec, from } = options | ||
const { type, name, spec, accept, from } = options | ||
@@ -32,2 +33,8 @@ if (typeof spec !== 'string') | ||
if (accept !== undefined) { | ||
if (typeof accept !== 'string') | ||
throw new TypeError('accept field must be a string if provided') | ||
this[_accept] = accept || '*' | ||
} | ||
if (typeof name !== 'string') | ||
@@ -50,5 +57,9 @@ throw new TypeError('must provide dependency name') | ||
satisfiedBy (node) { | ||
return depValid(node, this.spec, this.from) | ||
return depValid(node, this.spec, this.accept, this.from) | ||
} | ||
get dev () { | ||
return this[_type] === 'dev' | ||
} | ||
get optional () { | ||
@@ -74,2 +85,6 @@ return this[_type] === 'optional' || this[_type] === 'peerOptional' | ||
get accept () { | ||
return this[_accept] | ||
} | ||
get valid () { | ||
@@ -90,3 +105,3 @@ return !this.error | ||
? 'PEER LOCAL' | ||
: !depValid(this.to, this.spec, this.from) | ||
: !depValid(this.to, this.spec, this.accept, this.from) | ||
? 'INVALID' | ||
@@ -93,0 +108,0 @@ : 'OK' |
@@ -86,2 +86,3 @@ // inventory, path, realpath, root, and parent | ||
devOptional = true, | ||
peer = true, | ||
} = options | ||
@@ -136,2 +137,3 @@ | ||
this.devOptional = devOptional | ||
this.peer = peer | ||
this.extraneous = extraneous | ||
@@ -359,7 +361,9 @@ | ||
const from = this | ||
const ad = this.package.acceptDependencies || {} | ||
for (const [name, spec] of Object.entries(obj || {})) { | ||
const accept = ad[name] | ||
// if it's already set, then we keep the existing edge | ||
// NB: the Edge ctor adds itself to from.edgesOut | ||
if (!this.edgesOut.get(name)) { | ||
new Edge({ from, name, spec, type }) | ||
new Edge({ from, name, spec, accept, type }) | ||
} | ||
@@ -366,0 +370,0 @@ } |
@@ -83,2 +83,3 @@ // a module that manages a shrinkwrap file (npm-shrinkwrap.json or | ||
'bundleDependencies', | ||
'acceptDependencies', | ||
'funding', | ||
@@ -156,2 +157,4 @@ 'engines', | ||
else { | ||
if (node.peer) | ||
meta.peer = true | ||
if (node.dev) | ||
@@ -610,2 +613,5 @@ meta.dev = true | ||
else if (!node.isLink) { | ||
if (node.peer) | ||
lock.peer = true | ||
if (node.devOptional && !node.dev && !node.optional) | ||
@@ -612,0 +618,0 @@ lock.devOptional = true |
{ | ||
"name": "@npmcli/arborist", | ||
"version": "0.0.0-pre.4", | ||
"version": "0.0.0-pre.5", | ||
"description": "Manage node_modules trees", | ||
@@ -5,0 +5,0 @@ "dependencies": { |
103
README.md
@@ -163,3 +163,4 @@ # @npmcli/arborist | ||
* `node.children` Map of packages located in the node's `node_modules` folder. | ||
* `node.children` Map of packages located in the node's `node_modules` | ||
folder. | ||
* `node.package` The contents of this node's `package.json` file. | ||
@@ -170,21 +171,22 @@ * `node.path` File path to this package. If the node is a link, then this | ||
* `node.realpath` The full real filepath on disk where this node lives. | ||
* `node.location` A slash-normalized relative path from the root node | ||
to this node's path. | ||
* `node.location` A slash-normalized relative path from the root node to | ||
this node's path. | ||
* `node.isLink` Whether this represents a symlink. Always `false` for Node | ||
objects, always `true` for Link objects. | ||
* `node.isRoot` True if this node is a root node. (Ie, if `node.root === | ||
node`.) | ||
node`.) | ||
* `node.root` The root node where we are working. If not assigned to some | ||
other value, resolves to the node itself. (Ie, the root node's `root` | ||
property refers to itself.) | ||
other value, resolves to the node itself. (Ie, the root node's `root` | ||
property refers to itself.) | ||
* `node.isTop` True if this node is the top of its tree (ie, has no | ||
`parent`, false otherwise). | ||
`parent`, false otherwise). | ||
* `node.top` The top node in this node's tree. This will be equal to | ||
`node.root` for simple trees, but link targets will frequently be | ||
outside of (or nested somewhere within) a `node_modules` hierarchy, and | ||
so will have a different `top`. | ||
* `node.dev`, `node.optional`, `node.devOptional` Indicators as to whether | ||
this node is a dev dependency and/or optional dependency. These flags | ||
are relevant when pruning optional and/or dev dependencies out of the | ||
tree. See **Package Dependency Flags** below for explanations. | ||
`node.root` for simple trees, but link targets will frequently be outside | ||
of (or nested somewhere within) a `node_modules` hierarchy, and so will | ||
have a different `top`. | ||
* `node.dev`, `node.optional`, `node.devOptional`, `node.peer`, Indicators | ||
as to whether this node is a dev, optional, and/or peer dependency. | ||
These flags are relevant when pruning dependencies out of the tree or | ||
deciding what to reify. See **Package Dependency Flags** below for | ||
explanations. | ||
* `node.edgesOut` Edges in the dependency graph indicating nodes that this | ||
@@ -263,27 +265,47 @@ node depends on, which resolve its dependencies. | ||
``` | ||
| extraneous | dev | optional | devOptional | meaning | prune? | | ||
|------------+-----+----------+-------------+---------------------+-------------------| | ||
| | | | | production dep | never | | ||
|------------+-----+----------+-------------+---------------------+-------------------| | ||
| X | N/A | N/A | N/A | nothing depends on | always | | ||
| | | | | this, it is trash | | | ||
|------------+-----+----------+-------------+---------------------+-------------------| | ||
| | X | | X | devDependency, or | if pruning dev | | ||
| | | | not in lock | only depended upon | | | ||
| | | | | by devDependencies | | | ||
|------------+-----+----------+-------------+---------------------+-------------------| | ||
| | | X | X | optionalDependency, | if pruning | | ||
| | | | not in lock | or only depended on | optional | | ||
| | | | | by optionalDeps | | | ||
|------------+-----+----------+-------------+---------------------+-------------------| | ||
| | X | X | X | Optional dependency | if pruning EITHER | | ||
| | | | not in lock | of dep(s) in the | dev OR optional | | ||
| | | | | dev hierarchy | | | ||
|------------+-----+----------+-------------+---------------------+-------------------| | ||
| | | | X | BOTH a non-optional | if pruning BOTH | | ||
| | | | in lock | dep within the dev | dev AND optional | | ||
| | | | | hierarchy, AND a | | | ||
| | | | | dep within the | | | ||
| | | | | optional hierarchy | | | ||
+------------+-----+----------+-------------+---------------------+-------------------+ | ||
| extraneous | peer | dev | optional | devOptional | meaning | prune? | | ||
|------------+------+-----+----------+-------------+---------------------+-------------------| | ||
| | | | | | production dep | never | | ||
|------------+------+-----+----------+-------------+---------------------+-------------------| | ||
| X | N/A | N/A | N/A | N/A | nothing depends on | always | | ||
| | | | | | this, it is trash | | | ||
|------------+------+-----+----------+-------------+---------------------+-------------------| | ||
| | | X | | X | devDependency, or | if pruning dev | | ||
| | | | | not in lock | only depended upon | | | ||
| | | | | | by devDependencies | | | ||
|------------+------+-----+----------+-------------+---------------------+-------------------| | ||
| | | | X | X | optionalDependency, | if pruning | | ||
| | | | | not in lock | or only depended on | optional | | ||
| | | | | | by optionalDeps | | | ||
|------------+------+-----+----------+-------------+---------------------+-------------------| | ||
| | | X | X | X | Optional dependency | if pruning EITHER | | ||
| | | | | not in lock | of dep(s) in the | dev OR optional | | ||
| | | | | | dev hierarchy | | | ||
|------------+------+-----+----------+-------------+---------------------+-------------------| | ||
| | | | | X | BOTH a non-optional | if pruning BOTH | | ||
| | | | | in lock | dep within the dev | dev AND optional | | ||
| | | | | | hierarchy, AND a | | | ||
| | | | | | dep within the | | | ||
| | | | | | optional hierarchy | | | ||
|------------+------+-----+----------+-------------+---------------------+-------------------| | ||
| | X | | | | peer dependency, or | if pruning peers | | ||
| | | | | | only depended on by | | | ||
| | | | | | peer dependencies | | | ||
|------------+------+-----+----------+-------------+---------------------+-------------------| | ||
| | X | X | | X | peer dependency of | if pruning peer | | ||
| | | | | not in lock | dev node heirarchy | OR dev deps | | ||
|------------+------+-----+----------+-------------+---------------------+-------------------| | ||
| | X | | X | X | peer dependency of | if pruning peer | | ||
| | | | | not in lock | optional nodes, or | OR optional deps | | ||
| | | | | | peerOptional dep | | | ||
|------------+------+-----+----------+-------------+---------------------+-------------------| | ||
| | X | X | X | X | peer optional deps | if pruning peer | | ||
| | | | | not in lock | of the dev dep | OR optional OR | | ||
| | | | | | heirarchy | dev | | ||
|------------+------+-----+----------+-------------+---------------------+-------------------| | ||
| | X | | | X | BOTH a non-optional | if pruning peers | | ||
| | | | | in lock | peer dep within the | OR: | | ||
| | | | | | dev heirarchy, AND | BOTH optional | | ||
| | | | | | a peer optional dep | AND dev deps | | ||
+------------+------+-----+----------+-------------+---------------------+-------------------+ | ||
``` | ||
@@ -307,4 +329,7 @@ | ||
_both_ dev and optional dependencies are being removed. | ||
* If `node.peer` is set, then all the same semantics apply as above, except | ||
that the dep is brought in by a peer dep at some point, rather than a | ||
normal non-peer dependency. | ||
Note: `devOptional` is only set in the shrinkwrap/package-lock file if | ||
_neither_ `dev` nor `optional` are set, as it would be redundant. |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
229419
4428
332