@npmcli/arborist 2.2.9 → 2.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/lib/logging.js +10 -2
- package/bin/lib/options.js +7 -1
- package/bin/lib/timers.js +6 -2
- package/bin/virtual.js +2 -1
- package/lib/add-rm-pkg-deps.js +84 -82
- package/lib/arborist/build-ideal-tree.js +261 -73
- package/lib/arborist/index.js +4 -1
- package/lib/arborist/load-actual.js +28 -2
- package/lib/arborist/load-virtual.js +9 -6
- package/lib/arborist/rebuild.js +5 -4
- package/lib/arborist/reify.js +210 -49
- package/lib/audit-report.js +13 -6
- package/lib/debug.js +8 -1
- package/lib/diff.js +70 -11
- package/lib/index.js +1 -0
- package/lib/inventory.js +1 -1
- package/lib/link.js +13 -7
- package/lib/node.js +66 -24
- package/lib/printable.js +20 -2
- package/lib/shrinkwrap.js +5 -5
- package/lib/tree-check.js +46 -1
- package/lib/update-root-package-json.js +14 -2
- package/lib/vuln.js +3 -0
- package/package.json +5 -8
- package/lib/dep-spec.js +0 -43
package/lib/diff.js
CHANGED
|
@@ -11,7 +11,9 @@ const {existsSync} = require('fs')
|
|
|
11
11
|
const ssri = require('ssri')
|
|
12
12
|
|
|
13
13
|
class Diff {
|
|
14
|
-
constructor ({actual, ideal}) {
|
|
14
|
+
constructor ({actual, ideal, filterSet, shrinkwrapInflated}) {
|
|
15
|
+
this.filterSet = filterSet
|
|
16
|
+
this.shrinkwrapInflated = shrinkwrapInflated
|
|
15
17
|
this.children = []
|
|
16
18
|
this.actual = actual
|
|
17
19
|
this.ideal = ideal
|
|
@@ -29,9 +31,54 @@ class Diff {
|
|
|
29
31
|
this.removed = []
|
|
30
32
|
}
|
|
31
33
|
|
|
32
|
-
static calculate ({actual, ideal}) {
|
|
34
|
+
static calculate ({actual, ideal, filterNodes = [], shrinkwrapInflated = new Set()}) {
|
|
35
|
+
// if there's a filterNode, then:
|
|
36
|
+
// - get the path from the root to the filterNode. The root or
|
|
37
|
+
// root.target should have an edge either to the filterNode or
|
|
38
|
+
// a link to the filterNode. If not, abort. Add the path to the
|
|
39
|
+
// filterSet.
|
|
40
|
+
// - Add set of Nodes depended on by the filterNode to filterSet.
|
|
41
|
+
// - Anything outside of that set should be ignored by getChildren
|
|
42
|
+
const filterSet = new Set()
|
|
43
|
+
for (const filterNode of filterNodes) {
|
|
44
|
+
const { root } = filterNode
|
|
45
|
+
if (root !== ideal && root !== actual)
|
|
46
|
+
throw new Error('invalid filterNode: outside idealTree/actualTree')
|
|
47
|
+
const { target } = root
|
|
48
|
+
const rootTarget = target || root
|
|
49
|
+
const edge = [...rootTarget.edgesOut.values()].filter(e => {
|
|
50
|
+
return e.to && (e.to === filterNode || e.to.target === filterNode)
|
|
51
|
+
})[0]
|
|
52
|
+
filterSet.add(root)
|
|
53
|
+
filterSet.add(rootTarget)
|
|
54
|
+
filterSet.add(ideal)
|
|
55
|
+
filterSet.add(actual)
|
|
56
|
+
if (edge && edge.to) {
|
|
57
|
+
filterSet.add(edge.to)
|
|
58
|
+
if (edge.to.target)
|
|
59
|
+
filterSet.add(edge.to.target)
|
|
60
|
+
}
|
|
61
|
+
filterSet.add(filterNode)
|
|
62
|
+
|
|
63
|
+
depth({
|
|
64
|
+
tree: filterNode,
|
|
65
|
+
visit: node => filterSet.add(node),
|
|
66
|
+
getChildren: node => {
|
|
67
|
+
node = node.target || node
|
|
68
|
+
const loc = node.location
|
|
69
|
+
const idealNode = ideal.inventory.get(loc)
|
|
70
|
+
const ideals = !idealNode ? []
|
|
71
|
+
: [...idealNode.edgesOut.values()].filter(e => e.to).map(e => e.to)
|
|
72
|
+
const actualNode = actual.inventory.get(loc)
|
|
73
|
+
const actuals = !actualNode ? []
|
|
74
|
+
: [...actualNode.edgesOut.values()].filter(e => e.to).map(e => e.to)
|
|
75
|
+
return ideals.concat(actuals)
|
|
76
|
+
},
|
|
77
|
+
})
|
|
78
|
+
}
|
|
79
|
+
|
|
33
80
|
return depth({
|
|
34
|
-
tree: new Diff({actual, ideal}),
|
|
81
|
+
tree: new Diff({actual, ideal, filterSet, shrinkwrapInflated}),
|
|
35
82
|
getChildren,
|
|
36
83
|
leave,
|
|
37
84
|
})
|
|
@@ -89,20 +136,29 @@ const allChildren = node => {
|
|
|
89
136
|
// to create the diff tree
|
|
90
137
|
const getChildren = diff => {
|
|
91
138
|
const children = []
|
|
92
|
-
const {unchanged, removed} = diff
|
|
139
|
+
const {actual, ideal, unchanged, removed, filterSet, shrinkwrapInflated} = diff
|
|
93
140
|
|
|
94
141
|
// Note: we DON'T diff fsChildren themselves, because they are either
|
|
95
142
|
// included in the package contents, or part of some other project, and
|
|
96
143
|
// will never appear in legacy shrinkwraps anyway. but we _do_ include the
|
|
97
144
|
// child nodes of fsChildren, because those are nodes that we are typically
|
|
98
145
|
// responsible for installing.
|
|
99
|
-
const actualKids = allChildren(
|
|
100
|
-
const idealKids = allChildren(
|
|
146
|
+
const actualKids = allChildren(actual)
|
|
147
|
+
const idealKids = allChildren(ideal)
|
|
148
|
+
|
|
149
|
+
if (ideal && ideal.hasShrinkwrap && !shrinkwrapInflated.has(ideal)) {
|
|
150
|
+
// Guaranteed to get a diff.leaves here, because we always
|
|
151
|
+
// be called with a proper Diff object when ideal has a shrinkwrap
|
|
152
|
+
// that has not been inflated.
|
|
153
|
+
diff.leaves.push(diff)
|
|
154
|
+
return children
|
|
155
|
+
}
|
|
156
|
+
|
|
101
157
|
const paths = new Set([...actualKids.keys(), ...idealKids.keys()])
|
|
102
158
|
for (const path of paths) {
|
|
103
159
|
const actual = actualKids.get(path)
|
|
104
160
|
const ideal = idealKids.get(path)
|
|
105
|
-
diffNode(actual, ideal, children, unchanged, removed)
|
|
161
|
+
diffNode(actual, ideal, children, unchanged, removed, filterSet, shrinkwrapInflated)
|
|
106
162
|
}
|
|
107
163
|
|
|
108
164
|
if (diff.leaves && !children.length)
|
|
@@ -111,15 +167,18 @@ const getChildren = diff => {
|
|
|
111
167
|
return children
|
|
112
168
|
}
|
|
113
169
|
|
|
114
|
-
const diffNode = (actual, ideal, children, unchanged, removed) => {
|
|
170
|
+
const diffNode = (actual, ideal, children, unchanged, removed, filterSet, shrinkwrapInflated) => {
|
|
171
|
+
if (filterSet.size && !(filterSet.has(ideal) || filterSet.has(actual)))
|
|
172
|
+
return
|
|
173
|
+
|
|
115
174
|
const action = getAction({actual, ideal})
|
|
116
175
|
|
|
117
176
|
// if it's a match, then get its children
|
|
118
177
|
// otherwise, this is the child diff node
|
|
119
|
-
if (action) {
|
|
178
|
+
if (action || (!shrinkwrapInflated.has(ideal) && ideal.hasShrinkwrap)) {
|
|
120
179
|
if (action === 'REMOVE')
|
|
121
180
|
removed.push(actual)
|
|
122
|
-
children.push(new Diff({actual, ideal}))
|
|
181
|
+
children.push(new Diff({actual, ideal, filterSet, shrinkwrapInflated}))
|
|
123
182
|
} else {
|
|
124
183
|
unchanged.push(ideal)
|
|
125
184
|
// !*! Weird dirty hack warning !*!
|
|
@@ -150,7 +209,7 @@ const diffNode = (actual, ideal, children, unchanged, removed) => {
|
|
|
150
209
|
for (const node of bundledChildren)
|
|
151
210
|
node.parent = ideal
|
|
152
211
|
}
|
|
153
|
-
children.push(...getChildren({actual, ideal, unchanged, removed}))
|
|
212
|
+
children.push(...getChildren({actual, ideal, unchanged, removed, filterSet, shrinkwrapInflated}))
|
|
154
213
|
}
|
|
155
214
|
}
|
|
156
215
|
|
package/lib/index.js
CHANGED
|
@@ -3,5 +3,6 @@ module.exports.Arborist = module.exports
|
|
|
3
3
|
module.exports.Node = require('./node.js')
|
|
4
4
|
module.exports.Link = require('./link.js')
|
|
5
5
|
module.exports.Edge = require('./edge.js')
|
|
6
|
+
module.exports.Shrinkwrap = require('./shrinkwrap.js')
|
|
6
7
|
// XXX export the other classes, too. shrinkwrap, diff, etc.
|
|
7
8
|
// they're handy!
|
package/lib/inventory.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
// keys is the set of fields to be able to query.
|
|
5
5
|
const _primaryKey = Symbol('_primaryKey')
|
|
6
6
|
const _index = Symbol('_index')
|
|
7
|
-
const defaultKeys = ['name', 'license', 'funding', 'realpath']
|
|
7
|
+
const defaultKeys = ['name', 'license', 'funding', 'realpath', 'packageName']
|
|
8
8
|
const { hasOwnProperty } = Object.prototype
|
|
9
9
|
const debug = require('./debug.js')
|
|
10
10
|
class Inventory extends Map {
|
package/lib/link.js
CHANGED
|
@@ -23,13 +23,19 @@ class Link extends Node {
|
|
|
23
23
|
: null),
|
|
24
24
|
})
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
26
|
+
if (target)
|
|
27
|
+
this.target = target
|
|
28
|
+
else if (this.realpath === this.root.path)
|
|
29
|
+
this.target = this.root
|
|
30
|
+
else {
|
|
31
|
+
this.target = new Node({
|
|
32
|
+
...options,
|
|
33
|
+
path: realpath,
|
|
34
|
+
parent: null,
|
|
35
|
+
fsParent: null,
|
|
36
|
+
root: this.root,
|
|
37
|
+
})
|
|
38
|
+
}
|
|
33
39
|
}
|
|
34
40
|
|
|
35
41
|
get version () {
|
package/lib/node.js
CHANGED
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
// where we need to quickly find all instances of a given package name within a
|
|
29
29
|
// tree.
|
|
30
30
|
|
|
31
|
+
const semver = require('semver')
|
|
31
32
|
const nameFromFolder = require('@npmcli/name-from-folder')
|
|
32
33
|
const Edge = require('./edge.js')
|
|
33
34
|
const Inventory = require('./inventory.js')
|
|
@@ -290,6 +291,10 @@ class Node {
|
|
|
290
291
|
return this[_package].version || ''
|
|
291
292
|
}
|
|
292
293
|
|
|
294
|
+
get packageName () {
|
|
295
|
+
return this[_package].name || null
|
|
296
|
+
}
|
|
297
|
+
|
|
293
298
|
get pkgid () {
|
|
294
299
|
const { name = '', version = '' } = this.package
|
|
295
300
|
// root package will prefer package name over folder name,
|
|
@@ -349,10 +354,10 @@ class Node {
|
|
|
349
354
|
}
|
|
350
355
|
|
|
351
356
|
const why = {
|
|
352
|
-
name: this.isProjectRoot ? this.
|
|
357
|
+
name: this.isProjectRoot ? this.packageName : this.name,
|
|
353
358
|
version: this.package.version,
|
|
354
359
|
}
|
|
355
|
-
if (this.errors.length || !this.
|
|
360
|
+
if (this.errors.length || !this.packageName || !this.package.version) {
|
|
356
361
|
why.errors = this.errors.length ? this.errors : [
|
|
357
362
|
new Error('invalid package: lacks name and/or version'),
|
|
358
363
|
]
|
|
@@ -459,7 +464,7 @@ class Node {
|
|
|
459
464
|
if (this.isProjectRoot)
|
|
460
465
|
return false
|
|
461
466
|
const { root } = this
|
|
462
|
-
const { type, to } = root.edgesOut.get(this.
|
|
467
|
+
const { type, to } = root.edgesOut.get(this.packageName) || {}
|
|
463
468
|
return type === 'workspace' && to && (to.target === this || to === this)
|
|
464
469
|
}
|
|
465
470
|
|
|
@@ -685,6 +690,7 @@ class Node {
|
|
|
685
690
|
...this.children.values(),
|
|
686
691
|
...this.inventory.values(),
|
|
687
692
|
].filter(n => n !== this))
|
|
693
|
+
|
|
688
694
|
for (const child of family) {
|
|
689
695
|
if (child.root !== root) {
|
|
690
696
|
child[_delistFromMeta]()
|
|
@@ -704,12 +710,14 @@ class Node {
|
|
|
704
710
|
}
|
|
705
711
|
|
|
706
712
|
// if we had a target, and didn't find one in the new root, then bring
|
|
707
|
-
// it over as well
|
|
708
|
-
|
|
713
|
+
// it over as well, but only if we're setting the link into a new root,
|
|
714
|
+
// as we don't want to lose the target any time we remove a link.
|
|
715
|
+
if (this.isLink && target && !this.target && root !== this)
|
|
709
716
|
target.root = root
|
|
710
717
|
|
|
711
718
|
// tree should always be valid upon root setter completion.
|
|
712
719
|
treeCheck(this)
|
|
720
|
+
treeCheck(root)
|
|
713
721
|
}
|
|
714
722
|
|
|
715
723
|
get root () {
|
|
@@ -726,20 +734,14 @@ class Node {
|
|
|
726
734
|
|
|
727
735
|
[_loadDeps] () {
|
|
728
736
|
// Caveat! Order is relevant!
|
|
729
|
-
//
|
|
730
|
-
//
|
|
737
|
+
// Packages in optionalDependencies are optional.
|
|
738
|
+
// Packages in both deps and devDeps are required.
|
|
731
739
|
// Note the subtle breaking change from v6: it is no longer possible
|
|
732
740
|
// to have a different spec for a devDep than production dep.
|
|
733
|
-
this[_loadDepType](this.package.optionalDependencies, 'optional')
|
|
734
741
|
|
|
735
742
|
// Linked targets that are disconnected from the tree are tops,
|
|
736
743
|
// but don't have a 'path' field, only a 'realpath', because we
|
|
737
744
|
// don't know their canonical location. We don't need their devDeps.
|
|
738
|
-
const { isTop, path, sourceReference } = this
|
|
739
|
-
const { isTop: srcTop, path: srcPath } = sourceReference || {}
|
|
740
|
-
if (isTop && path && (!sourceReference || srcTop && srcPath))
|
|
741
|
-
this[_loadDepType](this.package.devDependencies, 'dev')
|
|
742
|
-
|
|
743
745
|
const pd = this.package.peerDependencies
|
|
744
746
|
if (pd && typeof pd === 'object' && !this.legacyPeerDeps) {
|
|
745
747
|
const pm = this.package.peerDependenciesMeta || {}
|
|
@@ -756,19 +758,22 @@ class Node {
|
|
|
756
758
|
}
|
|
757
759
|
|
|
758
760
|
this[_loadDepType](this.package.dependencies, 'prod')
|
|
761
|
+
this[_loadDepType](this.package.optionalDependencies, 'optional')
|
|
762
|
+
|
|
763
|
+
const { isTop, path, sourceReference } = this
|
|
764
|
+
const { isTop: srcTop, path: srcPath } = sourceReference || {}
|
|
765
|
+
if (isTop && path && (!sourceReference || srcTop && srcPath))
|
|
766
|
+
this[_loadDepType](this.package.devDependencies, 'dev')
|
|
759
767
|
}
|
|
760
768
|
|
|
761
|
-
[_loadDepType] (
|
|
762
|
-
const from = this
|
|
769
|
+
[_loadDepType] (deps, type) {
|
|
763
770
|
const ad = this.package.acceptDependencies || {}
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
// Prod deps should not be marked as dev, however.
|
|
768
|
-
// NB: the Edge ctor adds itself to from.edgesOut
|
|
771
|
+
// Because of the order in which _loadDeps runs, we always want to
|
|
772
|
+
// prioritize a new edge over an existing one
|
|
773
|
+
for (const [name, spec] of Object.entries(deps || {})) {
|
|
769
774
|
const current = this.edgesOut.get(name)
|
|
770
|
-
if (!current || current.
|
|
771
|
-
new Edge({ from, name, spec, accept, type })
|
|
775
|
+
if (!current || current.type !== 'workspace')
|
|
776
|
+
new Edge({ from: this, name, spec, accept: ad[name], type })
|
|
772
777
|
}
|
|
773
778
|
}
|
|
774
779
|
|
|
@@ -882,6 +887,43 @@ class Node {
|
|
|
882
887
|
return node.canReplaceWith(this)
|
|
883
888
|
}
|
|
884
889
|
|
|
890
|
+
// return true if it's safe to remove this node, because anything that
|
|
891
|
+
// is depending on it would be fine with the thing that they would resolve
|
|
892
|
+
// to if it was removed, or nothing is depending on it in the first place.
|
|
893
|
+
canDedupe (preferDedupe = false) {
|
|
894
|
+
// not allowed to mess with shrinkwraps or bundles
|
|
895
|
+
if (this.inDepBundle || this.inShrinkwrap)
|
|
896
|
+
return false
|
|
897
|
+
|
|
898
|
+
// it's a top level pkg, or a dep of one
|
|
899
|
+
if (!this.parent || !this.parent.parent)
|
|
900
|
+
return false
|
|
901
|
+
|
|
902
|
+
// no one wants it, remove it
|
|
903
|
+
if (this.edgesIn.size === 0)
|
|
904
|
+
return true
|
|
905
|
+
|
|
906
|
+
const other = this.parent.parent.resolve(this.name)
|
|
907
|
+
|
|
908
|
+
// nothing else, need this one
|
|
909
|
+
if (!other)
|
|
910
|
+
return false
|
|
911
|
+
|
|
912
|
+
// if it's the same thing, then always fine to remove
|
|
913
|
+
if (other.matches(this))
|
|
914
|
+
return true
|
|
915
|
+
|
|
916
|
+
// if the other thing can't replace this, then skip it
|
|
917
|
+
if (!other.canReplace(this))
|
|
918
|
+
return false
|
|
919
|
+
|
|
920
|
+
// if we prefer dedupe, or if the version is greater/equal, take the other
|
|
921
|
+
if (preferDedupe || semver.gte(other.version, this.version))
|
|
922
|
+
return true
|
|
923
|
+
|
|
924
|
+
return false
|
|
925
|
+
}
|
|
926
|
+
|
|
885
927
|
satisfies (requested) {
|
|
886
928
|
if (requested instanceof Edge)
|
|
887
929
|
return this.name === requested.name && requested.satisfiedBy(this)
|
|
@@ -924,8 +966,8 @@ class Node {
|
|
|
924
966
|
|
|
925
967
|
// if no resolved, check both package name and version
|
|
926
968
|
// otherwise, conclude that they are different things
|
|
927
|
-
return this.
|
|
928
|
-
this.
|
|
969
|
+
return this.packageName && node.packageName &&
|
|
970
|
+
this.packageName === node.packageName &&
|
|
929
971
|
this.version && node.version &&
|
|
930
972
|
this.version === node.version
|
|
931
973
|
}
|
package/lib/printable.js
CHANGED
|
@@ -2,12 +2,13 @@
|
|
|
2
2
|
// of the current node and its descendents
|
|
3
3
|
|
|
4
4
|
const util = require('util')
|
|
5
|
+
const relpath = require('./relpath.js')
|
|
5
6
|
|
|
6
7
|
class ArboristNode {
|
|
7
8
|
constructor (tree, path) {
|
|
8
9
|
this.name = tree.name
|
|
9
|
-
if (tree.
|
|
10
|
-
this.packageName = tree.
|
|
10
|
+
if (tree.packageName && tree.packageName !== this.name)
|
|
11
|
+
this.packageName = tree.packageName
|
|
11
12
|
if (tree.version)
|
|
12
13
|
this.version = tree.version
|
|
13
14
|
this.location = tree.location
|
|
@@ -28,6 +29,15 @@ class ArboristNode {
|
|
|
28
29
|
this.peer = true
|
|
29
30
|
if (tree.inBundle)
|
|
30
31
|
this.bundled = true
|
|
32
|
+
if (tree.inDepBundle)
|
|
33
|
+
this.bundler = tree.getBundler().location
|
|
34
|
+
const bd = tree.package && tree.package.bundleDependencies
|
|
35
|
+
if (bd && bd.length)
|
|
36
|
+
this.bundleDependencies = bd
|
|
37
|
+
if (tree.inShrinkwrap)
|
|
38
|
+
this.inShrinkwrap = true
|
|
39
|
+
else if (tree.hasShrinkwrap)
|
|
40
|
+
this.hasShrinkwrap = true
|
|
31
41
|
if (tree.error)
|
|
32
42
|
this.error = treeError(tree.error)
|
|
33
43
|
if (tree.errors && tree.errors.length)
|
|
@@ -47,6 +57,11 @@ class ArboristNode {
|
|
|
47
57
|
.map(edge => new EdgeIn(edge)))
|
|
48
58
|
}
|
|
49
59
|
|
|
60
|
+
if (tree.workspaces && tree.workspaces.size) {
|
|
61
|
+
this.workspaces = new Map([...tree.workspaces.entries()]
|
|
62
|
+
.map(([name, path]) => [name, relpath(tree.root.realpath, path)]))
|
|
63
|
+
}
|
|
64
|
+
|
|
50
65
|
// fsChildren sorted by path
|
|
51
66
|
if (tree.fsChildren.size) {
|
|
52
67
|
this.fsChildren = new Set([...tree.fsChildren]
|
|
@@ -126,6 +141,9 @@ class EdgeIn extends Edge {
|
|
|
126
141
|
}
|
|
127
142
|
|
|
128
143
|
const printableTree = (tree, path = []) => {
|
|
144
|
+
if (!tree)
|
|
145
|
+
return tree
|
|
146
|
+
|
|
129
147
|
const Cls = tree.isLink ? ArboristLink
|
|
130
148
|
: tree.sourceReference ? ArboristVirtualNode
|
|
131
149
|
: ArboristNode
|
package/lib/shrinkwrap.js
CHANGED
|
@@ -254,7 +254,7 @@ class Shrinkwrap {
|
|
|
254
254
|
meta[key.replace(/^_/, '')] = val
|
|
255
255
|
})
|
|
256
256
|
// we only include name if different from the node path name
|
|
257
|
-
const pname = node.
|
|
257
|
+
const pname = node.packageName
|
|
258
258
|
if (pname && pname !== node.name)
|
|
259
259
|
meta.name = pname
|
|
260
260
|
|
|
@@ -825,7 +825,7 @@ class Shrinkwrap {
|
|
|
825
825
|
[_buildLegacyLockfile] (node, lock, path = []) {
|
|
826
826
|
if (node === this.tree) {
|
|
827
827
|
// the root node
|
|
828
|
-
lock.name = node.
|
|
828
|
+
lock.name = node.packageName || node.name
|
|
829
829
|
if (node.version)
|
|
830
830
|
lock.version = node.version
|
|
831
831
|
}
|
|
@@ -870,9 +870,9 @@ class Shrinkwrap {
|
|
|
870
870
|
lock.from = spec.raw
|
|
871
871
|
} else if (!node.isRoot &&
|
|
872
872
|
node.package &&
|
|
873
|
-
node.
|
|
874
|
-
node.
|
|
875
|
-
lock.version = `npm:${node.
|
|
873
|
+
node.packageName &&
|
|
874
|
+
node.packageName !== node.name)
|
|
875
|
+
lock.version = `npm:${node.packageName}@${node.version}`
|
|
876
876
|
else if (node.package && node.version)
|
|
877
877
|
lock.version = node.version
|
|
878
878
|
|
package/lib/tree-check.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
const debug = require('./debug.js')
|
|
2
2
|
|
|
3
3
|
const checkTree = (tree, checkUnreachable = true) => {
|
|
4
|
+
const log = [['START TREE CHECK', tree.path]]
|
|
5
|
+
|
|
4
6
|
// this can only happen in tests where we have a "tree" object
|
|
5
7
|
// that isn't actually a tree.
|
|
6
8
|
if (!tree.root || !tree.root.inventory)
|
|
@@ -9,8 +11,21 @@ const checkTree = (tree, checkUnreachable = true) => {
|
|
|
9
11
|
const { inventory } = tree.root
|
|
10
12
|
const seen = new Set()
|
|
11
13
|
const check = (node, via = tree, viaType = 'self') => {
|
|
14
|
+
log.push([
|
|
15
|
+
'CHECK',
|
|
16
|
+
node && node.location,
|
|
17
|
+
via && via.location,
|
|
18
|
+
viaType,
|
|
19
|
+
'seen=' + seen.has(node),
|
|
20
|
+
'promise=' + !!(node && node.then),
|
|
21
|
+
'root=' + !!(node && node.isRoot),
|
|
22
|
+
])
|
|
23
|
+
|
|
12
24
|
if (!node || seen.has(node) || node.then)
|
|
13
25
|
return
|
|
26
|
+
|
|
27
|
+
seen.add(node)
|
|
28
|
+
|
|
14
29
|
if (node.isRoot && node !== tree.root) {
|
|
15
30
|
throw Object.assign(new Error('double root'), {
|
|
16
31
|
node: node.path,
|
|
@@ -19,6 +34,7 @@ const checkTree = (tree, checkUnreachable = true) => {
|
|
|
19
34
|
root: tree.root.path,
|
|
20
35
|
via: via.path,
|
|
21
36
|
viaType,
|
|
37
|
+
log,
|
|
22
38
|
})
|
|
23
39
|
}
|
|
24
40
|
|
|
@@ -31,6 +47,7 @@ const checkTree = (tree, checkUnreachable = true) => {
|
|
|
31
47
|
via: via.path,
|
|
32
48
|
viaType,
|
|
33
49
|
otherRoot: node.root && node.root.path,
|
|
50
|
+
log,
|
|
34
51
|
})
|
|
35
52
|
}
|
|
36
53
|
|
|
@@ -43,6 +60,7 @@ const checkTree = (tree, checkUnreachable = true) => {
|
|
|
43
60
|
viaType,
|
|
44
61
|
inventory: [...node.inventory.values()].map(node =>
|
|
45
62
|
[node.path, node.location]),
|
|
63
|
+
log,
|
|
46
64
|
})
|
|
47
65
|
}
|
|
48
66
|
|
|
@@ -53,6 +71,7 @@ const checkTree = (tree, checkUnreachable = true) => {
|
|
|
53
71
|
root: tree.root.path,
|
|
54
72
|
via: via.path,
|
|
55
73
|
viaType,
|
|
74
|
+
log,
|
|
56
75
|
})
|
|
57
76
|
}
|
|
58
77
|
|
|
@@ -65,14 +84,38 @@ const checkTree = (tree, checkUnreachable = true) => {
|
|
|
65
84
|
via: via.path,
|
|
66
85
|
viaType,
|
|
67
86
|
devEdges: devEdges.map(e => [e.type, e.name, e.spec, e.error]),
|
|
87
|
+
log,
|
|
88
|
+
})
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (node.path === tree.root.path && node !== tree.root) {
|
|
92
|
+
throw Object.assign(new Error('node with same path as root'), {
|
|
93
|
+
node: node.path,
|
|
94
|
+
tree: tree.path,
|
|
95
|
+
root: tree.root.path,
|
|
96
|
+
via: via.path,
|
|
97
|
+
viaType,
|
|
98
|
+
log,
|
|
99
|
+
})
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (!node.isLink && node.path !== node.realpath) {
|
|
103
|
+
throw Object.assign(new Error('non-link with mismatched path/realpath'), {
|
|
104
|
+
node: node.path,
|
|
105
|
+
tree: tree.path,
|
|
106
|
+
realpath: node.realpath,
|
|
107
|
+
root: tree.root.path,
|
|
108
|
+
via: via.path,
|
|
109
|
+
viaType,
|
|
110
|
+
log,
|
|
68
111
|
})
|
|
69
112
|
}
|
|
70
113
|
|
|
71
114
|
const { parent, fsParent, target } = node
|
|
72
|
-
seen.add(node)
|
|
73
115
|
check(parent, node, 'parent')
|
|
74
116
|
check(fsParent, node, 'fsParent')
|
|
75
117
|
check(target, node, 'target')
|
|
118
|
+
log.push(['CHILDREN', node.location, ...node.children.keys()])
|
|
76
119
|
for (const kid of node.children.values())
|
|
77
120
|
check(kid, node, 'children')
|
|
78
121
|
for (const kid of node.fsChildren)
|
|
@@ -81,6 +124,7 @@ const checkTree = (tree, checkUnreachable = true) => {
|
|
|
81
124
|
check(link, node, 'linksIn')
|
|
82
125
|
for (const top of node.tops)
|
|
83
126
|
check(top, node, 'tops')
|
|
127
|
+
log.push(['DONE', node.location])
|
|
84
128
|
}
|
|
85
129
|
check(tree)
|
|
86
130
|
if (checkUnreachable) {
|
|
@@ -92,6 +136,7 @@ const checkTree = (tree, checkUnreachable = true) => {
|
|
|
92
136
|
location: node.location,
|
|
93
137
|
root: tree.root.path,
|
|
94
138
|
tree: tree.path,
|
|
139
|
+
log,
|
|
95
140
|
})
|
|
96
141
|
}
|
|
97
142
|
}
|
|
@@ -6,8 +6,6 @@ const {resolve} = require('path')
|
|
|
6
6
|
|
|
7
7
|
const parseJSON = require('json-parse-even-better-errors')
|
|
8
8
|
|
|
9
|
-
const { orderDeps } = require('./dep-spec.js')
|
|
10
|
-
|
|
11
9
|
const depTypes = new Set([
|
|
12
10
|
'dependencies',
|
|
13
11
|
'optionalDependencies',
|
|
@@ -15,6 +13,20 @@ const depTypes = new Set([
|
|
|
15
13
|
'peerDependencies',
|
|
16
14
|
])
|
|
17
15
|
|
|
16
|
+
// sort alphabetically all types of deps for a given package
|
|
17
|
+
const orderDeps = (pkg) => {
|
|
18
|
+
for (const type of depTypes) {
|
|
19
|
+
if (pkg && pkg[type]) {
|
|
20
|
+
pkg[type] = Object.keys(pkg[type])
|
|
21
|
+
.sort((a, b) => a.localeCompare(b))
|
|
22
|
+
.reduce((res, key) => {
|
|
23
|
+
res[key] = pkg[type][key]
|
|
24
|
+
return res
|
|
25
|
+
}, {})
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return pkg
|
|
29
|
+
}
|
|
18
30
|
const parseJsonSafe = json => {
|
|
19
31
|
try {
|
|
20
32
|
return parseJSON(json)
|
package/lib/vuln.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@npmcli/arborist",
|
|
3
|
-
"version": "2.2
|
|
3
|
+
"version": "2.4.2",
|
|
4
4
|
"description": "Manage node_modules trees",
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"@npmcli/installed-package-contents": "^1.0.7",
|
|
@@ -14,12 +14,12 @@
|
|
|
14
14
|
"cacache": "^15.0.3",
|
|
15
15
|
"common-ancestor-path": "^1.0.1",
|
|
16
16
|
"json-parse-even-better-errors": "^2.3.1",
|
|
17
|
-
"json-stringify-nice": "^1.1.
|
|
17
|
+
"json-stringify-nice": "^1.1.2",
|
|
18
18
|
"mkdirp-infer-owner": "^2.0.0",
|
|
19
19
|
"npm-install-checks": "^4.0.0",
|
|
20
20
|
"npm-package-arg": "^8.1.0",
|
|
21
21
|
"npm-pick-manifest": "^6.1.0",
|
|
22
|
-
"npm-registry-fetch": "^
|
|
22
|
+
"npm-registry-fetch": "^10.0.0",
|
|
23
23
|
"pacote": "^11.2.6",
|
|
24
24
|
"parse-conflict-json": "^1.1.1",
|
|
25
25
|
"promise-all-reject-late": "^1.0.0",
|
|
@@ -41,8 +41,7 @@
|
|
|
41
41
|
"eslint-plugin-standard": "^4.0.1",
|
|
42
42
|
"minify-registry-metadata": "^2.1.0",
|
|
43
43
|
"mutate-fs": "^2.1.1",
|
|
44
|
-
"
|
|
45
|
-
"tap": "^14.11.0",
|
|
44
|
+
"tap": "^15.0.4",
|
|
46
45
|
"tcompare": "^3.0.4"
|
|
47
46
|
},
|
|
48
47
|
"scripts": {
|
|
@@ -76,10 +75,8 @@
|
|
|
76
75
|
"arborist": "bin/index.js"
|
|
77
76
|
},
|
|
78
77
|
"tap": {
|
|
79
|
-
"100": true,
|
|
80
78
|
"after": "test/fixtures/cleanup.js",
|
|
81
79
|
"coverage-map": "map.js",
|
|
82
|
-
"esm": false,
|
|
83
80
|
"test-env": [
|
|
84
81
|
"NODE_OPTIONS=--no-warnings"
|
|
85
82
|
],
|
|
@@ -87,6 +84,6 @@
|
|
|
87
84
|
"--no-warnings",
|
|
88
85
|
"--no-deprecation"
|
|
89
86
|
],
|
|
90
|
-
"timeout": "
|
|
87
|
+
"timeout": "240"
|
|
91
88
|
}
|
|
92
89
|
}
|
package/lib/dep-spec.js
DELETED
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
const types = [
|
|
2
|
-
'peerDependencies',
|
|
3
|
-
'devDependencies',
|
|
4
|
-
'optionalDependencies',
|
|
5
|
-
'dependencies',
|
|
6
|
-
]
|
|
7
|
-
|
|
8
|
-
const findType = (pkg, name) => {
|
|
9
|
-
for (const t of types) {
|
|
10
|
-
if (pkg[t] && typeof pkg[t] === 'object' && pkg[t][name] !== undefined)
|
|
11
|
-
return t
|
|
12
|
-
}
|
|
13
|
-
return 'dependencies'
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
// given a dep name and spec, update it wherever it exists in
|
|
17
|
-
// the manifest, or add the spec to 'dependencies' if not found.
|
|
18
|
-
const updateDepSpec = (pkg, name, newSpec) => {
|
|
19
|
-
const type = findType(pkg, name)
|
|
20
|
-
pkg[type] = pkg[type] || {}
|
|
21
|
-
pkg[type][name] = newSpec
|
|
22
|
-
return pkg
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
// sort alphabetically all types of deps for a given package
|
|
26
|
-
const orderDeps = (pkg) => {
|
|
27
|
-
for (const type of types) {
|
|
28
|
-
if (pkg && pkg[type]) {
|
|
29
|
-
pkg[type] = Object.keys(pkg[type])
|
|
30
|
-
.sort((a, b) => a.localeCompare(b))
|
|
31
|
-
.reduce((res, key) => {
|
|
32
|
-
res[key] = pkg[type][key]
|
|
33
|
-
return res
|
|
34
|
-
}, {})
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
return pkg
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
module.exports = {
|
|
41
|
-
orderDeps,
|
|
42
|
-
updateDepSpec,
|
|
43
|
-
}
|