@npmcli/arborist 2.8.2 → 2.9.0

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.
Files changed (49) hide show
  1. package/bin/actual.js +4 -2
  2. package/bin/audit.js +12 -6
  3. package/bin/dedupe.js +6 -3
  4. package/bin/funding.js +4 -2
  5. package/bin/ideal.js +2 -1
  6. package/bin/lib/logging.js +4 -3
  7. package/bin/lib/options.js +14 -12
  8. package/bin/lib/timers.js +6 -3
  9. package/bin/license.js +9 -5
  10. package/bin/prune.js +6 -3
  11. package/bin/reify.js +6 -3
  12. package/bin/virtual.js +4 -2
  13. package/lib/add-rm-pkg-deps.js +28 -15
  14. package/lib/arborist/audit.js +2 -1
  15. package/lib/arborist/build-ideal-tree.js +139 -72
  16. package/lib/arborist/deduper.js +2 -1
  17. package/lib/arborist/index.js +8 -4
  18. package/lib/arborist/load-actual.js +28 -13
  19. package/lib/arborist/load-virtual.js +37 -20
  20. package/lib/arborist/load-workspaces.js +4 -2
  21. package/lib/arborist/rebuild.js +34 -17
  22. package/lib/arborist/reify.js +153 -76
  23. package/lib/audit-report.js +44 -23
  24. package/lib/calc-dep-flags.js +18 -9
  25. package/lib/can-place-dep.js +59 -30
  26. package/lib/case-insensitive-map.js +4 -2
  27. package/lib/consistent-resolve.js +2 -1
  28. package/lib/deepest-nesting-target.js +4 -2
  29. package/lib/dep-valid.js +8 -4
  30. package/lib/diff.js +74 -22
  31. package/lib/edge.js +26 -13
  32. package/lib/gather-dep-set.js +2 -1
  33. package/lib/inventory.js +12 -6
  34. package/lib/link.js +14 -9
  35. package/lib/node.js +216 -113
  36. package/lib/optional-set.js +4 -2
  37. package/lib/peer-entry-sets.js +10 -5
  38. package/lib/place-dep.js +111 -37
  39. package/lib/printable.js +46 -25
  40. package/lib/realpath.js +12 -6
  41. package/lib/shrinkwrap.js +164 -90
  42. package/lib/signal-handling.js +6 -3
  43. package/lib/spec-from-lock.js +7 -4
  44. package/lib/tracker.js +24 -18
  45. package/lib/tree-check.js +12 -6
  46. package/lib/version-from-tgz.js +4 -2
  47. package/lib/vuln.js +44 -22
  48. package/lib/yarn-lock.js +34 -21
  49. package/package.json +8 -10
package/lib/diff.js CHANGED
@@ -31,7 +31,12 @@ class Diff {
31
31
  this.removed = []
32
32
  }
33
33
 
34
- static calculate ({actual, ideal, filterNodes = [], shrinkwrapInflated = new Set()}) {
34
+ static calculate ({
35
+ actual,
36
+ ideal,
37
+ filterNodes = [],
38
+ shrinkwrapInflated = new Set(),
39
+ }) {
35
40
  // if there's a filterNode, then:
36
41
  // - get the path from the root to the filterNode. The root or
37
42
  // root.target should have an edge either to the filterNode or
@@ -43,8 +48,9 @@ class Diff {
43
48
  const extraneous = new Set()
44
49
  for (const filterNode of filterNodes) {
45
50
  const { root } = filterNode
46
- if (root !== ideal && root !== actual)
51
+ if (root !== ideal && root !== actual) {
47
52
  throw new Error('invalid filterNode: outside idealTree/actualTree')
53
+ }
48
54
  const rootTarget = root.target
49
55
  const edge = [...rootTarget.edgesOut.values()].filter(e => {
50
56
  return e.to && (e.to === filterNode || e.to.target === filterNode)
@@ -73,8 +79,9 @@ class Diff {
73
79
  : [...actualNode.edgesOut.values()].filter(e => e.to).map(e => e.to)
74
80
  if (actualNode) {
75
81
  for (const child of actualNode.children.values()) {
76
- if (child.extraneous)
82
+ if (child.extraneous) {
77
83
  extraneous.add(child)
84
+ }
78
85
  }
79
86
  }
80
87
 
@@ -82,8 +89,9 @@ class Diff {
82
89
  },
83
90
  })
84
91
  }
85
- for (const extra of extraneous)
92
+ for (const extra of extraneous) {
86
93
  filterSet.add(extra)
94
+ }
87
95
 
88
96
  return depth({
89
97
  tree: new Diff({actual, ideal, filterSet, shrinkwrapInflated}),
@@ -94,23 +102,27 @@ class Diff {
94
102
  }
95
103
 
96
104
  const getAction = ({actual, ideal}) => {
97
- if (!ideal)
105
+ if (!ideal) {
98
106
  return 'REMOVE'
107
+ }
99
108
 
100
109
  // bundled meta-deps are copied over to the ideal tree when we visit it,
101
110
  // so they'll appear to be missing here. There's no need to handle them
102
111
  // in the diff, though, because they'll be replaced at reify time anyway
103
112
  // Otherwise, add the missing node.
104
- if (!actual)
113
+ if (!actual) {
105
114
  return ideal.inDepBundle ? null : 'ADD'
115
+ }
106
116
 
107
117
  // always ignore the root node
108
- if (ideal.isRoot && actual.isRoot)
118
+ if (ideal.isRoot && actual.isRoot) {
109
119
  return null
120
+ }
110
121
 
111
122
  // if the versions don't match, it's a change no matter what
112
- if (ideal.version !== actual.version)
123
+ if (ideal.version !== actual.version) {
113
124
  return 'CHANGE'
125
+ }
114
126
 
115
127
  const binsExist = ideal.binPaths.every((path) => existsSync(path))
116
128
 
@@ -125,33 +137,38 @@ const getAction = ({actual, ideal}) => {
125
137
  const noIntegrity = !ideal.integrity && !actual.integrity
126
138
  const noResolved = !ideal.resolved && !actual.resolved
127
139
  const resolvedMatch = ideal.resolved && ideal.resolved === actual.resolved
128
- if (noIntegrity && binsExist && (resolvedMatch || noResolved))
140
+ if (noIntegrity && binsExist && (resolvedMatch || noResolved)) {
129
141
  return null
142
+ }
130
143
 
131
144
  // otherwise, verify that it's the same bits
132
145
  // note that if ideal has integrity, and resolved doesn't, we treat
133
146
  // that as a 'change', so that it gets re-fetched and locked down.
134
147
  const integrityMismatch = !ideal.integrity || !actual.integrity ||
135
148
  !ssri.parse(ideal.integrity).match(actual.integrity)
136
- if (integrityMismatch || !binsExist)
149
+ if (integrityMismatch || !binsExist) {
137
150
  return 'CHANGE'
151
+ }
138
152
 
139
153
  return null
140
154
  }
141
155
 
142
156
  const allChildren = node => {
143
- if (!node)
157
+ if (!node) {
144
158
  return new Map()
159
+ }
145
160
 
146
161
  // if the node is root, and also a link, then what we really
147
162
  // want is to traverse the target's children
148
- if (node.isRoot && node.isLink)
163
+ if (node.isRoot && node.isLink) {
149
164
  return allChildren(node.target)
165
+ }
150
166
 
151
167
  const kids = new Map()
152
168
  for (const n of [node, ...node.fsChildren]) {
153
- for (const kid of n.children.values())
169
+ for (const kid of n.children.values()) {
154
170
  kids.set(kid.path, kid)
171
+ }
155
172
  }
156
173
  return kids
157
174
  }
@@ -160,7 +177,14 @@ const allChildren = node => {
160
177
  // to create the diff tree
161
178
  const getChildren = diff => {
162
179
  const children = []
163
- const {actual, ideal, unchanged, removed, filterSet, shrinkwrapInflated} = diff
180
+ const {
181
+ actual,
182
+ ideal,
183
+ unchanged,
184
+ removed,
185
+ filterSet,
186
+ shrinkwrapInflated,
187
+ } = diff
164
188
 
165
189
  // Note: we DON'T diff fsChildren themselves, because they are either
166
190
  // included in the package contents, or part of some other project, and
@@ -182,26 +206,45 @@ const getChildren = diff => {
182
206
  for (const path of paths) {
183
207
  const actual = actualKids.get(path)
184
208
  const ideal = idealKids.get(path)
185
- diffNode(actual, ideal, children, unchanged, removed, filterSet, shrinkwrapInflated)
209
+ diffNode({
210
+ actual,
211
+ ideal,
212
+ children,
213
+ unchanged,
214
+ removed,
215
+ filterSet,
216
+ shrinkwrapInflated,
217
+ })
186
218
  }
187
219
 
188
- if (diff.leaves && !children.length)
220
+ if (diff.leaves && !children.length) {
189
221
  diff.leaves.push(diff)
222
+ }
190
223
 
191
224
  return children
192
225
  }
193
226
 
194
- const diffNode = (actual, ideal, children, unchanged, removed, filterSet, shrinkwrapInflated) => {
195
- if (filterSet.size && !(filterSet.has(ideal) || filterSet.has(actual)))
227
+ const diffNode = ({
228
+ actual,
229
+ ideal,
230
+ children,
231
+ unchanged,
232
+ removed,
233
+ filterSet,
234
+ shrinkwrapInflated,
235
+ }) => {
236
+ if (filterSet.size && !(filterSet.has(ideal) || filterSet.has(actual))) {
196
237
  return
238
+ }
197
239
 
198
240
  const action = getAction({actual, ideal})
199
241
 
200
242
  // if it's a match, then get its children
201
243
  // otherwise, this is the child diff node
202
244
  if (action || (!shrinkwrapInflated.has(ideal) && ideal.hasShrinkwrap)) {
203
- if (action === 'REMOVE')
245
+ if (action === 'REMOVE') {
204
246
  removed.push(actual)
247
+ }
205
248
  children.push(new Diff({actual, ideal, filterSet, shrinkwrapInflated}))
206
249
  } else {
207
250
  unchanged.push(ideal)
@@ -227,13 +270,22 @@ const diffNode = (actual, ideal, children, unchanged, removed, filterSet, shrink
227
270
  if (actual && bd && bd.length) {
228
271
  const bundledChildren = []
229
272
  for (const node of actual.children.values()) {
230
- if (node.inBundle)
273
+ if (node.inBundle) {
231
274
  bundledChildren.push(node)
275
+ }
232
276
  }
233
- for (const node of bundledChildren)
277
+ for (const node of bundledChildren) {
234
278
  node.parent = ideal
279
+ }
235
280
  }
236
- children.push(...getChildren({actual, ideal, unchanged, removed, filterSet, shrinkwrapInflated}))
281
+ children.push(...getChildren({
282
+ actual,
283
+ ideal,
284
+ unchanged,
285
+ removed,
286
+ filterSet,
287
+ shrinkwrapInflated,
288
+ }))
237
289
  }
238
290
  }
239
291
 
package/lib/edge.js CHANGED
@@ -45,22 +45,26 @@ class Edge {
45
45
  constructor (options) {
46
46
  const { type, name, spec, accept, from } = options
47
47
 
48
- if (typeof spec !== 'string')
48
+ if (typeof spec !== 'string') {
49
49
  throw new TypeError('must provide string spec')
50
+ }
50
51
 
51
- if (type === 'workspace' && npa(spec).type !== 'directory')
52
+ if (type === 'workspace' && npa(spec).type !== 'directory') {
52
53
  throw new TypeError('workspace edges must be a symlink')
54
+ }
53
55
 
54
56
  this[_spec] = spec
55
57
 
56
58
  if (accept !== undefined) {
57
- if (typeof accept !== 'string')
59
+ if (typeof accept !== 'string') {
58
60
  throw new TypeError('accept field must be a string if provided')
61
+ }
59
62
  this[_accept] = accept || '*'
60
63
  }
61
64
 
62
- if (typeof name !== 'string')
65
+ if (typeof name !== 'string') {
63
66
  throw new TypeError('must provide dependency name')
67
+ }
64
68
  this[_name] = name
65
69
 
66
70
  if (!types.has(type)) {
@@ -69,20 +73,23 @@ class Edge {
69
73
  `(valid types are: ${Edge.types.join(', ')})`)
70
74
  }
71
75
  this[_type] = type
72
- if (!from)
76
+ if (!from) {
73
77
  throw new TypeError('must provide "from" node')
78
+ }
74
79
  this[_setFrom](from)
75
80
  this[_error] = this[_loadError]()
76
81
  this.overridden = false
77
82
  }
78
83
 
79
84
  satisfiedBy (node) {
80
- return node.name === this.name && depValid(node, this.spec, this.accept, this.from)
85
+ return node.name === this.name &&
86
+ depValid(node, this.spec, this.accept, this.from)
81
87
  }
82
88
 
83
89
  explain (seen = []) {
84
- if (this[_explanation])
90
+ if (this[_explanation]) {
85
91
  return this[_explanation]
92
+ }
86
93
 
87
94
  return this[_explanation] = this[_explain](seen)
88
95
  }
@@ -101,8 +108,9 @@ class Edge {
101
108
  }
102
109
 
103
110
  get bundled () {
104
- if (!this.from)
111
+ if (!this.from) {
105
112
  return false
113
+ }
106
114
  const { package: { bundleDependencies = [] } } = this.from
107
115
  return bundleDependencies.includes(this.name)
108
116
  }
@@ -175,20 +183,24 @@ class Edge {
175
183
  this[_explanation] = null
176
184
  const newTo = this[_from].resolve(this.name)
177
185
  if (newTo !== this[_to]) {
178
- if (this[_to])
186
+ if (this[_to]) {
179
187
  this[_to].edgesIn.delete(this)
188
+ }
180
189
  this[_to] = newTo
181
190
  this[_error] = this[_loadError]()
182
- if (this[_to])
191
+ if (this[_to]) {
183
192
  this[_to].addEdgeIn(this)
184
- } else if (hard)
193
+ }
194
+ } else if (hard) {
185
195
  this[_error] = this[_loadError]()
196
+ }
186
197
  }
187
198
 
188
199
  detach () {
189
200
  this[_explanation] = null
190
- if (this[_to])
201
+ if (this[_to]) {
191
202
  this[_to].edgesIn.delete(this)
203
+ }
192
204
  this[_from].edgesOut.delete(this.name)
193
205
  this[_to] = null
194
206
  this[_error] = 'DETACHED'
@@ -198,8 +210,9 @@ class Edge {
198
210
  [_setFrom] (node) {
199
211
  this[_explanation] = null
200
212
  this[_from] = node
201
- if (node.edgesOut.has(this.name))
213
+ if (node.edgesOut.has(this.name)) {
202
214
  node.edgesOut.get(this.name).detach()
215
+ }
203
216
  node.addEdgeOut(this)
204
217
  this.reload()
205
218
  }
@@ -14,8 +14,9 @@ const gatherDepSet = (set, edgeFilter) => {
14
14
  // as the deps set increases in size.
15
15
  for (const node of deps) {
16
16
  for (const edge of node.edgesOut.values()) {
17
- if (edge.to && edgeFilter(edge))
17
+ if (edge.to && edgeFilter(edge)) {
18
18
  deps.add(edge.to)
19
+ }
19
20
  }
20
21
  }
21
22
 
package/lib/inventory.js CHANGED
@@ -13,11 +13,13 @@ const debug = require('./debug.js')
13
13
  const getLicense = pkg => {
14
14
  if (pkg) {
15
15
  const lic = pkg.license || pkg.licence
16
- if (lic)
16
+ if (lic) {
17
17
  return lic
18
+ }
18
19
  const lics = pkg.licenses || pkg.licences
19
- if (Array.isArray(lics))
20
+ if (Array.isArray(lics)) {
20
21
  return lics[0]
22
+ }
21
23
  }
22
24
  }
23
25
 
@@ -42,8 +44,9 @@ class Inventory extends Map {
42
44
 
43
45
  * filter (fn) {
44
46
  for (const node of this.values()) {
45
- if (fn(node))
47
+ if (fn(node)) {
46
48
  yield node
49
+ }
47
50
  }
48
51
  }
49
52
 
@@ -62,8 +65,9 @@ class Inventory extends Map {
62
65
 
63
66
  const current = super.get(node[this.primaryKey])
64
67
  if (current) {
65
- if (current === node)
68
+ if (current === node) {
66
69
  return
70
+ }
67
71
  this.delete(current)
68
72
  }
69
73
  super.set(node[this.primaryKey], node)
@@ -85,8 +89,9 @@ class Inventory extends Map {
85
89
  }
86
90
 
87
91
  delete (node) {
88
- if (!this.has(node))
92
+ if (!this.has(node)) {
89
93
  return
94
+ }
90
95
 
91
96
  super.delete(node[this.primaryKey])
92
97
  for (const [key, map] of this[_index].entries()) {
@@ -95,8 +100,9 @@ class Inventory extends Map {
95
100
  const set = map.get(val)
96
101
  if (set) {
97
102
  set.delete(node)
98
- if (set.size === 0)
103
+ if (set.size === 0) {
99
104
  map.delete(node[key])
105
+ }
100
106
  }
101
107
  }
102
108
  }
package/lib/link.js CHANGED
@@ -11,8 +11,9 @@ class Link extends Node {
11
11
  constructor (options) {
12
12
  const { root, realpath, target, parent, fsParent } = options
13
13
 
14
- if (!realpath && !(target && target.path))
14
+ if (!realpath && !(target && target.path)) {
15
15
  throw new TypeError('must provide realpath for Link node')
16
+ }
16
17
 
17
18
  super({
18
19
  ...options,
@@ -23,11 +24,11 @@ class Link extends Node {
23
24
  : null),
24
25
  })
25
26
 
26
- if (target)
27
+ if (target) {
27
28
  this.target = target
28
- else if (this.realpath === this.root.path)
29
+ } else if (this.realpath === this.root.path) {
29
30
  this.target = this.root
30
- else {
31
+ } else {
31
32
  this.target = new Node({
32
33
  ...options,
33
34
  path: realpath,
@@ -48,8 +49,9 @@ class Link extends Node {
48
49
 
49
50
  set target (target) {
50
51
  const current = this[_target]
51
- if (target === current)
52
+ if (target === current) {
52
53
  return
54
+ }
53
55
 
54
56
  if (current && current.then) {
55
57
  debug(() => {
@@ -72,25 +74,28 @@ class Link extends Node {
72
74
  }
73
75
 
74
76
  if (!target) {
75
- if (current && current.linksIn)
77
+ if (current && current.linksIn) {
76
78
  current.linksIn.delete(this)
79
+ }
77
80
  if (this.path) {
78
81
  this[_delistFromMeta]()
79
82
  this[_target] = null
80
83
  this.package = {}
81
84
  this[_refreshLocation]()
82
- } else
85
+ } else {
83
86
  this[_target] = null
87
+ }
84
88
  return
85
89
  }
86
90
 
87
91
  if (!this.path) {
88
92
  // temp node pending assignment to a tree
89
93
  // we know it's not in the inventory yet, because no path.
90
- if (target.path)
94
+ if (target.path) {
91
95
  this.realpath = target.path
92
- else
96
+ } else {
93
97
  target.path = target.realpath = this.realpath
98
+ }
94
99
  target.root = this.root
95
100
  this[_target] = target
96
101
  target.linksIn.add(this)