@npmcli/arborist 2.7.1 → 2.8.3

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 (50) hide show
  1. package/bin/actual.js +4 -2
  2. package/bin/audit.js +12 -6
  3. package/bin/dedupe.js +49 -0
  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 +25 -14
  14. package/lib/arborist/audit.js +2 -1
  15. package/lib/arborist/build-ideal-tree.js +246 -757
  16. package/lib/arborist/deduper.js +2 -1
  17. package/lib/arborist/index.js +8 -4
  18. package/lib/arborist/load-actual.js +32 -15
  19. package/lib/arborist/load-virtual.js +34 -18
  20. package/lib/arborist/load-workspaces.js +4 -2
  21. package/lib/arborist/rebuild.js +31 -16
  22. package/lib/arborist/reify.js +332 -119
  23. package/lib/audit-report.js +42 -22
  24. package/lib/calc-dep-flags.js +18 -9
  25. package/lib/can-place-dep.js +430 -0
  26. package/lib/case-insensitive-map.js +50 -0
  27. package/lib/consistent-resolve.js +2 -1
  28. package/lib/deepest-nesting-target.js +18 -0
  29. package/lib/dep-valid.js +8 -4
  30. package/lib/diff.js +74 -22
  31. package/lib/edge.js +29 -14
  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 +269 -118
  36. package/lib/optional-set.js +4 -2
  37. package/lib/peer-entry-sets.js +77 -0
  38. package/lib/place-dep.js +578 -0
  39. package/lib/printable.js +48 -18
  40. package/lib/realpath.js +12 -6
  41. package/lib/shrinkwrap.js +168 -91
  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 +28 -16
  48. package/lib/yarn-lock.js +27 -15
  49. package/package.json +9 -13
  50. package/lib/peer-set.js +0 -25
package/lib/tracker.js CHANGED
@@ -11,35 +11,37 @@ module.exports = cls => class Tracker extends cls {
11
11
 
12
12
  addTracker (section, subsection = null, key = null) {
13
13
  // TrackerGroup type object not found
14
- if (!this.log.newGroup)
14
+ if (!this.log.newGroup) {
15
15
  return
16
+ }
16
17
 
17
- if (section === null || section === undefined)
18
+ if (section === null || section === undefined) {
18
19
  this[_onError](`Tracker can't be null or undefined`)
20
+ }
19
21
 
20
- if (key === null)
22
+ if (key === null) {
21
23
  key = subsection
24
+ }
22
25
 
23
26
  const hasTracker = this[_progress].has(section)
24
27
  const hasSubtracker = this[_progress].has(`${section}:${key}`)
25
28
 
26
- if (hasTracker && subsection === null)
29
+ if (hasTracker && subsection === null) {
27
30
  // 0. existing tracker, no subsection
28
31
  this[_onError](`Tracker "${section}" already exists`)
29
-
30
- else if (!hasTracker && subsection === null) {
32
+ } else if (!hasTracker && subsection === null) {
31
33
  // 1. no existing tracker, no subsection
32
34
  // Create a new tracker from this.log
33
35
  // starts progress bar
34
- if (this[_progress].size === 0)
36
+ if (this[_progress].size === 0) {
35
37
  this.log.enableProgress()
38
+ }
36
39
 
37
40
  this[_progress].set(section, this.log.newGroup(section))
38
- } else if (!hasTracker && subsection !== null)
41
+ } else if (!hasTracker && subsection !== null) {
39
42
  // 2. no parent tracker and subsection
40
43
  this[_onError](`Parent tracker "${section}" does not exist`)
41
-
42
- else if (!hasTracker || !hasSubtracker) {
44
+ } else if (!hasTracker || !hasSubtracker) {
43
45
  // 3. existing parent tracker, no subsection tracker
44
46
  // Create a new subtracker in this[_progress] from parent tracker
45
47
  this[_progress].set(`${section}:${key}`,
@@ -52,14 +54,17 @@ module.exports = cls => class Tracker extends cls {
52
54
 
53
55
  finishTracker (section, subsection = null, key = null) {
54
56
  // TrackerGroup type object not found
55
- if (!this.log.newGroup)
57
+ if (!this.log.newGroup) {
56
58
  return
59
+ }
57
60
 
58
- if (section === null || section === undefined)
61
+ if (section === null || section === undefined) {
59
62
  this[_onError](`Tracker can't be null or undefined`)
63
+ }
60
64
 
61
- if (key === null)
65
+ if (key === null) {
62
66
  key = subsection
67
+ }
63
68
 
64
69
  const hasTracker = this[_progress].has(section)
65
70
  const hasSubtracker = this[_progress].has(`${section}:${key}`)
@@ -71,8 +76,9 @@ module.exports = cls => class Tracker extends cls {
71
76
  // not have any remaining children
72
77
  const keys = this[_progress].keys()
73
78
  for (const key of keys) {
74
- if (key.match(new RegExp(section + ':')))
79
+ if (key.match(new RegExp(section + ':'))) {
75
80
  this.finishTracker(section, key)
81
+ }
76
82
  }
77
83
 
78
84
  // remove parent tracker
@@ -81,13 +87,13 @@ module.exports = cls => class Tracker extends cls {
81
87
 
82
88
  // remove progress bar if all
83
89
  // trackers are finished
84
- if (this[_progress].size === 0)
90
+ if (this[_progress].size === 0) {
85
91
  this.log.disableProgress()
86
- } else if (!hasTracker && subsection === null)
92
+ }
93
+ } else if (!hasTracker && subsection === null) {
87
94
  // 1. no existing parent tracker, no subsection
88
95
  this[_onError](`Tracker "${section}" does not exist`)
89
-
90
- else if (!hasTracker || hasSubtracker) {
96
+ } else if (!hasTracker || hasSubtracker) {
91
97
  // 2. subtracker exists
92
98
  // Finish subtracker and remove from this[_progress]
93
99
  this[_progress].get(`${section}:${key}`).finish()
package/lib/tree-check.js CHANGED
@@ -5,8 +5,9 @@ const checkTree = (tree, checkUnreachable = true) => {
5
5
 
6
6
  // this can only happen in tests where we have a "tree" object
7
7
  // that isn't actually a tree.
8
- if (!tree.root || !tree.root.inventory)
8
+ if (!tree.root || !tree.root.inventory) {
9
9
  return tree
10
+ }
10
11
 
11
12
  const { inventory } = tree.root
12
13
  const seen = new Set()
@@ -21,8 +22,9 @@ const checkTree = (tree, checkUnreachable = true) => {
21
22
  'root=' + !!(node && node.isRoot),
22
23
  ])
23
24
 
24
- if (!node || seen.has(node) || node.then)
25
+ if (!node || seen.has(node) || node.then) {
25
26
  return
27
+ }
26
28
 
27
29
  seen.add(node)
28
30
 
@@ -116,14 +118,18 @@ const checkTree = (tree, checkUnreachable = true) => {
116
118
  check(fsParent, node, 'fsParent')
117
119
  check(target, node, 'target')
118
120
  log.push(['CHILDREN', node.location, ...node.children.keys()])
119
- for (const kid of node.children.values())
121
+ for (const kid of node.children.values()) {
120
122
  check(kid, node, 'children')
121
- for (const kid of node.fsChildren)
123
+ }
124
+ for (const kid of node.fsChildren) {
122
125
  check(kid, node, 'fsChildren')
123
- for (const link of node.linksIn)
126
+ }
127
+ for (const link of node.linksIn) {
124
128
  check(link, node, 'linksIn')
125
- for (const top of node.tops)
129
+ }
130
+ for (const top of node.tops) {
126
131
  check(top, node, 'tops')
132
+ }
127
133
  log.push(['DONE', node.location])
128
134
  }
129
135
  check(tree)
@@ -4,8 +4,9 @@ const {basename} = require('path')
4
4
  const {parse} = require('url')
5
5
  module.exports = (name, tgz) => {
6
6
  const base = basename(tgz)
7
- if (!base.endsWith('.tgz'))
7
+ if (!base.endsWith('.tgz')) {
8
8
  return null
9
+ }
9
10
 
10
11
  const u = parse(tgz)
11
12
  if (/^https?:/.test(u.protocol)) {
@@ -35,8 +36,9 @@ module.exports = (name, tgz) => {
35
36
  }
36
37
 
37
38
  const versionFromBaseScopeName = (base, scope, name) => {
38
- if (!base.startsWith(name + '-'))
39
+ if (!base.startsWith(name + '-')) {
39
40
  return null
41
+ }
40
42
 
41
43
  const parsed = semver.parse(base.substring(name.length + 1, base.length - 4))
42
44
  return parsed ? {
package/lib/vuln.js CHANGED
@@ -28,8 +28,9 @@ const severities = new Map([
28
28
  [null, -1],
29
29
  ])
30
30
 
31
- for (const [name, val] of severities.entries())
31
+ for (const [name, val] of severities.entries()) {
32
32
  severities.set(val, name)
33
+ }
33
34
 
34
35
  class Vuln {
35
36
  constructor ({ name, advisory }) {
@@ -43,7 +44,7 @@ class Vuln {
43
44
  this[_simpleRange] = null
44
45
  this.nodes = new Set()
45
46
  // assume a fix is available unless it hits a top node
46
- // that locks it in place, setting this to false or {isSemVerMajor, version}.
47
+ // that locks it in place, setting this false or {isSemVerMajor, version}.
47
48
  this[_fixAvailable] = true
48
49
  this.addAdvisory(advisory)
49
50
  this.packument = advisory.packument
@@ -65,30 +66,35 @@ class Vuln {
65
66
  // - true: fix does not require -f
66
67
  for (const v of this.via) {
67
68
  // don't blow up on loops
68
- if (v.fixAvailable === f)
69
+ if (v.fixAvailable === f) {
69
70
  continue
71
+ }
70
72
 
71
- if (f === false)
73
+ if (f === false) {
72
74
  v.fixAvailable = f
73
- else if (v.fixAvailable === true)
75
+ } else if (v.fixAvailable === true) {
74
76
  v.fixAvailable = f
75
- else if (typeof f === 'object' && (
76
- typeof v.fixAvailable !== 'object' || !v.fixAvailable.isSemVerMajor))
77
+ } else if (typeof f === 'object' && (
78
+ typeof v.fixAvailable !== 'object' || !v.fixAvailable.isSemVerMajor)) {
77
79
  v.fixAvailable = f
80
+ }
78
81
  }
79
82
  }
80
83
 
81
84
  testSpec (spec) {
82
85
  const specObj = npa(spec)
83
- if (!specObj.registry)
86
+ if (!specObj.registry) {
84
87
  return true
88
+ }
85
89
 
86
- if (specObj.subSpec)
90
+ if (specObj.subSpec) {
87
91
  spec = specObj.subSpec.rawSpec
92
+ }
88
93
 
89
94
  for (const v of this.versions) {
90
- if (satisfies(v, spec) && !satisfies(v, this.range, semverOpt))
95
+ if (satisfies(v, spec) && !satisfies(v, this.range, semverOpt)) {
91
96
  return false
97
+ }
92
98
  }
93
99
  return true
94
100
  }
@@ -135,14 +141,16 @@ class Vuln {
135
141
  this[_range] = null
136
142
  this[_simpleRange] = null
137
143
  // refresh severity
138
- for (const advisory of this.advisories)
144
+ for (const advisory of this.advisories) {
139
145
  this.addAdvisory(advisory)
146
+ }
140
147
 
141
148
  // remove any effects that are no longer relevant
142
149
  const vias = new Set([...this.advisories].map(a => a.dependency))
143
150
  for (const via of this.via) {
144
- if (!vias.has(via.name))
151
+ if (!vias.has(via.name)) {
145
152
  this.deleteVia(via)
153
+ }
146
154
  }
147
155
  }
148
156
 
@@ -151,8 +159,9 @@ class Vuln {
151
159
  const sev = severities.get(advisory.severity)
152
160
  this[_range] = null
153
161
  this[_simpleRange] = null
154
- if (sev > severities.get(this.severity))
162
+ if (sev > severities.get(this.severity)) {
155
163
  this.severity = advisory.severity
164
+ }
156
165
  }
157
166
 
158
167
  get range () {
@@ -161,8 +170,9 @@ class Vuln {
161
170
  }
162
171
 
163
172
  get simpleRange () {
164
- if (this[_simpleRange] && this[_simpleRange] === this[_range])
173
+ if (this[_simpleRange] && this[_simpleRange] === this[_range]) {
165
174
  return this[_simpleRange]
175
+ }
166
176
 
167
177
  const versions = [...this.advisories][0].versions
168
178
  const range = this.range
@@ -171,12 +181,14 @@ class Vuln {
171
181
  }
172
182
 
173
183
  isVulnerable (node) {
174
- if (this.nodes.has(node))
184
+ if (this.nodes.has(node)) {
175
185
  return true
186
+ }
176
187
 
177
188
  const { version } = node.package
178
- if (!version)
189
+ if (!version) {
179
190
  return false
191
+ }
180
192
 
181
193
  for (const v of this.advisories) {
182
194
  if (v.testVersion(version)) {
package/lib/yarn-lock.js CHANGED
@@ -82,13 +82,15 @@ class YarnLock {
82
82
  const linere = /([^\r\n]*)\r?\n/gm
83
83
  let match
84
84
  let lineNum = 0
85
- if (!/\n$/.test(data))
85
+ if (!/\n$/.test(data)) {
86
86
  data += '\n'
87
+ }
87
88
  while (match = linere.exec(data)) {
88
89
  const line = match[1]
89
90
  lineNum++
90
- if (line.charAt(0) === '#')
91
+ if (line.charAt(0) === '#') {
91
92
  continue
93
+ }
92
94
  if (line === '') {
93
95
  this.endCurrent()
94
96
  continue
@@ -117,8 +119,9 @@ class YarnLock {
117
119
  const metadata = this.splitQuoted(line.trimLeft(), ' ')
118
120
  if (metadata.length === 2) {
119
121
  // strip off the legacy shasum hashes
120
- if (metadata[0] === 'resolved')
122
+ if (metadata[0] === 'resolved') {
121
123
  metadata[1] = metadata[1].replace(/#.*/, '')
124
+ }
122
125
  this.current[metadata[0]] = metadata[1]
123
126
  continue
124
127
  }
@@ -141,9 +144,9 @@ class YarnLock {
141
144
  let o = 0
142
145
  for (let i = 0; i < split.length; i++) {
143
146
  const chunk = split[i]
144
- if (/^".*"$/.test(chunk))
147
+ if (/^".*"$/.test(chunk)) {
145
148
  out[o++] = chunk.trim().slice(1, -1)
146
- else if (/^"/.test(chunk)) {
149
+ } else if (/^"/.test(chunk)) {
147
150
  let collect = chunk.trimLeft().slice(1)
148
151
  while (++i < split.length) {
149
152
  const n = split[i]
@@ -152,12 +155,14 @@ class YarnLock {
152
155
  if (/[^\\](\\\\)*"$/.test(n)) {
153
156
  collect += n.trimRight().slice(0, -1)
154
157
  break
155
- } else
158
+ } else {
156
159
  collect += n
160
+ }
157
161
  }
158
162
  out[o++] = collect
159
- } else
163
+ } else {
160
164
  out[o++] = chunk.trim()
165
+ }
161
166
  }
162
167
  return out
163
168
  }
@@ -226,17 +231,19 @@ class YarnLock {
226
231
  // no previous entry for this spec at all, so it's new
227
232
  if (!prev) {
228
233
  // if we saw a match already, then assign this spec to it as well
229
- if (priorEntry)
234
+ if (priorEntry) {
230
235
  priorEntry.addSpec(s)
231
- else
236
+ } else {
232
237
  newSpecs.push(s)
238
+ }
233
239
  continue
234
240
  }
235
241
 
236
242
  const m = match(prev, n)
237
243
  // there was a prior entry, but a different thing. skip this one
238
- if (!m)
244
+ if (!m) {
239
245
  continue
246
+ }
240
247
 
241
248
  // previous matches, but first time seeing it, so already has this spec.
242
249
  // go ahead and add all the previously unseen specs, though
@@ -259,8 +266,9 @@ class YarnLock {
259
266
  // if we never found a matching prior, then this is a whole new thing
260
267
  if (!priorEntry) {
261
268
  const entry = Object.assign(new YarnLockEntry(newSpecs), n)
262
- for (const s of newSpecs)
269
+ for (const s of newSpecs) {
263
270
  this.entries.set(s, entry)
271
+ }
264
272
  } else {
265
273
  // pick up any new info that we got for this node, so that we can
266
274
  // decorate with integrity/resolved/etc.
@@ -270,12 +278,15 @@ class YarnLock {
270
278
 
271
279
  entryDataFromNode (node) {
272
280
  const n = {}
273
- if (node.package.dependencies)
281
+ if (node.package.dependencies) {
274
282
  n.dependencies = node.package.dependencies
275
- if (node.package.optionalDependencies)
283
+ }
284
+ if (node.package.optionalDependencies) {
276
285
  n.optionalDependencies = node.package.optionalDependencies
277
- if (node.version)
286
+ }
287
+ if (node.version) {
278
288
  n.version = node.version
289
+ }
279
290
  if (node.resolved) {
280
291
  n.resolved = consistentResolve(
281
292
  node.resolved,
@@ -284,8 +295,9 @@ class YarnLock {
284
295
  true
285
296
  )
286
297
  }
287
- if (node.integrity)
298
+ if (node.integrity) {
288
299
  n.integrity = node.integrity
300
+ }
289
301
 
290
302
  return n
291
303
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@npmcli/arborist",
3
- "version": "2.7.1",
3
+ "version": "2.8.3",
4
4
  "description": "Manage node_modules trees",
5
5
  "dependencies": {
6
6
  "@npmcli/installed-package-contents": "^1.0.7",
@@ -19,10 +19,10 @@
19
19
  "mkdirp": "^1.0.4",
20
20
  "mkdirp-infer-owner": "^2.0.0",
21
21
  "npm-install-checks": "^4.0.0",
22
- "npm-package-arg": "^8.1.0",
22
+ "npm-package-arg": "^8.1.5",
23
23
  "npm-pick-manifest": "^6.1.0",
24
24
  "npm-registry-fetch": "^11.0.0",
25
- "pacote": "^11.2.6",
25
+ "pacote": "^11.3.5",
26
26
  "parse-conflict-json": "^1.1.1",
27
27
  "proc-log": "^1.0.0",
28
28
  "promise-all-reject-late": "^1.0.0",
@@ -32,18 +32,13 @@
32
32
  "rimraf": "^3.0.2",
33
33
  "semver": "^7.3.5",
34
34
  "ssri": "^8.0.1",
35
- "tar": "^6.1.0",
36
35
  "treeverse": "^1.0.4",
37
36
  "walk-up-path": "^1.0.0"
38
37
  },
39
38
  "devDependencies": {
39
+ "@npmcli/lint": "^1.0.2",
40
40
  "benchmark": "^2.1.4",
41
41
  "chalk": "^4.1.0",
42
- "eslint": "^7.9.0",
43
- "eslint-plugin-import": "^2.22.0",
44
- "eslint-plugin-node": "^11.1.0",
45
- "eslint-plugin-promise": "^4.2.1",
46
- "eslint-plugin-standard": "^4.0.1",
47
42
  "minify-registry-metadata": "^2.1.0",
48
43
  "tap": "^15.0.9",
49
44
  "tcompare": "^5.0.6"
@@ -51,18 +46,19 @@
51
46
  "scripts": {
52
47
  "test": "npm run test-only --",
53
48
  "test-only": "tap",
54
- "posttest": "npm run lint",
49
+ "posttest": "npm run lint --",
55
50
  "snap": "tap",
56
- "postsnap": "npm run lint",
51
+ "postsnap": "npm run lintfix --",
57
52
  "test-proxy": "ARBORIST_TEST_PROXY=1 tap --snapshot",
58
53
  "preversion": "npm test",
59
54
  "postversion": "npm publish",
60
55
  "prepublishOnly": "git push origin --follow-tags",
61
56
  "eslint": "eslint",
62
- "lint": "npm run eslint -- \"lib/**/*.js\" \"test/arborist/*.js\" \"test/*.js\" \"bin/**/*.js\"",
57
+ "lint": "npm run npmclilint -- \"lib/**/*.*js\" \"bin/**/*.*js\" \"test/*.*js\" \"test/arborist/*.*js\"",
63
58
  "lintfix": "npm run lint -- --fix",
64
59
  "benchmark": "node scripts/benchmark.js",
65
- "benchclean": "rm -rf scripts/benchmark/*/"
60
+ "benchclean": "rm -rf scripts/benchmark/*/",
61
+ "npmclilint": "npmcli-lint"
66
62
  },
67
63
  "repository": {
68
64
  "type": "git",
package/lib/peer-set.js DELETED
@@ -1,25 +0,0 @@
1
- // when we have to dupe a set of peer dependencies deeper into the tree in
2
- // order to make room for a dep that would otherwise conflict, we use
3
- // this to get the set of all deps that have to be checked to ensure
4
- // nothing is locking them into the current location.
5
- //
6
- // this is different in its semantics from an "optional set" (ie, the nodes
7
- // that should be removed if an optional dep fails), because in this case,
8
- // we specifically intend to include deps in the peer set that have
9
- // dependants outside the set.
10
- const peerSet = node => {
11
- const set = new Set([node])
12
- for (const node of set) {
13
- for (const edge of node.edgesOut.values()) {
14
- if (edge.valid && edge.peer && edge.to)
15
- set.add(edge.to)
16
- }
17
- for (const edge of node.edgesIn) {
18
- if (edge.valid && edge.peer)
19
- set.add(edge.from)
20
- }
21
- }
22
- return set
23
- }
24
-
25
- module.exports = peerSet