@npmcli/arborist 7.3.1 → 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 +56 -46
- package/lib/arborist/index.js +102 -14
- package/lib/arborist/load-actual.js +2 -4
- package/lib/arborist/rebuild.js +53 -82
- package/lib/arborist/reify.js +32 -20
- 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 +2 -2
- 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
|
@@ -38,22 +38,15 @@ const resetDepFlags = require('../reset-dep-flags.js')
|
|
|
38
38
|
// them with unit tests and reuse them across mixins
|
|
39
39
|
const _updateAll = Symbol.for('updateAll')
|
|
40
40
|
const _flagsSuspect = Symbol.for('flagsSuspect')
|
|
41
|
-
const _workspaces = Symbol.for('workspaces')
|
|
42
41
|
const _setWorkspaces = Symbol.for('setWorkspaces')
|
|
43
42
|
const _updateNames = Symbol.for('updateNames')
|
|
44
43
|
const _resolvedAdd = Symbol.for('resolvedAdd')
|
|
45
44
|
const _usePackageLock = Symbol.for('usePackageLock')
|
|
46
45
|
const _rpcache = Symbol.for('realpathCache')
|
|
47
46
|
const _stcache = Symbol.for('statCache')
|
|
48
|
-
const _includeWorkspaceRoot = Symbol.for('includeWorkspaceRoot')
|
|
49
|
-
|
|
50
|
-
// exposed symbol for unit testing the placeDep method directly
|
|
51
|
-
const _peerSetSource = Symbol.for('peerSetSource')
|
|
52
47
|
|
|
53
48
|
// used by Reify mixin
|
|
54
|
-
const
|
|
55
|
-
const _global = Symbol.for('global')
|
|
56
|
-
const _idealTreePrune = Symbol.for('idealTreePrune')
|
|
49
|
+
const _addNodeToTrashList = Symbol.for('addNodeToTrashList')
|
|
57
50
|
|
|
58
51
|
// Push items in, pop them sorted by depth and then path
|
|
59
52
|
// Sorts physically shallower deps up to the front of the queue, because
|
|
@@ -117,6 +110,10 @@ module.exports = cls => class IdealTreeBuilder extends cls {
|
|
|
117
110
|
#loadFailures = new Set()
|
|
118
111
|
#manifests = new Map()
|
|
119
112
|
#mutateTree = false
|
|
113
|
+
// a map of each module in a peer set to the thing that depended on
|
|
114
|
+
// that set of peers in the first place. Use a WeakMap so that we
|
|
115
|
+
// don't hold onto references for nodes that are garbage collected.
|
|
116
|
+
#peerSetSource = new WeakMap()
|
|
120
117
|
#preferDedupe = false
|
|
121
118
|
#prune
|
|
122
119
|
#strictPeerDeps
|
|
@@ -131,20 +128,16 @@ module.exports = cls => class IdealTreeBuilder extends cls {
|
|
|
131
128
|
|
|
132
129
|
const {
|
|
133
130
|
follow = false,
|
|
134
|
-
force = false,
|
|
135
|
-
global = false,
|
|
136
131
|
installStrategy = 'hoisted',
|
|
137
132
|
idealTree = null,
|
|
138
|
-
includeWorkspaceRoot = false,
|
|
139
133
|
installLinks = false,
|
|
140
134
|
legacyPeerDeps = false,
|
|
141
135
|
packageLock = true,
|
|
142
136
|
strictPeerDeps = false,
|
|
143
|
-
workspaces
|
|
137
|
+
workspaces,
|
|
138
|
+
global,
|
|
144
139
|
} = options
|
|
145
140
|
|
|
146
|
-
this[_workspaces] = workspaces || []
|
|
147
|
-
this[_force] = !!force
|
|
148
141
|
this.#strictPeerDeps = !!strictPeerDeps
|
|
149
142
|
|
|
150
143
|
this.idealTree = idealTree
|
|
@@ -152,24 +145,16 @@ module.exports = cls => class IdealTreeBuilder extends cls {
|
|
|
152
145
|
this.legacyPeerDeps = legacyPeerDeps
|
|
153
146
|
|
|
154
147
|
this[_usePackageLock] = packageLock
|
|
155
|
-
this[_global] = !!global
|
|
156
148
|
this.#installStrategy = global ? 'shallow' : installStrategy
|
|
157
149
|
this.#follow = !!follow
|
|
158
150
|
|
|
159
|
-
if (
|
|
151
|
+
if (workspaces?.length && global) {
|
|
160
152
|
throw new Error('Cannot operate on workspaces in global mode')
|
|
161
153
|
}
|
|
162
154
|
|
|
163
155
|
this[_updateAll] = false
|
|
164
156
|
this[_updateNames] = []
|
|
165
157
|
this[_resolvedAdd] = []
|
|
166
|
-
|
|
167
|
-
// a map of each module in a peer set to the thing that depended on
|
|
168
|
-
// that set of peers in the first place. Use a WeakMap so that we
|
|
169
|
-
// don't hold onto references for nodes that are garbage collected.
|
|
170
|
-
this[_peerSetSource] = new WeakMap()
|
|
171
|
-
|
|
172
|
-
this[_includeWorkspaceRoot] = includeWorkspaceRoot
|
|
173
158
|
}
|
|
174
159
|
|
|
175
160
|
get explicitRequests () {
|
|
@@ -196,7 +181,7 @@ module.exports = cls => class IdealTreeBuilder extends cls {
|
|
|
196
181
|
|
|
197
182
|
process.emit('time', 'idealTree')
|
|
198
183
|
|
|
199
|
-
if (!options.add && !options.rm && !options.update && this
|
|
184
|
+
if (!options.add && !options.rm && !options.update && this.options.global) {
|
|
200
185
|
throw new Error('global requires add, rm, or update option')
|
|
201
186
|
}
|
|
202
187
|
|
|
@@ -232,7 +217,7 @@ module.exports = cls => class IdealTreeBuilder extends cls {
|
|
|
232
217
|
for (const node of this.idealTree.inventory.values()) {
|
|
233
218
|
if (!node.optional) {
|
|
234
219
|
try {
|
|
235
|
-
checkEngine(node.package, npmVersion, nodeVersion, this
|
|
220
|
+
checkEngine(node.package, npmVersion, nodeVersion, this.options.force)
|
|
236
221
|
} catch (err) {
|
|
237
222
|
if (engineStrict) {
|
|
238
223
|
throw err
|
|
@@ -243,7 +228,7 @@ module.exports = cls => class IdealTreeBuilder extends cls {
|
|
|
243
228
|
current: err.current,
|
|
244
229
|
})
|
|
245
230
|
}
|
|
246
|
-
checkPlatform(node.package, this
|
|
231
|
+
checkPlatform(node.package, this.options.force)
|
|
247
232
|
}
|
|
248
233
|
}
|
|
249
234
|
}
|
|
@@ -295,7 +280,7 @@ module.exports = cls => class IdealTreeBuilder extends cls {
|
|
|
295
280
|
async #initTree () {
|
|
296
281
|
process.emit('time', 'idealTree:init')
|
|
297
282
|
let root
|
|
298
|
-
if (this
|
|
283
|
+
if (this.options.global) {
|
|
299
284
|
root = await this.#globalRootNode()
|
|
300
285
|
} else {
|
|
301
286
|
try {
|
|
@@ -313,7 +298,7 @@ module.exports = cls => class IdealTreeBuilder extends cls {
|
|
|
313
298
|
// When updating all, we load the shrinkwrap, but don't bother
|
|
314
299
|
// to build out the full virtual tree from it, since we'll be
|
|
315
300
|
// reconstructing it anyway.
|
|
316
|
-
.then(root => this
|
|
301
|
+
.then(root => this.options.global ? root
|
|
317
302
|
: !this[_usePackageLock] || this[_updateAll]
|
|
318
303
|
? Shrinkwrap.reset({
|
|
319
304
|
path: this.path,
|
|
@@ -329,7 +314,7 @@ module.exports = cls => class IdealTreeBuilder extends cls {
|
|
|
329
314
|
// Load on a new Arborist object, so the Nodes aren't the same,
|
|
330
315
|
// or else it'll get super confusing when we change them!
|
|
331
316
|
.then(async root => {
|
|
332
|
-
if ((!this[_updateAll] && !this
|
|
317
|
+
if ((!this[_updateAll] && !this.options.global && !root.meta.loadedFromDisk) || (this.options.global && this[_updateNames].length)) {
|
|
333
318
|
await new this.constructor(this.options).loadActual({ root })
|
|
334
319
|
const tree = root.target
|
|
335
320
|
// even though we didn't load it from a package-lock.json FILE,
|
|
@@ -408,7 +393,7 @@ module.exports = cls => class IdealTreeBuilder extends cls {
|
|
|
408
393
|
devOptional: false,
|
|
409
394
|
peer: false,
|
|
410
395
|
optional: false,
|
|
411
|
-
global: this
|
|
396
|
+
global: this.options.global,
|
|
412
397
|
installLinks: this.installLinks,
|
|
413
398
|
legacyPeerDeps: this.legacyPeerDeps,
|
|
414
399
|
loadOverrides: true,
|
|
@@ -423,7 +408,7 @@ module.exports = cls => class IdealTreeBuilder extends cls {
|
|
|
423
408
|
devOptional: false,
|
|
424
409
|
peer: false,
|
|
425
410
|
optional: false,
|
|
426
|
-
global: this
|
|
411
|
+
global: this.options.global,
|
|
427
412
|
installLinks: this.installLinks,
|
|
428
413
|
legacyPeerDeps: this.legacyPeerDeps,
|
|
429
414
|
root,
|
|
@@ -438,11 +423,11 @@ module.exports = cls => class IdealTreeBuilder extends cls {
|
|
|
438
423
|
process.emit('time', 'idealTree:userRequests')
|
|
439
424
|
const tree = this.idealTree.target
|
|
440
425
|
|
|
441
|
-
if (!this
|
|
426
|
+
if (!this.options.workspaces.length) {
|
|
442
427
|
await this.#applyUserRequestsToNode(tree, options)
|
|
443
428
|
} else {
|
|
444
|
-
const nodes = this.workspaceNodes(tree, this
|
|
445
|
-
if (this
|
|
429
|
+
const nodes = this.workspaceNodes(tree, this.options.workspaces)
|
|
430
|
+
if (this.options.includeWorkspaceRoot) {
|
|
446
431
|
nodes.push(tree)
|
|
447
432
|
}
|
|
448
433
|
const appliedRequests = nodes.map(
|
|
@@ -458,14 +443,14 @@ module.exports = cls => class IdealTreeBuilder extends cls {
|
|
|
458
443
|
// If we have a list of package names to update, and we know it's
|
|
459
444
|
// going to update them wherever they are, add any paths into those
|
|
460
445
|
// named nodes to the buildIdealTree queue.
|
|
461
|
-
if (!this
|
|
446
|
+
if (!this.options.global && this[_updateNames].length) {
|
|
462
447
|
this.#queueNamedUpdates()
|
|
463
448
|
}
|
|
464
449
|
|
|
465
450
|
// global updates only update the globalTop nodes, but we need to know
|
|
466
451
|
// that they're there, and not reinstall the world unnecessarily.
|
|
467
452
|
const globalExplicitUpdateNames = []
|
|
468
|
-
if (this
|
|
453
|
+
if (this.options.global && (this[_updateAll] || this[_updateNames].length)) {
|
|
469
454
|
const nm = resolve(this.path, 'node_modules')
|
|
470
455
|
const paths = await readdirScoped(nm).catch(() => [])
|
|
471
456
|
for (const p of paths) {
|
|
@@ -510,7 +495,7 @@ module.exports = cls => class IdealTreeBuilder extends cls {
|
|
|
510
495
|
// triggers a refresh of all edgesOut. this has to be done BEFORE
|
|
511
496
|
// adding the edges to explicitRequests, because the package setter
|
|
512
497
|
// resets all edgesOut.
|
|
513
|
-
if (add && add.length || rm && rm.length || this
|
|
498
|
+
if (add && add.length || rm && rm.length || this.options.global) {
|
|
514
499
|
tree.package = tree.package
|
|
515
500
|
}
|
|
516
501
|
|
|
@@ -616,7 +601,7 @@ module.exports = cls => class IdealTreeBuilder extends cls {
|
|
|
616
601
|
//
|
|
617
602
|
// XXX: how to handle top nodes that aren't the root? Maybe the report
|
|
618
603
|
// just tells the user to cd into that directory and fix it?
|
|
619
|
-
if (this
|
|
604
|
+
if (this.options.force && this.auditReport && this.auditReport.topVulns.size) {
|
|
620
605
|
options.add = options.add || []
|
|
621
606
|
options.rm = options.rm || []
|
|
622
607
|
const nodesTouched = new Set()
|
|
@@ -900,7 +885,7 @@ This is a one-time fix-up, please be patient...
|
|
|
900
885
|
// dep if allowed.
|
|
901
886
|
|
|
902
887
|
const tasks = []
|
|
903
|
-
const peerSource = this
|
|
888
|
+
const peerSource = this.#peerSetSource.get(node) || node
|
|
904
889
|
for (const edge of this.#problemEdges(node)) {
|
|
905
890
|
if (edge.peerConflicted) {
|
|
906
891
|
continue
|
|
@@ -958,7 +943,7 @@ This is a one-time fix-up, please be patient...
|
|
|
958
943
|
|
|
959
944
|
auditReport: this.auditReport,
|
|
960
945
|
explicitRequest: this.#explicitRequests.has(edge),
|
|
961
|
-
force: this
|
|
946
|
+
force: this.options.force,
|
|
962
947
|
installLinks: this.installLinks,
|
|
963
948
|
installStrategy: this.#installStrategy,
|
|
964
949
|
legacyPeerDeps: this.legacyPeerDeps,
|
|
@@ -1099,13 +1084,13 @@ This is a one-time fix-up, please be patient...
|
|
|
1099
1084
|
|
|
1100
1085
|
// keep track of the thing that caused this node to be included.
|
|
1101
1086
|
const src = parent.sourceReference
|
|
1102
|
-
this
|
|
1087
|
+
this.#peerSetSource.set(node, src)
|
|
1103
1088
|
|
|
1104
1089
|
// do not load the peers along with the set if this is a global top pkg
|
|
1105
1090
|
// otherwise we'll be tempted to put peers as other top-level installed
|
|
1106
1091
|
// things, potentially clobbering what's there already, which is not
|
|
1107
1092
|
// what we want. the missing edges will be picked up on the next pass.
|
|
1108
|
-
if (this
|
|
1093
|
+
if (this.options.global && edge.from.isProjectRoot) {
|
|
1109
1094
|
return node
|
|
1110
1095
|
}
|
|
1111
1096
|
|
|
@@ -1328,7 +1313,7 @@ This is a one-time fix-up, please be patient...
|
|
|
1328
1313
|
const parentEdge = node.parent.edgesOut.get(edge.name)
|
|
1329
1314
|
const { isProjectRoot, isWorkspace } = node.parent.sourceReference
|
|
1330
1315
|
const isMine = isProjectRoot || isWorkspace
|
|
1331
|
-
const conflictOK = this
|
|
1316
|
+
const conflictOK = this.options.force || !isMine && !this.#strictPeerDeps
|
|
1332
1317
|
|
|
1333
1318
|
if (!edge.to) {
|
|
1334
1319
|
if (!parentEdge) {
|
|
@@ -1415,7 +1400,7 @@ This is a one-time fix-up, please be patient...
|
|
|
1415
1400
|
currentEdge: currentEdge ? currentEdge.explain() : null,
|
|
1416
1401
|
edge: edge.explain(),
|
|
1417
1402
|
strictPeerDeps: this.#strictPeerDeps,
|
|
1418
|
-
force: this
|
|
1403
|
+
force: this.options.force,
|
|
1419
1404
|
}
|
|
1420
1405
|
}
|
|
1421
1406
|
|
|
@@ -1503,7 +1488,7 @@ This is a one-time fix-up, please be patient...
|
|
|
1503
1488
|
// otherwise, don't bother.
|
|
1504
1489
|
const needPrune = metaFromDisk && (mutateTree || flagsSuspect)
|
|
1505
1490
|
if (this.#prune && needPrune) {
|
|
1506
|
-
this
|
|
1491
|
+
this.#idealTreePrune()
|
|
1507
1492
|
for (const node of this.idealTree.inventory.values()) {
|
|
1508
1493
|
if (node.extraneous) {
|
|
1509
1494
|
node.parent = null
|
|
@@ -1514,7 +1499,7 @@ This is a one-time fix-up, please be patient...
|
|
|
1514
1499
|
process.emit('timeEnd', 'idealTree:fixDepFlags')
|
|
1515
1500
|
}
|
|
1516
1501
|
|
|
1517
|
-
|
|
1502
|
+
#idealTreePrune () {
|
|
1518
1503
|
for (const node of this.idealTree.inventory.values()) {
|
|
1519
1504
|
if (node.extraneous) {
|
|
1520
1505
|
node.parent = null
|
|
@@ -1534,4 +1519,29 @@ This is a one-time fix-up, please be patient...
|
|
|
1534
1519
|
}
|
|
1535
1520
|
}
|
|
1536
1521
|
}
|
|
1522
|
+
|
|
1523
|
+
async prune (options = {}) {
|
|
1524
|
+
// allow the user to set options on the ctor as well.
|
|
1525
|
+
// XXX: deprecate separate method options objects.
|
|
1526
|
+
options = { ...this.options, ...options }
|
|
1527
|
+
|
|
1528
|
+
await this.buildIdealTree(options)
|
|
1529
|
+
|
|
1530
|
+
this.#idealTreePrune()
|
|
1531
|
+
|
|
1532
|
+
if (!this.options.workspacesEnabled) {
|
|
1533
|
+
const excludeNodes = this.excludeWorkspacesDependencySet(this.idealTree)
|
|
1534
|
+
for (const node of this.idealTree.inventory.values()) {
|
|
1535
|
+
if (
|
|
1536
|
+
node.parent !== null
|
|
1537
|
+
&& !node.isProjectRoot
|
|
1538
|
+
&& !excludeNodes.has(node)
|
|
1539
|
+
) {
|
|
1540
|
+
this[_addNodeToTrashList](node)
|
|
1541
|
+
}
|
|
1542
|
+
}
|
|
1543
|
+
}
|
|
1544
|
+
|
|
1545
|
+
return this.reify(options)
|
|
1546
|
+
}
|
|
1537
1547
|
}
|
package/lib/arborist/index.js
CHANGED
|
@@ -29,15 +29,16 @@
|
|
|
29
29
|
const { resolve } = require('path')
|
|
30
30
|
const { homedir } = require('os')
|
|
31
31
|
const { depth } = require('treeverse')
|
|
32
|
+
const mapWorkspaces = require('@npmcli/map-workspaces')
|
|
33
|
+
const log = require('proc-log')
|
|
34
|
+
|
|
32
35
|
const { saveTypeMap } = require('../add-rm-pkg-deps.js')
|
|
36
|
+
const AuditReport = require('../audit-report.js')
|
|
37
|
+
const relpath = require('../relpath.js')
|
|
33
38
|
|
|
34
39
|
const mixins = [
|
|
35
40
|
require('../tracker.js'),
|
|
36
|
-
require('./pruner.js'),
|
|
37
|
-
require('./deduper.js'),
|
|
38
|
-
require('./audit.js'),
|
|
39
41
|
require('./build-ideal-tree.js'),
|
|
40
|
-
require('./set-workspaces.js'),
|
|
41
42
|
require('./load-actual.js'),
|
|
42
43
|
require('./load-virtual.js'),
|
|
43
44
|
require('./rebuild.js'),
|
|
@@ -45,9 +46,8 @@ const mixins = [
|
|
|
45
46
|
require('./isolated-reifier.js'),
|
|
46
47
|
]
|
|
47
48
|
|
|
48
|
-
const
|
|
49
|
+
const _setWorkspaces = Symbol.for('setWorkspaces')
|
|
49
50
|
const Base = mixins.reduce((a, b) => b(a), require('events'))
|
|
50
|
-
const getWorkspaceNodes = require('../get-workspace-nodes.js')
|
|
51
51
|
|
|
52
52
|
// if it's 1, 2, or 3, set it explicitly that.
|
|
53
53
|
// if undefined or null, set it null
|
|
@@ -72,20 +72,26 @@ class Arborist extends Base {
|
|
|
72
72
|
nodeVersion: process.version,
|
|
73
73
|
...options,
|
|
74
74
|
Arborist: this.constructor,
|
|
75
|
-
|
|
75
|
+
binLinks: 'binLinks' in options ? !!options.binLinks : true,
|
|
76
76
|
cache: options.cache || `${homedir()}/.npm/_cacache`,
|
|
77
|
+
force: !!options.force,
|
|
78
|
+
global: !!options.global,
|
|
79
|
+
ignoreScripts: !!options.ignoreScripts,
|
|
80
|
+
installStrategy: options.global ? 'shallow' : (options.installStrategy ? options.installStrategy : 'hoisted'),
|
|
81
|
+
lockfileVersion: lockfileVersion(options.lockfileVersion),
|
|
77
82
|
packumentCache: options.packumentCache || new Map(),
|
|
78
|
-
|
|
83
|
+
path: options.path || '.',
|
|
84
|
+
rebuildBundle: 'rebuildBundle' in options ? !!options.rebuildBundle : true,
|
|
79
85
|
replaceRegistryHost: options.replaceRegistryHost,
|
|
80
|
-
|
|
81
|
-
|
|
86
|
+
scriptShell: options.scriptShell,
|
|
87
|
+
workspaces: options.workspaces || [],
|
|
88
|
+
workspacesEnabled: options.workspacesEnabled !== false,
|
|
82
89
|
}
|
|
90
|
+
// TODO is this even used? If not is that a bug?
|
|
83
91
|
this.replaceRegistryHost = this.options.replaceRegistryHost =
|
|
84
92
|
(!this.options.replaceRegistryHost || this.options.replaceRegistryHost === 'npmjs') ?
|
|
85
93
|
'registry.npmjs.org' : this.options.replaceRegistryHost
|
|
86
94
|
|
|
87
|
-
this[_workspacesEnabled] = this.options.workspacesEnabled
|
|
88
|
-
|
|
89
95
|
if (options.saveType && !saveTypeMap.get(options.saveType)) {
|
|
90
96
|
throw new Error(`Invalid saveType ${options.saveType}`)
|
|
91
97
|
}
|
|
@@ -97,12 +103,40 @@ class Arborist extends Base {
|
|
|
97
103
|
// TODO: We should change these to static functions instead
|
|
98
104
|
// of methods for the next major version
|
|
99
105
|
|
|
100
|
-
//
|
|
106
|
+
// Get the actual nodes corresponding to a root node's child workspaces,
|
|
107
|
+
// given a list of workspace names.
|
|
101
108
|
workspaceNodes (tree, workspaces) {
|
|
102
|
-
|
|
109
|
+
const wsMap = tree.workspaces
|
|
110
|
+
if (!wsMap) {
|
|
111
|
+
log.warn('workspaces', 'filter set, but no workspaces present')
|
|
112
|
+
return []
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const nodes = []
|
|
116
|
+
for (const name of workspaces) {
|
|
117
|
+
const path = wsMap.get(name)
|
|
118
|
+
if (!path) {
|
|
119
|
+
log.warn('workspaces', `${name} in filter set, but not in workspaces`)
|
|
120
|
+
continue
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const loc = relpath(tree.realpath, path)
|
|
124
|
+
const node = tree.inventory.get(loc)
|
|
125
|
+
|
|
126
|
+
if (!node) {
|
|
127
|
+
log.warn('workspaces', `${name} in filter set, but no workspace folder present`)
|
|
128
|
+
continue
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
nodes.push(node)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return nodes
|
|
103
135
|
}
|
|
104
136
|
|
|
105
137
|
// returns a set of workspace nodes and all their deps
|
|
138
|
+
// TODO why is includeWorkspaceRoot a param?
|
|
139
|
+
// TODO why is workspaces a param?
|
|
106
140
|
workspaceDependencySet (tree, workspaces, includeWorkspaceRoot) {
|
|
107
141
|
const wsNodes = this.workspaceNodes(tree, workspaces)
|
|
108
142
|
if (includeWorkspaceRoot) {
|
|
@@ -162,6 +196,60 @@ class Arborist extends Base {
|
|
|
162
196
|
})
|
|
163
197
|
return rootDepSet
|
|
164
198
|
}
|
|
199
|
+
|
|
200
|
+
async [_setWorkspaces] (node) {
|
|
201
|
+
const workspaces = await mapWorkspaces({
|
|
202
|
+
cwd: node.path,
|
|
203
|
+
pkg: node.package,
|
|
204
|
+
})
|
|
205
|
+
|
|
206
|
+
if (node && workspaces.size) {
|
|
207
|
+
node.workspaces = workspaces
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
return node
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
async audit (options = {}) {
|
|
214
|
+
this.addTracker('audit')
|
|
215
|
+
if (this.options.global) {
|
|
216
|
+
throw Object.assign(
|
|
217
|
+
new Error('`npm audit` does not support testing globals'),
|
|
218
|
+
{ code: 'EAUDITGLOBAL' }
|
|
219
|
+
)
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// allow the user to set options on the ctor as well.
|
|
223
|
+
// XXX: deprecate separate method options objects.
|
|
224
|
+
options = { ...this.options, ...options }
|
|
225
|
+
|
|
226
|
+
process.emit('time', 'audit')
|
|
227
|
+
let tree
|
|
228
|
+
if (options.packageLock === false) {
|
|
229
|
+
// build ideal tree
|
|
230
|
+
await this.loadActual(options)
|
|
231
|
+
await this.buildIdealTree()
|
|
232
|
+
tree = this.idealTree
|
|
233
|
+
} else {
|
|
234
|
+
tree = await this.loadVirtual()
|
|
235
|
+
}
|
|
236
|
+
if (this.options.workspaces.length) {
|
|
237
|
+
options.filterSet = this.workspaceDependencySet(
|
|
238
|
+
tree,
|
|
239
|
+
this.options.workspaces,
|
|
240
|
+
this.options.includeWorkspaceRoot
|
|
241
|
+
)
|
|
242
|
+
}
|
|
243
|
+
if (!options.workspacesEnabled) {
|
|
244
|
+
options.filterSet =
|
|
245
|
+
this.excludeWorkspacesDependencySet(tree)
|
|
246
|
+
}
|
|
247
|
+
this.auditReport = await AuditReport.load(tree, options)
|
|
248
|
+
const ret = options.fix ? this.reify(options) : this.auditReport
|
|
249
|
+
process.emit('timeEnd', 'audit')
|
|
250
|
+
this.finishTracker('audit')
|
|
251
|
+
return ret
|
|
252
|
+
}
|
|
165
253
|
}
|
|
166
254
|
|
|
167
255
|
module.exports = Arborist
|
|
@@ -16,7 +16,6 @@ const realpath = require('../realpath.js')
|
|
|
16
16
|
|
|
17
17
|
// public symbols
|
|
18
18
|
const _changePath = Symbol.for('_changePath')
|
|
19
|
-
const _global = Symbol.for('global')
|
|
20
19
|
const _setWorkspaces = Symbol.for('setWorkspaces')
|
|
21
20
|
const _rpcache = Symbol.for('realpathCache')
|
|
22
21
|
const _stcache = Symbol.for('statCache')
|
|
@@ -45,8 +44,6 @@ module.exports = cls => class ActualLoader extends cls {
|
|
|
45
44
|
constructor (options) {
|
|
46
45
|
super(options)
|
|
47
46
|
|
|
48
|
-
this[_global] = !!options.global
|
|
49
|
-
|
|
50
47
|
// the tree of nodes on disk
|
|
51
48
|
this.actualTree = options.actualTree
|
|
52
49
|
|
|
@@ -58,6 +55,7 @@ module.exports = cls => class ActualLoader extends cls {
|
|
|
58
55
|
}
|
|
59
56
|
|
|
60
57
|
// public method
|
|
58
|
+
// TODO remove options param in next semver major
|
|
61
59
|
async loadActual (options = {}) {
|
|
62
60
|
// In the past this.actualTree was set as a promise that eventually
|
|
63
61
|
// resolved, and overwrite this.actualTree with the resolved value. This
|
|
@@ -100,7 +98,7 @@ module.exports = cls => class ActualLoader extends cls {
|
|
|
100
98
|
async #loadActual (options) {
|
|
101
99
|
// mostly realpath to throw if the root doesn't exist
|
|
102
100
|
const {
|
|
103
|
-
global
|
|
101
|
+
global,
|
|
104
102
|
filter = () => true,
|
|
105
103
|
root = null,
|
|
106
104
|
transplantFilter = () => true,
|