@npmcli/arborist 7.3.0 → 7.4.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.
- package/lib/arborist/build-ideal-tree.js +98 -62
- package/lib/arborist/index.js +102 -14
- package/lib/arborist/load-actual.js +2 -4
- package/lib/arborist/rebuild.js +55 -84
- package/lib/arborist/reify.js +39 -24
- package/lib/case-insensitive-map.js +20 -20
- package/lib/index.js +0 -2
- package/lib/query-selector-all.js +80 -2
- package/lib/tracker.js +28 -29
- package/lib/version-from-tgz.js +4 -5
- package/lib/vuln.js +28 -31
- package/package.json +3 -3
- package/lib/arborist/audit.js +0 -51
- package/lib/arborist/deduper.js +0 -19
- package/lib/arborist/pruner.js +0 -30
- package/lib/arborist/set-workspaces.js +0 -19
- package/lib/get-workspace-nodes.js +0 -36
|
@@ -8,6 +8,7 @@ const { minimatch } = require('minimatch')
|
|
|
8
8
|
const npa = require('npm-package-arg')
|
|
9
9
|
const pacote = require('pacote')
|
|
10
10
|
const semver = require('semver')
|
|
11
|
+
const fetch = require('npm-registry-fetch')
|
|
11
12
|
|
|
12
13
|
// handle results for parsed query asts, results are stored in a map that has a
|
|
13
14
|
// key that points to each ast selector node and stores the resulting array of
|
|
@@ -18,6 +19,7 @@ class Results {
|
|
|
18
19
|
#initialItems
|
|
19
20
|
#inventory
|
|
20
21
|
#outdatedCache = new Map()
|
|
22
|
+
#vulnCache
|
|
21
23
|
#pendingCombinator
|
|
22
24
|
#results = new Map()
|
|
23
25
|
#targetNode
|
|
@@ -26,6 +28,7 @@ class Results {
|
|
|
26
28
|
this.#currentAstSelector = opts.rootAstNode.nodes[0]
|
|
27
29
|
this.#inventory = opts.inventory
|
|
28
30
|
this.#initialItems = opts.initialItems
|
|
31
|
+
this.#vulnCache = opts.vulnCache
|
|
29
32
|
this.#targetNode = opts.targetNode
|
|
30
33
|
|
|
31
34
|
this.currentResults = this.#initialItems
|
|
@@ -211,6 +214,7 @@ class Results {
|
|
|
211
214
|
inventory: this.#inventory,
|
|
212
215
|
rootAstNode: this.currentAstNode.nestedNode,
|
|
213
216
|
targetNode: item,
|
|
217
|
+
vulnCache: this.#vulnCache,
|
|
214
218
|
})
|
|
215
219
|
if (res.size > 0) {
|
|
216
220
|
found.push(item)
|
|
@@ -239,6 +243,7 @@ class Results {
|
|
|
239
243
|
inventory: this.#inventory,
|
|
240
244
|
rootAstNode: this.currentAstNode.nestedNode,
|
|
241
245
|
targetNode: this.currentAstNode,
|
|
246
|
+
vulnCache: this.#vulnCache,
|
|
242
247
|
})
|
|
243
248
|
return [...res]
|
|
244
249
|
}
|
|
@@ -266,6 +271,7 @@ class Results {
|
|
|
266
271
|
inventory: this.#inventory,
|
|
267
272
|
rootAstNode: this.currentAstNode.nestedNode,
|
|
268
273
|
targetNode: this.currentAstNode,
|
|
274
|
+
vulnCache: this.#vulnCache,
|
|
269
275
|
})
|
|
270
276
|
const internalSelector = new Set(res)
|
|
271
277
|
return this.initialItems.filter(node =>
|
|
@@ -432,6 +438,75 @@ class Results {
|
|
|
432
438
|
return this.initialItems.filter(node => node.target.edgesIn.size > 1)
|
|
433
439
|
}
|
|
434
440
|
|
|
441
|
+
async vulnPseudo () {
|
|
442
|
+
if (!this.initialItems.length) {
|
|
443
|
+
return this.initialItems
|
|
444
|
+
}
|
|
445
|
+
if (!this.#vulnCache) {
|
|
446
|
+
const packages = {}
|
|
447
|
+
// We have to map the items twice, once to get the request, and a second time to filter out the results of that request
|
|
448
|
+
this.initialItems.map((node) => {
|
|
449
|
+
if (node.isProjectRoot || node.package.private) {
|
|
450
|
+
return
|
|
451
|
+
}
|
|
452
|
+
if (!packages[node.name]) {
|
|
453
|
+
packages[node.name] = []
|
|
454
|
+
}
|
|
455
|
+
if (!packages[node.name].includes(node.version)) {
|
|
456
|
+
packages[node.name].push(node.version)
|
|
457
|
+
}
|
|
458
|
+
})
|
|
459
|
+
const res = await fetch('/-/npm/v1/security/advisories/bulk', {
|
|
460
|
+
...this.flatOptions,
|
|
461
|
+
registry: this.flatOptions.auditRegistry || this.flatOptions.registry,
|
|
462
|
+
method: 'POST',
|
|
463
|
+
gzip: true,
|
|
464
|
+
body: packages,
|
|
465
|
+
})
|
|
466
|
+
this.#vulnCache = await res.json()
|
|
467
|
+
}
|
|
468
|
+
const advisories = this.#vulnCache
|
|
469
|
+
const { vulns } = this.currentAstNode
|
|
470
|
+
return this.initialItems.filter(item => {
|
|
471
|
+
const vulnerable = advisories[item.name]?.filter(advisory => {
|
|
472
|
+
// This could be for another version of this package elsewhere in the tree
|
|
473
|
+
if (!semver.intersects(advisory.vulnerable_versions, item.version)) {
|
|
474
|
+
return false
|
|
475
|
+
}
|
|
476
|
+
if (!vulns) {
|
|
477
|
+
return true
|
|
478
|
+
}
|
|
479
|
+
// vulns are OR with each other, if any one matches we're done
|
|
480
|
+
for (const vuln of vulns) {
|
|
481
|
+
if (vuln.severity && !vuln.severity.includes('*')) {
|
|
482
|
+
if (!vuln.severity.includes(advisory.severity)) {
|
|
483
|
+
continue
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
if (vuln?.cwe) {
|
|
488
|
+
// * is special, it means "has a cwe"
|
|
489
|
+
if (vuln.cwe.includes('*')) {
|
|
490
|
+
if (!advisory.cwe.length) {
|
|
491
|
+
continue
|
|
492
|
+
}
|
|
493
|
+
} else if (!vuln.cwe.every(cwe => advisory.cwe.includes(`CWE-${cwe}`))) {
|
|
494
|
+
continue
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
return true
|
|
498
|
+
}
|
|
499
|
+
})
|
|
500
|
+
if (vulnerable?.length) {
|
|
501
|
+
item.queryContext = {
|
|
502
|
+
advisories: vulnerable,
|
|
503
|
+
}
|
|
504
|
+
return true
|
|
505
|
+
}
|
|
506
|
+
return false
|
|
507
|
+
})
|
|
508
|
+
}
|
|
509
|
+
|
|
435
510
|
async outdatedPseudo () {
|
|
436
511
|
const { outdatedKind = 'any' } = this.currentAstNode
|
|
437
512
|
|
|
@@ -445,6 +520,11 @@ class Results {
|
|
|
445
520
|
return false
|
|
446
521
|
}
|
|
447
522
|
|
|
523
|
+
// private packages can't be published, skip them
|
|
524
|
+
if (node.package.private) {
|
|
525
|
+
return false
|
|
526
|
+
}
|
|
527
|
+
|
|
448
528
|
// we cache the promise representing the full versions list, this helps reduce the
|
|
449
529
|
// number of requests we send by keeping population of the cache in a single tick
|
|
450
530
|
// making it less likely that multiple requests for the same package will be inflight
|
|
@@ -839,8 +919,6 @@ const retrieveNodesFromParsedAst = async (opts) => {
|
|
|
839
919
|
return results.collect(rootAstNode)
|
|
840
920
|
}
|
|
841
921
|
|
|
842
|
-
// We are keeping this async in the event that we do add async operators, we
|
|
843
|
-
// won't have to have a breaking change on this function signature.
|
|
844
922
|
const querySelectorAll = async (targetNode, query, flatOptions) => {
|
|
845
923
|
// This never changes ever we just pass it around. But we can't scope it to
|
|
846
924
|
// this whole file if we ever want to support concurrent calls to this
|
package/lib/tracker.js
CHANGED
|
@@ -1,47 +1,46 @@
|
|
|
1
|
-
const _progress = Symbol('_progress')
|
|
2
|
-
const _onError = Symbol('_onError')
|
|
3
|
-
const _setProgress = Symbol('_setProgess')
|
|
4
1
|
const npmlog = require('npmlog')
|
|
5
2
|
|
|
6
3
|
module.exports = cls => class Tracker extends cls {
|
|
4
|
+
#progress = new Map()
|
|
5
|
+
#setProgress
|
|
6
|
+
|
|
7
7
|
constructor (options = {}) {
|
|
8
8
|
super(options)
|
|
9
|
-
this
|
|
10
|
-
this[_progress] = new Map()
|
|
9
|
+
this.#setProgress = !!options.progress
|
|
11
10
|
}
|
|
12
11
|
|
|
13
12
|
addTracker (section, subsection = null, key = null) {
|
|
14
13
|
if (section === null || section === undefined) {
|
|
15
|
-
this
|
|
14
|
+
this.#onError(`Tracker can't be null or undefined`)
|
|
16
15
|
}
|
|
17
16
|
|
|
18
17
|
if (key === null) {
|
|
19
18
|
key = subsection
|
|
20
19
|
}
|
|
21
20
|
|
|
22
|
-
const hasTracker = this
|
|
23
|
-
const hasSubtracker = this
|
|
21
|
+
const hasTracker = this.#progress.has(section)
|
|
22
|
+
const hasSubtracker = this.#progress.has(`${section}:${key}`)
|
|
24
23
|
|
|
25
24
|
if (hasTracker && subsection === null) {
|
|
26
25
|
// 0. existing tracker, no subsection
|
|
27
|
-
this
|
|
26
|
+
this.#onError(`Tracker "${section}" already exists`)
|
|
28
27
|
} else if (!hasTracker && subsection === null) {
|
|
29
28
|
// 1. no existing tracker, no subsection
|
|
30
29
|
// Create a new tracker from npmlog
|
|
31
30
|
// starts progress bar
|
|
32
|
-
if (this
|
|
31
|
+
if (this.#setProgress && this.#progress.size === 0) {
|
|
33
32
|
npmlog.enableProgress()
|
|
34
33
|
}
|
|
35
34
|
|
|
36
|
-
this
|
|
35
|
+
this.#progress.set(section, npmlog.newGroup(section))
|
|
37
36
|
} else if (!hasTracker && subsection !== null) {
|
|
38
37
|
// 2. no parent tracker and subsection
|
|
39
|
-
this
|
|
38
|
+
this.#onError(`Parent tracker "${section}" does not exist`)
|
|
40
39
|
} else if (!hasTracker || !hasSubtracker) {
|
|
41
40
|
// 3. existing parent tracker, no subsection tracker
|
|
42
|
-
// Create a new subtracker in this
|
|
43
|
-
this
|
|
44
|
-
this
|
|
41
|
+
// Create a new subtracker in this.#progress from parent tracker
|
|
42
|
+
this.#progress.set(`${section}:${key}`,
|
|
43
|
+
this.#progress.get(section).newGroup(`${section}:${subsection}`)
|
|
45
44
|
)
|
|
46
45
|
}
|
|
47
46
|
// 4. existing parent tracker, existing subsection tracker
|
|
@@ -50,22 +49,22 @@ module.exports = cls => class Tracker extends cls {
|
|
|
50
49
|
|
|
51
50
|
finishTracker (section, subsection = null, key = null) {
|
|
52
51
|
if (section === null || section === undefined) {
|
|
53
|
-
this
|
|
52
|
+
this.#onError(`Tracker can't be null or undefined`)
|
|
54
53
|
}
|
|
55
54
|
|
|
56
55
|
if (key === null) {
|
|
57
56
|
key = subsection
|
|
58
57
|
}
|
|
59
58
|
|
|
60
|
-
const hasTracker = this
|
|
61
|
-
const hasSubtracker = this
|
|
59
|
+
const hasTracker = this.#progress.has(section)
|
|
60
|
+
const hasSubtracker = this.#progress.has(`${section}:${key}`)
|
|
62
61
|
|
|
63
62
|
// 0. parent tracker exists, no subsection
|
|
64
|
-
// Finish parent tracker and remove from this
|
|
63
|
+
// Finish parent tracker and remove from this.#progress
|
|
65
64
|
if (hasTracker && subsection === null) {
|
|
66
65
|
// check if parent tracker does
|
|
67
66
|
// not have any remaining children
|
|
68
|
-
const keys = this
|
|
67
|
+
const keys = this.#progress.keys()
|
|
69
68
|
for (const key of keys) {
|
|
70
69
|
if (key.match(new RegExp(section + ':'))) {
|
|
71
70
|
this.finishTracker(section, key)
|
|
@@ -73,28 +72,28 @@ module.exports = cls => class Tracker extends cls {
|
|
|
73
72
|
}
|
|
74
73
|
|
|
75
74
|
// remove parent tracker
|
|
76
|
-
this
|
|
77
|
-
this
|
|
75
|
+
this.#progress.get(section).finish()
|
|
76
|
+
this.#progress.delete(section)
|
|
78
77
|
|
|
79
78
|
// remove progress bar if all
|
|
80
79
|
// trackers are finished
|
|
81
|
-
if (this
|
|
80
|
+
if (this.#setProgress && this.#progress.size === 0) {
|
|
82
81
|
npmlog.disableProgress()
|
|
83
82
|
}
|
|
84
83
|
} else if (!hasTracker && subsection === null) {
|
|
85
84
|
// 1. no existing parent tracker, no subsection
|
|
86
|
-
this
|
|
85
|
+
this.#onError(`Tracker "${section}" does not exist`)
|
|
87
86
|
} else if (!hasTracker || hasSubtracker) {
|
|
88
87
|
// 2. subtracker exists
|
|
89
|
-
// Finish subtracker and remove from this
|
|
90
|
-
this
|
|
91
|
-
this
|
|
88
|
+
// Finish subtracker and remove from this.#progress
|
|
89
|
+
this.#progress.get(`${section}:${key}`).finish()
|
|
90
|
+
this.#progress.delete(`${section}:${key}`)
|
|
92
91
|
}
|
|
93
92
|
// 3. existing parent tracker, no subsection
|
|
94
93
|
}
|
|
95
94
|
|
|
96
|
-
|
|
97
|
-
if (this
|
|
95
|
+
#onError (msg) {
|
|
96
|
+
if (this.#setProgress) {
|
|
98
97
|
npmlog.disableProgress()
|
|
99
98
|
}
|
|
100
99
|
throw new Error(msg)
|
package/lib/version-from-tgz.js
CHANGED
|
@@ -1,22 +1,21 @@
|
|
|
1
|
-
/* eslint node/no-deprecated-api: "off" */
|
|
2
1
|
const semver = require('semver')
|
|
3
2
|
const { basename } = require('path')
|
|
4
|
-
const {
|
|
3
|
+
const { URL } = require('url')
|
|
5
4
|
module.exports = (name, tgz) => {
|
|
6
5
|
const base = basename(tgz)
|
|
7
6
|
if (!base.endsWith('.tgz')) {
|
|
8
7
|
return null
|
|
9
8
|
}
|
|
10
9
|
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
if (tgz.startsWith('http:/') || tgz.startsWith('https:/')) {
|
|
11
|
+
const u = new URL(tgz)
|
|
13
12
|
// registry url? check for most likely pattern.
|
|
14
13
|
// either /@foo/bar/-/bar-1.2.3.tgz or
|
|
15
14
|
// /foo/-/foo-1.2.3.tgz, and fall through to
|
|
16
15
|
// basename checking. Note that registries can
|
|
17
16
|
// be mounted below the root url, so /a/b/-/x/y/foo/-/foo-1.2.3.tgz
|
|
18
17
|
// is a potential option.
|
|
19
|
-
const tfsplit = u.
|
|
18
|
+
const tfsplit = u.pathname.slice(1).split('/-/')
|
|
20
19
|
if (tfsplit.length > 1) {
|
|
21
20
|
const afterTF = tfsplit.pop()
|
|
22
21
|
if (afterTF === base) {
|
package/lib/vuln.js
CHANGED
|
@@ -16,24 +16,23 @@ const semverOpt = { loose: true, includePrerelease: true }
|
|
|
16
16
|
|
|
17
17
|
const localeCompare = require('@isaacs/string-locale-compare')('en')
|
|
18
18
|
const npa = require('npm-package-arg')
|
|
19
|
-
const _range = Symbol('_range')
|
|
20
|
-
const _simpleRange = Symbol('_simpleRange')
|
|
21
|
-
const _fixAvailable = Symbol('_fixAvailable')
|
|
22
19
|
|
|
23
20
|
const severities = new Map([
|
|
24
|
-
['info', 0],
|
|
25
|
-
['low', 1],
|
|
26
|
-
['moderate', 2],
|
|
27
|
-
['high', 3],
|
|
28
|
-
['critical', 4],
|
|
29
|
-
[null, -1],
|
|
21
|
+
['info', 0], [0, 'info'],
|
|
22
|
+
['low', 1], [1, 'low'],
|
|
23
|
+
['moderate', 2], [2, 'moderate'],
|
|
24
|
+
['high', 3], [3, 'high'],
|
|
25
|
+
['critical', 4], [4, 'critical'],
|
|
26
|
+
[null, -1], [-1, null],
|
|
30
27
|
])
|
|
31
28
|
|
|
32
|
-
for (const [name, val] of severities.entries()) {
|
|
33
|
-
severities.set(val, name)
|
|
34
|
-
}
|
|
35
|
-
|
|
36
29
|
class Vuln {
|
|
30
|
+
#range = null
|
|
31
|
+
#simpleRange = null
|
|
32
|
+
// assume a fix is available unless it hits a top node
|
|
33
|
+
// that locks it in place, setting this false or {isSemVerMajor, version}.
|
|
34
|
+
#fixAvailable = true
|
|
35
|
+
|
|
37
36
|
constructor ({ name, advisory }) {
|
|
38
37
|
this.name = name
|
|
39
38
|
this.via = new Set()
|
|
@@ -41,23 +40,18 @@ class Vuln {
|
|
|
41
40
|
this.severity = null
|
|
42
41
|
this.effects = new Set()
|
|
43
42
|
this.topNodes = new Set()
|
|
44
|
-
this[_range] = null
|
|
45
|
-
this[_simpleRange] = null
|
|
46
43
|
this.nodes = new Set()
|
|
47
|
-
// assume a fix is available unless it hits a top node
|
|
48
|
-
// that locks it in place, setting this false or {isSemVerMajor, version}.
|
|
49
|
-
this[_fixAvailable] = true
|
|
50
44
|
this.addAdvisory(advisory)
|
|
51
45
|
this.packument = advisory.packument
|
|
52
46
|
this.versions = advisory.versions
|
|
53
47
|
}
|
|
54
48
|
|
|
55
49
|
get fixAvailable () {
|
|
56
|
-
return this
|
|
50
|
+
return this.#fixAvailable
|
|
57
51
|
}
|
|
58
52
|
|
|
59
53
|
set fixAvailable (f) {
|
|
60
|
-
this
|
|
54
|
+
this.#fixAvailable = f
|
|
61
55
|
// if there's a fix available for this at the top level, it means that
|
|
62
56
|
// it will also fix the vulns that led to it being there. to get there,
|
|
63
57
|
// we set the vias to the most "strict" of fix availables.
|
|
@@ -131,7 +125,7 @@ class Vuln {
|
|
|
131
125
|
effects: [...this.effects].map(v => v.name).sort(localeCompare),
|
|
132
126
|
range: this.simpleRange,
|
|
133
127
|
nodes: [...this.nodes].map(n => n.location).sort(localeCompare),
|
|
134
|
-
fixAvailable: this
|
|
128
|
+
fixAvailable: this.#fixAvailable,
|
|
135
129
|
}
|
|
136
130
|
}
|
|
137
131
|
|
|
@@ -151,8 +145,8 @@ class Vuln {
|
|
|
151
145
|
this.advisories.delete(advisory)
|
|
152
146
|
// make sure we have the max severity of all the vulns causing this one
|
|
153
147
|
this.severity = null
|
|
154
|
-
this
|
|
155
|
-
this
|
|
148
|
+
this.#range = null
|
|
149
|
+
this.#simpleRange = null
|
|
156
150
|
// refresh severity
|
|
157
151
|
for (const advisory of this.advisories) {
|
|
158
152
|
this.addAdvisory(advisory)
|
|
@@ -170,27 +164,30 @@ class Vuln {
|
|
|
170
164
|
addAdvisory (advisory) {
|
|
171
165
|
this.advisories.add(advisory)
|
|
172
166
|
const sev = severities.get(advisory.severity)
|
|
173
|
-
this
|
|
174
|
-
this
|
|
167
|
+
this.#range = null
|
|
168
|
+
this.#simpleRange = null
|
|
175
169
|
if (sev > severities.get(this.severity)) {
|
|
176
170
|
this.severity = advisory.severity
|
|
177
171
|
}
|
|
178
172
|
}
|
|
179
173
|
|
|
180
174
|
get range () {
|
|
181
|
-
|
|
182
|
-
|
|
175
|
+
if (!this.#range) {
|
|
176
|
+
this.#range = [...this.advisories].map(v => v.range).join(' || ')
|
|
177
|
+
}
|
|
178
|
+
return this.#range
|
|
183
179
|
}
|
|
184
180
|
|
|
185
181
|
get simpleRange () {
|
|
186
|
-
if (this
|
|
187
|
-
return this
|
|
182
|
+
if (this.#simpleRange && this.#simpleRange === this.#range) {
|
|
183
|
+
return this.#simpleRange
|
|
188
184
|
}
|
|
189
185
|
|
|
190
186
|
const versions = [...this.advisories][0].versions
|
|
191
187
|
const range = this.range
|
|
192
|
-
|
|
193
|
-
|
|
188
|
+
this.#simpleRange = simplifyRange(versions, range, semverOpt)
|
|
189
|
+
this.#range = this.#simpleRange
|
|
190
|
+
return this.#simpleRange
|
|
194
191
|
}
|
|
195
192
|
|
|
196
193
|
isVulnerable (node) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@npmcli/arborist",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.4.0",
|
|
4
4
|
"description": "Manage node_modules trees",
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"@isaacs/string-locale-compare": "^1.1.0",
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"@npmcli/name-from-folder": "^2.0.0",
|
|
12
12
|
"@npmcli/node-gyp": "^3.0.0",
|
|
13
13
|
"@npmcli/package-json": "^5.0.0",
|
|
14
|
-
"@npmcli/query": "^3.0
|
|
14
|
+
"@npmcli/query": "^3.1.0",
|
|
15
15
|
"@npmcli/run-script": "^7.0.2",
|
|
16
16
|
"bin-links": "^4.0.1",
|
|
17
17
|
"cacache": "^18.0.0",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"parse-conflict-json": "^3.0.0",
|
|
31
31
|
"proc-log": "^3.0.0",
|
|
32
32
|
"promise-all-reject-late": "^1.0.0",
|
|
33
|
-
"promise-call-limit": "^
|
|
33
|
+
"promise-call-limit": "^3.0.1",
|
|
34
34
|
"read-package-json-fast": "^3.0.2",
|
|
35
35
|
"semver": "^7.3.7",
|
|
36
36
|
"ssri": "^10.0.5",
|
package/lib/arborist/audit.js
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
// mixin implementing the audit method
|
|
2
|
-
|
|
3
|
-
const AuditReport = require('../audit-report.js')
|
|
4
|
-
|
|
5
|
-
// shared with reify
|
|
6
|
-
const _global = Symbol.for('global')
|
|
7
|
-
const _workspaces = Symbol.for('workspaces')
|
|
8
|
-
const _includeWorkspaceRoot = Symbol.for('includeWorkspaceRoot')
|
|
9
|
-
|
|
10
|
-
module.exports = cls => class Auditor extends cls {
|
|
11
|
-
async audit (options = {}) {
|
|
12
|
-
this.addTracker('audit')
|
|
13
|
-
if (this[_global]) {
|
|
14
|
-
throw Object.assign(
|
|
15
|
-
new Error('`npm audit` does not support testing globals'),
|
|
16
|
-
{ code: 'EAUDITGLOBAL' }
|
|
17
|
-
)
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
// allow the user to set options on the ctor as well.
|
|
21
|
-
// XXX: deprecate separate method options objects.
|
|
22
|
-
options = { ...this.options, ...options }
|
|
23
|
-
|
|
24
|
-
process.emit('time', 'audit')
|
|
25
|
-
let tree
|
|
26
|
-
if (options.packageLock === false) {
|
|
27
|
-
// build ideal tree
|
|
28
|
-
await this.loadActual(options)
|
|
29
|
-
await this.buildIdealTree()
|
|
30
|
-
tree = this.idealTree
|
|
31
|
-
} else {
|
|
32
|
-
tree = await this.loadVirtual()
|
|
33
|
-
}
|
|
34
|
-
if (this[_workspaces] && this[_workspaces].length) {
|
|
35
|
-
options.filterSet = this.workspaceDependencySet(
|
|
36
|
-
tree,
|
|
37
|
-
this[_workspaces],
|
|
38
|
-
this[_includeWorkspaceRoot]
|
|
39
|
-
)
|
|
40
|
-
}
|
|
41
|
-
if (!options.workspacesEnabled) {
|
|
42
|
-
options.filterSet =
|
|
43
|
-
this.excludeWorkspacesDependencySet(tree)
|
|
44
|
-
}
|
|
45
|
-
this.auditReport = await AuditReport.load(tree, options)
|
|
46
|
-
const ret = options.fix ? this.reify(options) : this.auditReport
|
|
47
|
-
process.emit('timeEnd', 'audit')
|
|
48
|
-
this.finishTracker('audit')
|
|
49
|
-
return ret
|
|
50
|
-
}
|
|
51
|
-
}
|
package/lib/arborist/deduper.js
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
module.exports = cls => class Deduper extends cls {
|
|
2
|
-
async dedupe (options = {}) {
|
|
3
|
-
// allow the user to set options on the ctor as well.
|
|
4
|
-
// XXX: deprecate separate method options objects.
|
|
5
|
-
options = { ...this.options, ...options }
|
|
6
|
-
const tree = await this.loadVirtual().catch(() => this.loadActual())
|
|
7
|
-
const names = []
|
|
8
|
-
for (const name of tree.inventory.query('name')) {
|
|
9
|
-
if (tree.inventory.query('name', name).size > 1) {
|
|
10
|
-
names.push(name)
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
return this.reify({
|
|
14
|
-
...options,
|
|
15
|
-
preferDedupe: true,
|
|
16
|
-
update: { names },
|
|
17
|
-
})
|
|
18
|
-
}
|
|
19
|
-
}
|
package/lib/arborist/pruner.js
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
const _idealTreePrune = Symbol.for('idealTreePrune')
|
|
2
|
-
const _workspacesEnabled = Symbol.for('workspacesEnabled')
|
|
3
|
-
const _addNodeToTrashList = Symbol.for('addNodeToTrashList')
|
|
4
|
-
|
|
5
|
-
module.exports = cls => class Pruner extends cls {
|
|
6
|
-
async prune (options = {}) {
|
|
7
|
-
// allow the user to set options on the ctor as well.
|
|
8
|
-
// XXX: deprecate separate method options objects.
|
|
9
|
-
options = { ...this.options, ...options }
|
|
10
|
-
|
|
11
|
-
await this.buildIdealTree(options)
|
|
12
|
-
|
|
13
|
-
this[_idealTreePrune]()
|
|
14
|
-
|
|
15
|
-
if (!this[_workspacesEnabled]) {
|
|
16
|
-
const excludeNodes = this.excludeWorkspacesDependencySet(this.idealTree)
|
|
17
|
-
for (const node of this.idealTree.inventory.values()) {
|
|
18
|
-
if (
|
|
19
|
-
node.parent !== null
|
|
20
|
-
&& !node.isProjectRoot
|
|
21
|
-
&& !excludeNodes.has(node)
|
|
22
|
-
) {
|
|
23
|
-
this[_addNodeToTrashList](node)
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
return this.reify(options)
|
|
29
|
-
}
|
|
30
|
-
}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
const mapWorkspaces = require('@npmcli/map-workspaces')
|
|
2
|
-
|
|
3
|
-
// shared ref used by other mixins/Arborist
|
|
4
|
-
const _setWorkspaces = Symbol.for('setWorkspaces')
|
|
5
|
-
|
|
6
|
-
module.exports = cls => class MapWorkspaces extends cls {
|
|
7
|
-
async [_setWorkspaces] (node) {
|
|
8
|
-
const workspaces = await mapWorkspaces({
|
|
9
|
-
cwd: node.path,
|
|
10
|
-
pkg: node.package,
|
|
11
|
-
})
|
|
12
|
-
|
|
13
|
-
if (node && workspaces.size) {
|
|
14
|
-
node.workspaces = workspaces
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
return node
|
|
18
|
-
}
|
|
19
|
-
}
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
// Get the actual nodes corresponding to a root node's child workspaces,
|
|
2
|
-
// given a list of workspace names.
|
|
3
|
-
|
|
4
|
-
const log = require('proc-log')
|
|
5
|
-
const relpath = require('./relpath.js')
|
|
6
|
-
|
|
7
|
-
const getWorkspaceNodes = (tree, workspaces) => {
|
|
8
|
-
const wsMap = tree.workspaces
|
|
9
|
-
if (!wsMap) {
|
|
10
|
-
log.warn('workspaces', 'filter set, but no workspaces present')
|
|
11
|
-
return []
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
const nodes = []
|
|
15
|
-
for (const name of workspaces) {
|
|
16
|
-
const path = wsMap.get(name)
|
|
17
|
-
if (!path) {
|
|
18
|
-
log.warn('workspaces', `${name} in filter set, but not in workspaces`)
|
|
19
|
-
continue
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const loc = relpath(tree.realpath, path)
|
|
23
|
-
const node = tree.inventory.get(loc)
|
|
24
|
-
|
|
25
|
-
if (!node) {
|
|
26
|
-
log.warn('workspaces', `${name} in filter set, but no workspace folder present`)
|
|
27
|
-
continue
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
nodes.push(node)
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
return nodes
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
module.exports = getWorkspaceNodes
|