@npmcli/arborist 5.0.3 → 5.0.6

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/README.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # @npmcli/arborist
2
2
 
3
+ [![npm version](https://img.shields.io/npm/v/@npmcli/arborist.svg)](https://npm.im/@npmcli/arborist)
4
+ [![license](https://img.shields.io/npm/l/@npmcli/arborist.svg)](https://npm.im/@npmcli/arborist)
5
+ [![CI - @npmcli/arborist](https://github.com/npm/cli/actions/workflows/ci-npmcli-arborist.yml/badge.svg)](https://github.com/npm/cli/actions/workflows/ci-npmcli-arborist.yml)
6
+
3
7
  Inspect and manage `node_modules` trees.
4
8
 
5
9
  ![a tree with the word ARBORIST superimposed on it](https://raw.githubusercontent.com/npm/arborist/main/docs/logo.svg?sanitize=true)
@@ -355,6 +355,21 @@ Try using the package name instead, e.g:
355
355
  })
356
356
 
357
357
  .then(tree => {
358
+ // search the virtual tree for invalid edges, if any are found add their source to
359
+ // the depsQueue so that we'll fix it later
360
+ depth({
361
+ tree,
362
+ getChildren: (node) => [...node.edgesOut.values()].map(edge => edge.to),
363
+ filter: node => node,
364
+ visit: node => {
365
+ for (const edge of node.edgesOut.values()) {
366
+ if (!edge.valid) {
367
+ this[_depsQueue].push(node)
368
+ break // no need to continue the loop after the first hit
369
+ }
370
+ }
371
+ },
372
+ })
358
373
  // null the virtual tree, because we're about to hack away at it
359
374
  // if you want another one, load another copy.
360
375
  this.idealTree = tree
@@ -743,6 +758,12 @@ This is a one-time fix-up, please be patient...
743
758
  continue
744
759
  }
745
760
 
761
+ // if the node's location isn't within node_modules then this is actually
762
+ // a link target, so skip it. the link node itself will be queued later.
763
+ if (!node.location.startsWith('node_modules')) {
764
+ continue
765
+ }
766
+
746
767
  queue.push(async () => {
747
768
  log.silly('inflate', node.location)
748
769
  const { resolved, version, path, name, location, integrity } = node
@@ -750,8 +771,7 @@ This is a one-time fix-up, please be patient...
750
771
  const useResolved = resolved && (
751
772
  !version || resolved.startsWith('file:')
752
773
  )
753
- const id = useResolved ? resolved
754
- : version || `file:${node.path}`
774
+ const id = useResolved ? resolved : version
755
775
  const spec = npa.resolve(name, id, dirname(path))
756
776
  const t = `idealTree:inflate:${location}`
757
777
  this.addTracker(t)
@@ -79,7 +79,7 @@ module.exports = cls => class VirtualLoader extends cls {
79
79
  async [loadRoot] (s) {
80
80
  const pj = this.path + '/package.json'
81
81
  const pkg = await rpj(pj).catch(() => s.data.packages['']) || {}
82
- return this[loadWorkspaces](this[loadNode]('', pkg))
82
+ return this[loadWorkspaces](this[loadNode]('', pkg, true))
83
83
  }
84
84
 
85
85
  async [loadFromShrinkwrap] (s, root) {
@@ -264,7 +264,7 @@ module.exports = cls => class VirtualLoader extends cls {
264
264
  }
265
265
  }
266
266
 
267
- [loadNode] (location, sw) {
267
+ [loadNode] (location, sw, loadOverrides) {
268
268
  const p = this.virtualTree ? this.virtualTree.realpath : this.path
269
269
  const path = resolve(p, location)
270
270
  // shrinkwrap doesn't include package name unless necessary
@@ -290,6 +290,7 @@ module.exports = cls => class VirtualLoader extends cls {
290
290
  optional,
291
291
  devOptional,
292
292
  peer,
293
+ loadOverrides,
293
294
  })
294
295
  // cast to boolean because they're undefined in the lock file when false
295
296
  node.extraneous = !!sw.extraneous
@@ -1308,7 +1308,7 @@ module.exports = cls => class Reifier extends cls {
1308
1308
  // to only names that are found in this list
1309
1309
  const retrieveUpdatedNodes = names => {
1310
1310
  const filterDirectDependencies = node =>
1311
- !node.isRoot && node.resolveParent.isRoot
1311
+ !node.isRoot && node.resolveParent && node.resolveParent.isRoot
1312
1312
  && (!names || names.includes(node.name))
1313
1313
  && exactVersion(node) // skip update for exact ranges
1314
1314
 
@@ -134,16 +134,7 @@ class AuditReport extends Map {
134
134
  const seen = new Set()
135
135
  for (const advisory of advisories) {
136
136
  const { name, range } = advisory
137
-
138
- // don't flag the exact same name/range more than once
139
- // adding multiple advisories with the same range is fine, but no
140
- // need to search for nodes we already would have added.
141
137
  const k = `${name}@${range}`
142
- if (seen.has(k)) {
143
- continue
144
- }
145
-
146
- seen.add(k)
147
138
 
148
139
  const vuln = this.get(name) || new Vuln({ name, advisory })
149
140
  if (this.has(name)) {
@@ -151,44 +142,50 @@ class AuditReport extends Map {
151
142
  }
152
143
  super.set(name, vuln)
153
144
 
154
- const p = []
155
- for (const node of this.tree.inventory.query('packageName', name)) {
156
- if (!shouldAudit(node, this[_omit], this.filterSet)) {
157
- continue
158
- }
145
+ // don't flag the exact same name/range more than once
146
+ // adding multiple advisories with the same range is fine, but no
147
+ // need to search for nodes we already would have added.
148
+ if (!seen.has(k)) {
149
+ const p = []
150
+ for (const node of this.tree.inventory.query('packageName', name)) {
151
+ if (!shouldAudit(node, this[_omit], this.filterSet)) {
152
+ continue
153
+ }
159
154
 
160
- // if not vulnerable by this advisory, keep searching
161
- if (!advisory.testVersion(node.version)) {
162
- continue
163
- }
155
+ // if not vulnerable by this advisory, keep searching
156
+ if (!advisory.testVersion(node.version)) {
157
+ continue
158
+ }
164
159
 
165
- // we will have loaded the source already if this is a metavuln
166
- if (advisory.type === 'metavuln') {
167
- vuln.addVia(this.get(advisory.dependency))
168
- }
160
+ // we will have loaded the source already if this is a metavuln
161
+ if (advisory.type === 'metavuln') {
162
+ vuln.addVia(this.get(advisory.dependency))
163
+ }
169
164
 
170
- // already marked this one, no need to do it again
171
- if (vuln.nodes.has(node)) {
172
- continue
173
- }
165
+ // already marked this one, no need to do it again
166
+ if (vuln.nodes.has(node)) {
167
+ continue
168
+ }
174
169
 
175
- // haven't marked this one yet. get its dependents.
176
- vuln.nodes.add(node)
177
- for (const { from: dep, spec } of node.edgesIn) {
178
- if (dep.isTop && !vuln.topNodes.has(dep)) {
179
- this[_checkTopNode](dep, vuln, spec)
180
- } else {
170
+ // haven't marked this one yet. get its dependents.
171
+ vuln.nodes.add(node)
172
+ for (const { from: dep, spec } of node.edgesIn) {
173
+ if (dep.isTop && !vuln.topNodes.has(dep)) {
174
+ this[_checkTopNode](dep, vuln, spec)
175
+ } else {
181
176
  // calculate a metavuln, if necessary
182
- const calc = this.calculator.calculate(dep.packageName, advisory)
183
- p.push(calc.then(meta => {
184
- if (meta.testVersion(dep.version, spec)) {
185
- advisories.add(meta)
186
- }
187
- }))
177
+ const calc = this.calculator.calculate(dep.packageName, advisory)
178
+ p.push(calc.then(meta => {
179
+ if (meta.testVersion(dep.version, spec)) {
180
+ advisories.add(meta)
181
+ }
182
+ }))
183
+ }
188
184
  }
189
185
  }
186
+ await Promise.all(p)
187
+ seen.add(k)
190
188
  }
191
- await Promise.all(p)
192
189
 
193
190
  // make sure we actually got something. if not, remove it
194
191
  // this can happen if you are loading from a lockfile created by
package/lib/edge.js CHANGED
@@ -215,6 +215,11 @@ class Edge {
215
215
 
216
216
  reload (hard = false) {
217
217
  this[_explanation] = null
218
+ if (this[_from].overrides) {
219
+ this.overrides = this[_from].overrides.getEdgeRule(this)
220
+ } else {
221
+ delete this.overrides
222
+ }
218
223
  const newTo = this[_from].resolve(this.name)
219
224
  if (newTo !== this[_to]) {
220
225
  if (this[_to]) {
package/lib/node.js CHANGED
@@ -792,6 +792,9 @@ class Node {
792
792
  target.root = root
793
793
  }
794
794
 
795
+ if (!this.overrides && this.parent && this.parent.overrides) {
796
+ this.overrides = this.parent.overrides.getNodeRule(this)
797
+ }
795
798
  // tree should always be valid upon root setter completion.
796
799
  treeCheck(this)
797
800
  treeCheck(root)
@@ -7,7 +7,7 @@ const pathSafeHash = s =>
7
7
  .update(s)
8
8
  .digest('base64')
9
9
  .replace(/[^a-zA-Z0-9]+/g, '')
10
- .substr(0, 8)
10
+ .slice(0, 8)
11
11
 
12
12
  const retirePath = from => {
13
13
  const d = dirname(from)
package/lib/shrinkwrap.js CHANGED
@@ -807,7 +807,7 @@ class Shrinkwrap {
807
807
  const pathFixed = !resolved ? null
808
808
  : !/^file:/.test(resolved) ? resolved
809
809
  // resolve onto the metadata path
810
- : `file:${resolve(this.path, resolved.substr(5))}`
810
+ : `file:${resolve(this.path, resolved.slice(5))}`
811
811
 
812
812
  // if we have one, only set the other if it matches
813
813
  // otherwise it could be for a completely different thing.
@@ -1056,7 +1056,7 @@ class Shrinkwrap {
1056
1056
  // turn absolute file: paths into relative paths from the node
1057
1057
  // this especially shows up with workspace edges when the root
1058
1058
  // node is also a workspace in the set.
1059
- const p = resolve(node.realpath, spec.substr('file:'.length))
1059
+ const p = resolve(node.realpath, spec.slice('file:'.length))
1060
1060
  set[k] = `file:${relpath(node.realpath, p)}`
1061
1061
  } else {
1062
1062
  set[k] = spec
package/lib/tracker.js CHANGED
@@ -1,10 +1,12 @@
1
1
  const _progress = Symbol('_progress')
2
2
  const _onError = Symbol('_onError')
3
+ const _setProgress = Symbol('_setProgess')
3
4
  const npmlog = require('npmlog')
4
5
 
5
6
  module.exports = cls => class Tracker extends cls {
6
7
  constructor (options = {}) {
7
8
  super(options)
9
+ this[_setProgress] = !!options.progress
8
10
  this[_progress] = new Map()
9
11
  }
10
12
 
@@ -27,7 +29,7 @@ module.exports = cls => class Tracker extends cls {
27
29
  // 1. no existing tracker, no subsection
28
30
  // Create a new tracker from npmlog
29
31
  // starts progress bar
30
- if (this[_progress].size === 0) {
32
+ if (this[_setProgress] && this[_progress].size === 0) {
31
33
  npmlog.enableProgress()
32
34
  }
33
35
 
@@ -76,7 +78,7 @@ module.exports = cls => class Tracker extends cls {
76
78
 
77
79
  // remove progress bar if all
78
80
  // trackers are finished
79
- if (this[_progress].size === 0) {
81
+ if (this[_setProgress] && this[_progress].size === 0) {
80
82
  npmlog.disableProgress()
81
83
  }
82
84
  } else if (!hasTracker && subsection === null) {
@@ -92,7 +94,9 @@ module.exports = cls => class Tracker extends cls {
92
94
  }
93
95
 
94
96
  [_onError] (msg) {
95
- npmlog.disableProgress()
97
+ if (this[_setProgress]) {
98
+ npmlog.disableProgress()
99
+ }
96
100
  throw new Error(msg)
97
101
  }
98
102
  }
@@ -16,7 +16,7 @@ module.exports = (name, tgz) => {
16
16
  // basename checking. Note that registries can
17
17
  // be mounted below the root url, so /a/b/-/x/y/foo/-/foo-1.2.3.tgz
18
18
  // is a potential option.
19
- const tfsplit = u.path.substr(1).split('/-/')
19
+ const tfsplit = u.path.slice(1).split('/-/')
20
20
  if (tfsplit.length > 1) {
21
21
  const afterTF = tfsplit.pop()
22
22
  if (afterTF === base) {
package/package.json CHANGED
@@ -1,16 +1,16 @@
1
1
  {
2
2
  "name": "@npmcli/arborist",
3
- "version": "5.0.3",
3
+ "version": "5.0.6",
4
4
  "description": "Manage node_modules trees",
5
5
  "dependencies": {
6
6
  "@isaacs/string-locale-compare": "^1.1.0",
7
7
  "@npmcli/installed-package-contents": "^1.0.7",
8
8
  "@npmcli/map-workspaces": "^2.0.0",
9
9
  "@npmcli/metavuln-calculator": "^3.0.1",
10
- "@npmcli/move-file": "^1.1.0",
10
+ "@npmcli/move-file": "^2.0.0",
11
11
  "@npmcli/name-from-folder": "^1.0.1",
12
- "@npmcli/node-gyp": "^1.0.3",
13
- "@npmcli/package-json": "^1.0.1",
12
+ "@npmcli/node-gyp": "^2.0.0",
13
+ "@npmcli/package-json": "^2.0.0",
14
14
  "@npmcli/run-script": "^3.0.0",
15
15
  "bin-links": "^3.0.0",
16
16
  "cacache": "^16.0.0",
@@ -20,7 +20,7 @@
20
20
  "mkdirp": "^1.0.4",
21
21
  "mkdirp-infer-owner": "^2.0.0",
22
22
  "nopt": "^5.0.0",
23
- "npm-install-checks": "^4.0.0",
23
+ "npm-install-checks": "^5.0.0",
24
24
  "npm-package-arg": "^9.0.0",
25
25
  "npm-pick-manifest": "^7.0.0",
26
26
  "npm-registry-fetch": "^13.0.0",
@@ -34,17 +34,18 @@
34
34
  "readdir-scoped-modules": "^1.1.0",
35
35
  "rimraf": "^3.0.2",
36
36
  "semver": "^7.3.5",
37
- "ssri": "^8.0.1",
38
- "treeverse": "^1.0.4",
37
+ "ssri": "^9.0.0",
38
+ "treeverse": "^2.0.0",
39
39
  "walk-up-path": "^1.0.0"
40
40
  },
41
41
  "devDependencies": {
42
- "@npmcli/template-oss": "^2.4.2",
42
+ "@npmcli/eslint-config": "^3.0.1",
43
+ "@npmcli/template-oss": "3.3.2",
43
44
  "benchmark": "^2.1.4",
44
45
  "chalk": "^4.1.0",
45
46
  "minify-registry-metadata": "^2.1.0",
46
47
  "nock": "^13.2.0",
47
- "tap": "^15.1.2",
48
+ "tap": "^16.0.1",
48
49
  "tcompare": "^5.0.6"
49
50
  },
50
51
  "scripts": {
@@ -57,24 +58,24 @@
57
58
  "postversion": "npm publish",
58
59
  "prepublishOnly": "git push origin --follow-tags",
59
60
  "eslint": "eslint",
60
- "lint": "eslint '**/*.js'",
61
+ "lint": "eslint \"**/*.js\"",
61
62
  "lintfix": "npm run lint -- --fix",
62
63
  "benchmark": "node scripts/benchmark.js",
63
64
  "benchclean": "rm -rf scripts/benchmark/*/",
64
65
  "npmclilint": "npmcli-lint",
65
- "postlint": "npm-template-check",
66
- "template-copy": "npm-template-copy --force"
66
+ "postlint": "template-oss-check",
67
+ "template-oss-apply": "template-oss-apply --force"
67
68
  },
68
69
  "repository": {
69
70
  "type": "git",
70
- "url": "https://github.com/npm/cli",
71
+ "url": "https://github.com/npm/cli.git",
71
72
  "directory": "workspaces/arborist"
72
73
  },
73
74
  "author": "GitHub Inc.",
74
75
  "license": "ISC",
75
76
  "files": [
76
- "bin",
77
- "lib"
77
+ "bin/",
78
+ "lib/"
78
79
  ],
79
80
  "main": "lib/index.js",
80
81
  "bin": {
@@ -96,13 +97,10 @@
96
97
  "timeout": "360"
97
98
  },
98
99
  "engines": {
99
- "node": "^12.13.0 || ^14.15.0 || >=16"
100
+ "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
100
101
  },
101
- "eslintIgnore": [
102
- "test/fixtures/",
103
- "!test/fixtures/*.js"
104
- ],
105
102
  "templateOSS": {
106
- "version": "2.9.2"
103
+ "//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.",
104
+ "version": "3.3.2"
107
105
  }
108
106
  }