@npmcli/arborist 2.2.7 → 2.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/bin/lib/logging.js +10 -2
- package/bin/lib/options.js +7 -1
- package/bin/lib/timers.js +6 -2
- package/bin/virtual.js +2 -1
- package/lib/add-rm-pkg-deps.js +1 -1
- package/lib/arborist/build-ideal-tree.js +118 -31
- package/lib/arborist/index.js +1 -1
- package/lib/arborist/load-actual.js +28 -2
- package/lib/arborist/load-virtual.js +9 -6
- package/lib/arborist/rebuild.js +5 -4
- package/lib/arborist/reify.js +163 -35
- package/lib/debug.js +8 -1
- package/lib/diff.js +70 -11
- package/lib/index.js +1 -0
- package/lib/link.js +13 -7
- package/lib/node.js +5 -2
- package/lib/printable.js +23 -3
- package/lib/shrinkwrap.js +14 -4
- package/lib/tree-check.js +46 -1
- package/package.json +6 -9
package/bin/lib/logging.js
CHANGED
|
@@ -20,13 +20,21 @@ const levelMap = new Map(levels.reduce((set, level, index) => {
|
|
|
20
20
|
}, []))
|
|
21
21
|
|
|
22
22
|
const { inspect, format } = require('util')
|
|
23
|
+
const colors = process.stderr.isTTY
|
|
24
|
+
const magenta = colors ? msg => `\x1B[35m${msg}\x1B[39m` : m => m
|
|
23
25
|
if (loglevel !== 'silent') {
|
|
24
26
|
process.on('log', (level, ...args) => {
|
|
25
27
|
if (levelMap.get(level) < levelMap.get(loglevel))
|
|
26
28
|
return
|
|
27
|
-
const pref = `${process.pid} ${level} `
|
|
29
|
+
const pref = `${process.pid} ${magenta(level)} `
|
|
28
30
|
if (level === 'warn' && args[0] === 'ERESOLVE')
|
|
29
|
-
args[2] = inspect(args[2], { depth: 10 })
|
|
31
|
+
args[2] = inspect(args[2], { depth: 10, colors })
|
|
32
|
+
else {
|
|
33
|
+
args = args.map(a => {
|
|
34
|
+
return typeof a === 'string' ? a
|
|
35
|
+
: inspect(a, { depth: 10, colors })
|
|
36
|
+
})
|
|
37
|
+
}
|
|
30
38
|
const msg = pref + format(...args).trim().split('\n').join(`\n${pref}`)
|
|
31
39
|
console.error(msg)
|
|
32
40
|
})
|
package/bin/lib/options.js
CHANGED
|
@@ -33,7 +33,13 @@ for (const arg of process.argv.slice(2)) {
|
|
|
33
33
|
options.omit.push(arg.substr('--omit='.length))
|
|
34
34
|
} else if (/^--before=/.test(arg))
|
|
35
35
|
options.before = new Date(arg.substr('--before='.length))
|
|
36
|
-
else if (
|
|
36
|
+
else if (/^-w.+/.test(arg)) {
|
|
37
|
+
options.workspaces = options.workspaces || []
|
|
38
|
+
options.workspaces.push(arg.replace(/^-w/, ''))
|
|
39
|
+
} else if (/^--workspace=/.test(arg)) {
|
|
40
|
+
options.workspaces = options.workspaces || []
|
|
41
|
+
options.workspaces.push(arg.replace(/^--workspace=/, ''))
|
|
42
|
+
} else if (/^--[^=]+=/.test(arg)) {
|
|
37
43
|
const [key, ...v] = arg.replace(/^--/, '').split('=')
|
|
38
44
|
const val = v.join('=')
|
|
39
45
|
options[key] = val === 'false' ? false : val === 'true' ? true : val
|
package/bin/lib/timers.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const timers = Object.create(null)
|
|
2
|
+
const { format } = require('util')
|
|
2
3
|
|
|
3
4
|
process.on('time', name => {
|
|
4
5
|
if (timers[name])
|
|
@@ -6,17 +7,20 @@ process.on('time', name => {
|
|
|
6
7
|
timers[name] = process.hrtime()
|
|
7
8
|
})
|
|
8
9
|
|
|
10
|
+
const dim = process.stderr.isTTY ? msg => `\x1B[2m${msg}\x1B[22m` : m => m
|
|
11
|
+
const red = process.stderr.isTTY ? msg => `\x1B[31m${msg}\x1B[39m` : m => m
|
|
9
12
|
process.on('timeEnd', name => {
|
|
10
13
|
if (!timers[name])
|
|
11
14
|
throw new Error('timer not started! ' + name)
|
|
12
15
|
const res = process.hrtime(timers[name])
|
|
13
16
|
delete timers[name]
|
|
14
|
-
|
|
17
|
+
const msg = format(`${process.pid} ${name}`, res[0] * 1e3 + res[1] / 1e6)
|
|
18
|
+
console.error(dim(msg))
|
|
15
19
|
})
|
|
16
20
|
|
|
17
21
|
process.on('exit', () => {
|
|
18
22
|
for (const name of Object.keys(timers)) {
|
|
19
|
-
console.error('Dangling timer:
|
|
23
|
+
console.error(red('Dangling timer:'), name)
|
|
20
24
|
process.exitCode = 1
|
|
21
25
|
}
|
|
22
26
|
})
|
package/bin/virtual.js
CHANGED
|
@@ -8,7 +8,8 @@ require('./lib/timers.js')
|
|
|
8
8
|
const start = process.hrtime()
|
|
9
9
|
new Arborist(options).loadVirtual().then(tree => {
|
|
10
10
|
const end = process.hrtime(start)
|
|
11
|
-
|
|
11
|
+
if (!options.quiet)
|
|
12
|
+
print(tree)
|
|
12
13
|
if (options.save)
|
|
13
14
|
tree.meta.save()
|
|
14
15
|
console.error(`read ${tree.inventory.size} deps in ${end[0] * 1000 + end[1] / 1e6}ms`)
|
package/lib/add-rm-pkg-deps.js
CHANGED
|
@@ -71,7 +71,7 @@ const addSingle = ({pkg, spec, saveBundle, saveType}) => {
|
|
|
71
71
|
pkg.devDependencies[name] = pkg.peerDependencies[name]
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
-
if (saveBundle) {
|
|
74
|
+
if (saveBundle && saveType !== 'peer' && saveType !== 'peerOptional') {
|
|
75
75
|
// keep it sorted, keep it unique
|
|
76
76
|
const bd = new Set(pkg.bundleDependencies || [])
|
|
77
77
|
bd.add(spec.name)
|
|
@@ -44,12 +44,14 @@ const _currentDep = Symbol('currentDep')
|
|
|
44
44
|
const _updateAll = Symbol('updateAll')
|
|
45
45
|
const _mutateTree = Symbol('mutateTree')
|
|
46
46
|
const _flagsSuspect = Symbol.for('flagsSuspect')
|
|
47
|
+
const _workspaces = Symbol.for('workspaces')
|
|
47
48
|
const _prune = Symbol('prune')
|
|
48
49
|
const _preferDedupe = Symbol('preferDedupe')
|
|
49
50
|
const _legacyBundling = Symbol('legacyBundling')
|
|
50
51
|
const _parseSettings = Symbol('parseSettings')
|
|
51
52
|
const _initTree = Symbol('initTree')
|
|
52
53
|
const _applyUserRequests = Symbol('applyUserRequests')
|
|
54
|
+
const _applyUserRequestsToNode = Symbol('applyUserRequestsToNode')
|
|
53
55
|
const _inflateAncientLockfile = Symbol('inflateAncientLockfile')
|
|
54
56
|
const _buildDeps = Symbol('buildDeps')
|
|
55
57
|
const _buildDepStep = Symbol('buildDepStep')
|
|
@@ -109,7 +111,7 @@ const _peerSetSource = Symbol.for('peerSetSource')
|
|
|
109
111
|
|
|
110
112
|
// used by Reify mixin
|
|
111
113
|
const _force = Symbol.for('force')
|
|
112
|
-
const _explicitRequests = Symbol
|
|
114
|
+
const _explicitRequests = Symbol('explicitRequests')
|
|
113
115
|
const _global = Symbol.for('global')
|
|
114
116
|
const _idealTreePrune = Symbol.for('idealTreePrune')
|
|
115
117
|
|
|
@@ -130,8 +132,10 @@ module.exports = cls => class IdealTreeBuilder extends cls {
|
|
|
130
132
|
force = false,
|
|
131
133
|
packageLock = true,
|
|
132
134
|
strictPeerDeps = false,
|
|
135
|
+
workspaces = [],
|
|
133
136
|
} = options
|
|
134
137
|
|
|
138
|
+
this[_workspaces] = workspaces || []
|
|
135
139
|
this[_force] = !!force
|
|
136
140
|
this[_strictPeerDeps] = !!strictPeerDeps
|
|
137
141
|
|
|
@@ -143,6 +147,9 @@ module.exports = cls => class IdealTreeBuilder extends cls {
|
|
|
143
147
|
this[_globalStyle] = this[_global] || globalStyle
|
|
144
148
|
this[_follow] = !!follow
|
|
145
149
|
|
|
150
|
+
if (this[_workspaces].length && this[_global])
|
|
151
|
+
throw new Error('Cannot operate on workspaces in global mode')
|
|
152
|
+
|
|
146
153
|
this[_explicitRequests] = new Set()
|
|
147
154
|
this[_preferDedupe] = false
|
|
148
155
|
this[_legacyBundling] = false
|
|
@@ -157,6 +164,7 @@ module.exports = cls => class IdealTreeBuilder extends cls {
|
|
|
157
164
|
this[_manifests] = new Map()
|
|
158
165
|
this[_peerConflict] = null
|
|
159
166
|
this[_edgesOverridden] = new Set()
|
|
167
|
+
this[_resolvedAdd] = []
|
|
160
168
|
|
|
161
169
|
// a map of each module in a peer set to the thing that depended on
|
|
162
170
|
// that set of peers in the first place. Use a WeakMap so that we
|
|
@@ -204,8 +212,8 @@ module.exports = cls => class IdealTreeBuilder extends cls {
|
|
|
204
212
|
|
|
205
213
|
try {
|
|
206
214
|
await this[_initTree]()
|
|
207
|
-
await this[_applyUserRequests](options)
|
|
208
215
|
await this[_inflateAncientLockfile]()
|
|
216
|
+
await this[_applyUserRequests](options)
|
|
209
217
|
await this[_buildDeps]()
|
|
210
218
|
await this[_fixDepFlags]()
|
|
211
219
|
await this[_pruneFailedOptional]()
|
|
@@ -266,6 +274,7 @@ module.exports = cls => class IdealTreeBuilder extends cls {
|
|
|
266
274
|
this[_preferDedupe] = !!options.preferDedupe
|
|
267
275
|
this[_legacyBundling] = !!options.legacyBundling
|
|
268
276
|
this[_updateNames] = update.names
|
|
277
|
+
|
|
269
278
|
this[_updateAll] = update.all
|
|
270
279
|
// we prune by default unless explicitly set to boolean false
|
|
271
280
|
this[_prune] = options.prune !== false
|
|
@@ -387,6 +396,42 @@ module.exports = cls => class IdealTreeBuilder extends cls {
|
|
|
387
396
|
async [_applyUserRequests] (options) {
|
|
388
397
|
process.emit('time', 'idealTree:userRequests')
|
|
389
398
|
const tree = this.idealTree.target || this.idealTree
|
|
399
|
+
|
|
400
|
+
if (!this[_workspaces].length) {
|
|
401
|
+
return this[_applyUserRequestsToNode](tree, options).then(() =>
|
|
402
|
+
process.emit('timeEnd', 'idealTree:userRequests'))
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
const wsMap = tree.workspaces
|
|
406
|
+
if (!wsMap) {
|
|
407
|
+
this.log.warn('idealTree', 'Workspace filter set, but no workspaces present')
|
|
408
|
+
return
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
const promises = []
|
|
412
|
+
for (const name of this[_workspaces]) {
|
|
413
|
+
const path = wsMap.get(name)
|
|
414
|
+
if (!path) {
|
|
415
|
+
this.log.warn('idealTree', `Workspace ${name} in filter set, but not in workspaces`)
|
|
416
|
+
continue
|
|
417
|
+
}
|
|
418
|
+
const loc = relpath(tree.realpath, path)
|
|
419
|
+
const node = tree.inventory.get(loc)
|
|
420
|
+
|
|
421
|
+
/* istanbul ignore if - should be impossible */
|
|
422
|
+
if (!node) {
|
|
423
|
+
this.log.warn('idealTree', `Workspace ${name} in filter set, but no workspace folder present`)
|
|
424
|
+
continue
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
promises.push(this[_applyUserRequestsToNode](node, options))
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
return Promise.all(promises).then(() =>
|
|
431
|
+
process.emit('timeEnd', 'idealTree:userRequests'))
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
async [_applyUserRequestsToNode] (tree, options) {
|
|
390
435
|
// If we have a list of package names to update, and we know it's
|
|
391
436
|
// going to update them wherever they are, add any paths into those
|
|
392
437
|
// named nodes to the buildIdealTree queue.
|
|
@@ -395,38 +440,49 @@ module.exports = cls => class IdealTreeBuilder extends cls {
|
|
|
395
440
|
|
|
396
441
|
// global updates only update the globalTop nodes, but we need to know
|
|
397
442
|
// that they're there, and not reinstall the world unnecessarily.
|
|
443
|
+
const globalExplicitUpdateNames = []
|
|
398
444
|
if (this[_global] && (this[_updateAll] || this[_updateNames].length)) {
|
|
399
445
|
const nm = resolve(this.path, 'node_modules')
|
|
400
446
|
for (const name of await readdir(nm).catch(() => [])) {
|
|
401
|
-
if (this[_updateNames].includes(name))
|
|
402
|
-
this[_explicitRequests].add(name)
|
|
403
447
|
tree.package.dependencies = tree.package.dependencies || {}
|
|
404
|
-
|
|
448
|
+
const updateName = this[_updateNames].includes(name)
|
|
449
|
+
if (this[_updateAll] || updateName) {
|
|
450
|
+
if (updateName)
|
|
451
|
+
globalExplicitUpdateNames.push(name)
|
|
405
452
|
tree.package.dependencies[name] = '*'
|
|
453
|
+
}
|
|
406
454
|
}
|
|
407
455
|
}
|
|
408
456
|
|
|
409
457
|
if (this.auditReport && this.auditReport.size > 0)
|
|
410
458
|
this[_queueVulnDependents](options)
|
|
411
459
|
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
460
|
+
const { add, rm } = options
|
|
461
|
+
|
|
462
|
+
if (rm && rm.length) {
|
|
463
|
+
addRmPkgDeps.rm(tree.package, rm)
|
|
464
|
+
for (const name of rm)
|
|
465
|
+
this[_explicitRequests].add({ from: tree, name, action: 'DELETE' })
|
|
416
466
|
}
|
|
417
467
|
|
|
418
|
-
if (
|
|
419
|
-
await this[_add](options)
|
|
468
|
+
if (add && add.length)
|
|
469
|
+
await this[_add](tree, options)
|
|
420
470
|
|
|
421
|
-
// triggers a refresh of all edgesOut
|
|
422
|
-
|
|
471
|
+
// triggers a refresh of all edgesOut. this has to be done BEFORE
|
|
472
|
+
// adding the edges to explicitRequests, because the package setter
|
|
473
|
+
// resets all edgesOut.
|
|
474
|
+
if (add && add.length || rm && rm.length || this[_global])
|
|
423
475
|
tree.package = tree.package
|
|
424
|
-
|
|
476
|
+
|
|
477
|
+
for (const spec of this[_resolvedAdd])
|
|
478
|
+
this[_explicitRequests].add(tree.edgesOut.get(spec.name))
|
|
479
|
+
for (const name of globalExplicitUpdateNames)
|
|
480
|
+
this[_explicitRequests].add(tree.edgesOut.get(name))
|
|
425
481
|
}
|
|
426
482
|
|
|
427
483
|
// This returns a promise because we might not have the name yet,
|
|
428
484
|
// and need to call pacote.manifest to find the name.
|
|
429
|
-
[_add] ({add, saveType = null, saveBundle = false}) {
|
|
485
|
+
[_add] (tree, {add, saveType = null, saveBundle = false}) {
|
|
430
486
|
// get the name for each of the specs in the list.
|
|
431
487
|
// ie, doing `foo@bar` we just return foo
|
|
432
488
|
// but if it's a url or git, we don't know the name until we
|
|
@@ -438,10 +494,9 @@ module.exports = cls => class IdealTreeBuilder extends cls {
|
|
|
438
494
|
.then(add => this[_updateFilePath](add))
|
|
439
495
|
.then(add => this[_followSymlinkPath](add))
|
|
440
496
|
})).then(add => {
|
|
441
|
-
this[_resolvedAdd]
|
|
497
|
+
this[_resolvedAdd].push(...add)
|
|
442
498
|
// now add is a list of spec objects with names.
|
|
443
499
|
// find a home for each of them!
|
|
444
|
-
const tree = this.idealTree.target || this.idealTree
|
|
445
500
|
addRmPkgDeps.add({
|
|
446
501
|
pkg: tree.package,
|
|
447
502
|
add,
|
|
@@ -449,8 +504,6 @@ module.exports = cls => class IdealTreeBuilder extends cls {
|
|
|
449
504
|
saveType,
|
|
450
505
|
path: this.path,
|
|
451
506
|
})
|
|
452
|
-
for (const spec of add)
|
|
453
|
-
this[_explicitRequests].add(spec.name)
|
|
454
507
|
})
|
|
455
508
|
}
|
|
456
509
|
|
|
@@ -883,6 +936,8 @@ This is a one-time fix-up, please be patient...
|
|
|
883
936
|
// create a virtual root node with the same deps as the node that
|
|
884
937
|
// is requesting this one, so that we can get all the peer deps in
|
|
885
938
|
// a context where they're likely to be resolvable.
|
|
939
|
+
// Note that the virtual root will also have virtual copies of the
|
|
940
|
+
// targets of any child Links, so that they resolve appropriately.
|
|
886
941
|
const parent = parent_ || this[_virtualRoot](edge.from)
|
|
887
942
|
const realParent = edge.peer ? edge.from.resolveParent : edge.from
|
|
888
943
|
|
|
@@ -936,11 +991,23 @@ This is a one-time fix-up, please be patient...
|
|
|
936
991
|
return this[_virtualRoots].get(node)
|
|
937
992
|
|
|
938
993
|
const vr = new Node({
|
|
939
|
-
path:
|
|
994
|
+
path: node.realpath,
|
|
940
995
|
sourceReference: node,
|
|
941
996
|
legacyPeerDeps: this.legacyPeerDeps,
|
|
942
997
|
})
|
|
943
998
|
|
|
999
|
+
// also need to set up any targets from any link deps, so that
|
|
1000
|
+
// they are properly reflected in the virtual environment
|
|
1001
|
+
for (const child of node.children.values()) {
|
|
1002
|
+
if (child.isLink) {
|
|
1003
|
+
new Node({
|
|
1004
|
+
path: child.realpath,
|
|
1005
|
+
sourceReference: child.target,
|
|
1006
|
+
root: vr,
|
|
1007
|
+
})
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
|
|
944
1011
|
this[_virtualRoots].set(node, vr)
|
|
945
1012
|
return vr
|
|
946
1013
|
}
|
|
@@ -977,7 +1044,7 @@ This is a one-time fix-up, please be patient...
|
|
|
977
1044
|
// if it's peerOptional and not explicitly requested.
|
|
978
1045
|
if (!edge.to) {
|
|
979
1046
|
return edge.type !== 'peerOptional' ||
|
|
980
|
-
this[_explicitRequests].has(edge
|
|
1047
|
+
this[_explicitRequests].has(edge)
|
|
981
1048
|
}
|
|
982
1049
|
|
|
983
1050
|
// If the edge has an error, there's a problem.
|
|
@@ -993,7 +1060,7 @@ This is a one-time fix-up, please be patient...
|
|
|
993
1060
|
return true
|
|
994
1061
|
|
|
995
1062
|
// If the user has explicitly asked to install this package, it's a problem.
|
|
996
|
-
if (node.isProjectRoot && this[_explicitRequests].has(edge
|
|
1063
|
+
if (node.isProjectRoot && this[_explicitRequests].has(edge))
|
|
997
1064
|
return true
|
|
998
1065
|
|
|
999
1066
|
// No problems!
|
|
@@ -1117,10 +1184,20 @@ This is a one-time fix-up, please be patient...
|
|
|
1117
1184
|
continue
|
|
1118
1185
|
|
|
1119
1186
|
// problem
|
|
1120
|
-
this[_failPeerConflict](edge)
|
|
1187
|
+
this[_failPeerConflict](edge, parentEdge)
|
|
1121
1188
|
}
|
|
1122
1189
|
}
|
|
1123
1190
|
|
|
1191
|
+
// There is something present already, and we're not happy about it
|
|
1192
|
+
// See if the thing we WOULD be happy with is also going to satisfy
|
|
1193
|
+
// the other dependents on the current node.
|
|
1194
|
+
const current = edge.to
|
|
1195
|
+
const dep = await this[_nodeFromEdge](edge, null, null, required)
|
|
1196
|
+
if (dep.canReplace(current)) {
|
|
1197
|
+
await this[_nodeFromEdge](edge, node.parent, null, required)
|
|
1198
|
+
continue
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1124
1201
|
// at this point we know that there is a dep there, and
|
|
1125
1202
|
// we don't like it. always fail strictly, always allow forcibly or
|
|
1126
1203
|
// in non-strict mode if it's not our fault. don't warn here, because
|
|
@@ -1133,17 +1210,17 @@ This is a one-time fix-up, please be patient...
|
|
|
1133
1210
|
continue
|
|
1134
1211
|
|
|
1135
1212
|
// ok, it's the root, or we're in unforced strict mode, so this is bad
|
|
1136
|
-
this[_failPeerConflict](edge)
|
|
1213
|
+
this[_failPeerConflict](edge, parentEdge)
|
|
1137
1214
|
}
|
|
1138
1215
|
return node
|
|
1139
1216
|
}
|
|
1140
1217
|
|
|
1141
|
-
[_failPeerConflict] (edge) {
|
|
1142
|
-
const expl = this[_explainPeerConflict](edge)
|
|
1218
|
+
[_failPeerConflict] (edge, currentEdge) {
|
|
1219
|
+
const expl = this[_explainPeerConflict](edge, currentEdge)
|
|
1143
1220
|
throw Object.assign(new Error('unable to resolve dependency tree'), expl)
|
|
1144
1221
|
}
|
|
1145
1222
|
|
|
1146
|
-
[_explainPeerConflict] (edge) {
|
|
1223
|
+
[_explainPeerConflict] (edge, currentEdge) {
|
|
1147
1224
|
const node = edge.from
|
|
1148
1225
|
const curNode = node.resolve(edge.name)
|
|
1149
1226
|
const pc = this[_peerConflict] || { peer: null, current: null }
|
|
@@ -1152,6 +1229,10 @@ This is a one-time fix-up, please be patient...
|
|
|
1152
1229
|
return {
|
|
1153
1230
|
code: 'ERESOLVE',
|
|
1154
1231
|
current,
|
|
1232
|
+
// it SHOULD be impossible to get here without a current node in place,
|
|
1233
|
+
// but this at least gives us something report on when bugs creep into
|
|
1234
|
+
// the tree handling logic.
|
|
1235
|
+
currentEdge: currentEdge ? currentEdge.explain() : null,
|
|
1155
1236
|
edge: edge.explain(),
|
|
1156
1237
|
peerConflict,
|
|
1157
1238
|
strictPeerDeps: this[_strictPeerDeps],
|
|
@@ -1176,7 +1257,7 @@ This is a one-time fix-up, please be patient...
|
|
|
1176
1257
|
[_placeDep] (dep, node, edge, peerEntryEdge = null, peerPath = []) {
|
|
1177
1258
|
if (edge.to &&
|
|
1178
1259
|
!edge.error &&
|
|
1179
|
-
!this[_explicitRequests].has(edge
|
|
1260
|
+
!this[_explicitRequests].has(edge) &&
|
|
1180
1261
|
!this[_updateNames].includes(edge.name) &&
|
|
1181
1262
|
!this[_isVulnerable](edge.to))
|
|
1182
1263
|
return []
|
|
@@ -1466,9 +1547,15 @@ This is a one-time fix-up, please be patient...
|
|
|
1466
1547
|
if (target.children.has(edge.name)) {
|
|
1467
1548
|
const current = target.children.get(edge.name)
|
|
1468
1549
|
|
|
1469
|
-
// same thing = keep
|
|
1470
|
-
if
|
|
1471
|
-
|
|
1550
|
+
// same thing = keep, UNLESS the current doesn't satisfy and new
|
|
1551
|
+
// one does satisfy. This can happen if it's a link to a matching target
|
|
1552
|
+
// at a different location, which satisfies a version dep, but not a
|
|
1553
|
+
// file: dep. If neither of them satisfy, then we can replace it,
|
|
1554
|
+
// because presumably it's better for a peer or something.
|
|
1555
|
+
if (dep.matches(current)) {
|
|
1556
|
+
if (current.satisfies(edge) || !dep.satisfies(edge))
|
|
1557
|
+
return KEEP
|
|
1558
|
+
}
|
|
1472
1559
|
|
|
1473
1560
|
const { version: curVer } = current
|
|
1474
1561
|
const { version: newVer } = dep
|
package/lib/arborist/index.js
CHANGED
|
@@ -54,7 +54,7 @@ class Arborist extends Base {
|
|
|
54
54
|
...options,
|
|
55
55
|
path: options.path || '.',
|
|
56
56
|
cache: options.cache || `${homedir()}/.npm/_cacache`,
|
|
57
|
-
packumentCache: new Map(),
|
|
57
|
+
packumentCache: options.packumentCache || new Map(),
|
|
58
58
|
log: options.log || procLog,
|
|
59
59
|
}
|
|
60
60
|
this.cache = resolve(this.options.cache)
|
|
@@ -32,6 +32,7 @@ const _loadActual = Symbol('loadActual')
|
|
|
32
32
|
const _loadActualVirtually = Symbol('loadActualVirtually')
|
|
33
33
|
const _loadActualActually = Symbol('loadActualActually')
|
|
34
34
|
const _loadWorkspaces = Symbol.for('loadWorkspaces')
|
|
35
|
+
const _loadWorkspaceTargets = Symbol('loadWorkspaceTargets')
|
|
35
36
|
const _actualTreePromise = Symbol('actualTreePromise')
|
|
36
37
|
const _actualTree = Symbol('actualTree')
|
|
37
38
|
const _transplant = Symbol('transplant')
|
|
@@ -150,18 +151,22 @@ module.exports = cls => class ActualLoader extends cls {
|
|
|
150
151
|
await new this.constructor({...this.options}).loadVirtual({
|
|
151
152
|
root: this[_actualTree],
|
|
152
153
|
})
|
|
154
|
+
await this[_loadWorkspaces](this[_actualTree])
|
|
155
|
+
if (this[_actualTree].workspaces && this[_actualTree].workspaces.size)
|
|
156
|
+
calcDepFlags(this[_actualTree], !root)
|
|
153
157
|
this[_transplant](root)
|
|
154
158
|
return this[_actualTree]
|
|
155
159
|
}
|
|
156
160
|
|
|
157
161
|
async [_loadActualActually] ({ root, ignoreMissing, global }) {
|
|
158
162
|
await this[_loadFSTree](this[_actualTree])
|
|
163
|
+
await this[_loadWorkspaces](this[_actualTree])
|
|
164
|
+
await this[_loadWorkspaceTargets](this[_actualTree])
|
|
159
165
|
if (!ignoreMissing)
|
|
160
166
|
await this[_findMissingEdges]()
|
|
161
167
|
this[_findFSParents]()
|
|
162
168
|
this[_transplant](root)
|
|
163
169
|
|
|
164
|
-
await this[_loadWorkspaces](this[_actualTree])
|
|
165
170
|
if (global) {
|
|
166
171
|
// need to depend on the children, or else all of them
|
|
167
172
|
// will end up being flagged as extraneous, since the
|
|
@@ -178,16 +183,37 @@ module.exports = cls => class ActualLoader extends cls {
|
|
|
178
183
|
return this[_actualTree]
|
|
179
184
|
}
|
|
180
185
|
|
|
186
|
+
// if there are workspace targets without Link nodes created, load
|
|
187
|
+
// the targets, so that we know what they are.
|
|
188
|
+
async [_loadWorkspaceTargets] (tree) {
|
|
189
|
+
if (!tree.workspaces || !tree.workspaces.size)
|
|
190
|
+
return
|
|
191
|
+
|
|
192
|
+
const promises = []
|
|
193
|
+
for (const path of tree.workspaces.values()) {
|
|
194
|
+
if (!this[_cache].has(path)) {
|
|
195
|
+
const p = this[_loadFSNode]({ path, root: this[_actualTree] })
|
|
196
|
+
.then(node => this[_loadFSTree](node))
|
|
197
|
+
promises.push(p)
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
await Promise.all(promises)
|
|
201
|
+
}
|
|
202
|
+
|
|
181
203
|
[_transplant] (root) {
|
|
182
204
|
if (!root || root === this[_actualTree])
|
|
183
205
|
return
|
|
206
|
+
|
|
184
207
|
this[_actualTree][_changePath](root.path)
|
|
185
208
|
for (const node of this[_actualTree].children.values()) {
|
|
186
209
|
if (!this[_transplantFilter](node))
|
|
187
|
-
node.
|
|
210
|
+
node.root = null
|
|
188
211
|
}
|
|
189
212
|
|
|
190
213
|
root.replace(this[_actualTree])
|
|
214
|
+
for (const node of this[_actualTree].fsChildren)
|
|
215
|
+
node.root = this[_transplantFilter](node) ? root : null
|
|
216
|
+
|
|
191
217
|
this[_actualTree] = root
|
|
192
218
|
}
|
|
193
219
|
|
|
@@ -93,7 +93,8 @@ module.exports = cls => class VirtualLoader extends cls {
|
|
|
93
93
|
this.virtualTree = root
|
|
94
94
|
const {links, nodes} = this[resolveNodes](s, root)
|
|
95
95
|
await this[resolveLinks](links, nodes)
|
|
96
|
-
|
|
96
|
+
if (!(s.originalLockfileVersion >= 2))
|
|
97
|
+
this[assignBundles](nodes)
|
|
97
98
|
if (this[flagsSuspect])
|
|
98
99
|
this[reCalcDepFlags](nodes.values())
|
|
99
100
|
return root
|
|
@@ -220,22 +221,24 @@ module.exports = cls => class VirtualLoader extends cls {
|
|
|
220
221
|
[assignBundles] (nodes) {
|
|
221
222
|
for (const [location, node] of nodes) {
|
|
222
223
|
// Skip assignment of parentage for the root package
|
|
223
|
-
if (!location)
|
|
224
|
+
if (!location || node.target && !node.target.location)
|
|
224
225
|
continue
|
|
225
226
|
const { name, parent, package: { inBundle }} = node
|
|
227
|
+
|
|
226
228
|
if (!parent)
|
|
227
229
|
continue
|
|
228
230
|
|
|
229
231
|
// read inBundle from package because 'package' here is
|
|
230
232
|
// actually a v2 lockfile metadata entry.
|
|
231
|
-
// If the *parent* is also bundled, though,
|
|
232
|
-
// that it's being pulled in
|
|
233
|
+
// If the *parent* is also bundled, though, or if the parent has
|
|
234
|
+
// no dependency on it, then we assume that it's being pulled in
|
|
235
|
+
// just by virtue of its parent or a transitive dep being bundled.
|
|
233
236
|
const { package: ppkg } = parent
|
|
234
237
|
const { inBundle: parentBundled } = ppkg
|
|
235
|
-
if (inBundle && !parentBundled) {
|
|
238
|
+
if (inBundle && !parentBundled && parent.edgesOut.has(node.name)) {
|
|
236
239
|
if (!ppkg.bundleDependencies)
|
|
237
240
|
ppkg.bundleDependencies = [name]
|
|
238
|
-
else
|
|
241
|
+
else
|
|
239
242
|
ppkg.bundleDependencies.push(name)
|
|
240
243
|
}
|
|
241
244
|
}
|
package/lib/arborist/rebuild.js
CHANGED
|
@@ -115,10 +115,6 @@ module.exports = cls => class Builder extends cls {
|
|
|
115
115
|
await this[_runScripts]('preinstall')
|
|
116
116
|
if (this[_binLinks] && type !== 'links')
|
|
117
117
|
await this[_linkAllBins]()
|
|
118
|
-
if (!this[_ignoreScripts]) {
|
|
119
|
-
await this[_runScripts]('install')
|
|
120
|
-
await this[_runScripts]('postinstall')
|
|
121
|
-
}
|
|
122
118
|
|
|
123
119
|
// links should also run prepare scripts and only link bins after that
|
|
124
120
|
if (type === 'links') {
|
|
@@ -128,6 +124,11 @@ module.exports = cls => class Builder extends cls {
|
|
|
128
124
|
await this[_linkAllBins]()
|
|
129
125
|
}
|
|
130
126
|
|
|
127
|
+
if (!this[_ignoreScripts]) {
|
|
128
|
+
await this[_runScripts]('install')
|
|
129
|
+
await this[_runScripts]('postinstall')
|
|
130
|
+
}
|
|
131
|
+
|
|
131
132
|
process.emit('timeEnd', `build:${type}`)
|
|
132
133
|
}
|
|
133
134
|
|