@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.
- package/bin/actual.js +4 -2
- package/bin/audit.js +12 -6
- package/bin/dedupe.js +49 -0
- package/bin/funding.js +4 -2
- package/bin/ideal.js +2 -1
- package/bin/lib/logging.js +4 -3
- package/bin/lib/options.js +14 -12
- package/bin/lib/timers.js +6 -3
- package/bin/license.js +9 -5
- package/bin/prune.js +6 -3
- package/bin/reify.js +6 -3
- package/bin/virtual.js +4 -2
- package/lib/add-rm-pkg-deps.js +25 -14
- package/lib/arborist/audit.js +2 -1
- package/lib/arborist/build-ideal-tree.js +246 -757
- package/lib/arborist/deduper.js +2 -1
- package/lib/arborist/index.js +8 -4
- package/lib/arborist/load-actual.js +32 -15
- package/lib/arborist/load-virtual.js +34 -18
- package/lib/arborist/load-workspaces.js +4 -2
- package/lib/arborist/rebuild.js +31 -16
- package/lib/arborist/reify.js +332 -119
- package/lib/audit-report.js +42 -22
- package/lib/calc-dep-flags.js +18 -9
- package/lib/can-place-dep.js +430 -0
- package/lib/case-insensitive-map.js +50 -0
- package/lib/consistent-resolve.js +2 -1
- package/lib/deepest-nesting-target.js +18 -0
- package/lib/dep-valid.js +8 -4
- package/lib/diff.js +74 -22
- package/lib/edge.js +29 -14
- package/lib/gather-dep-set.js +2 -1
- package/lib/inventory.js +12 -6
- package/lib/link.js +14 -9
- package/lib/node.js +269 -118
- package/lib/optional-set.js +4 -2
- package/lib/peer-entry-sets.js +77 -0
- package/lib/place-dep.js +578 -0
- package/lib/printable.js +48 -18
- package/lib/realpath.js +12 -6
- package/lib/shrinkwrap.js +168 -91
- package/lib/signal-handling.js +6 -3
- package/lib/spec-from-lock.js +7 -4
- package/lib/tracker.js +24 -18
- package/lib/tree-check.js +12 -6
- package/lib/version-from-tgz.js +4 -2
- package/lib/vuln.js +28 -16
- package/lib/yarn-lock.js +27 -15
- package/package.json +9 -13
- package/lib/peer-set.js +0 -25
|
@@ -3,14 +3,23 @@ const rpj = require('read-package-json-fast')
|
|
|
3
3
|
const npa = require('npm-package-arg')
|
|
4
4
|
const pacote = require('pacote')
|
|
5
5
|
const cacache = require('cacache')
|
|
6
|
-
const semver = require('semver')
|
|
7
6
|
const promiseCallLimit = require('promise-call-limit')
|
|
8
|
-
const getPeerSet = require('../peer-set.js')
|
|
9
7
|
const realpath = require('../../lib/realpath.js')
|
|
10
8
|
const { resolve, dirname } = require('path')
|
|
11
9
|
const { promisify } = require('util')
|
|
12
10
|
const treeCheck = require('../tree-check.js')
|
|
13
11
|
const readdir = promisify(require('readdir-scoped-modules'))
|
|
12
|
+
const fs = require('fs')
|
|
13
|
+
const lstat = promisify(fs.lstat)
|
|
14
|
+
const readlink = promisify(fs.readlink)
|
|
15
|
+
const { depth } = require('treeverse')
|
|
16
|
+
|
|
17
|
+
const {
|
|
18
|
+
OK,
|
|
19
|
+
REPLACE,
|
|
20
|
+
CONFLICT,
|
|
21
|
+
} = require('../can-place-dep.js')
|
|
22
|
+
const PlaceDep = require('../place-dep.js')
|
|
14
23
|
|
|
15
24
|
const debug = require('../debug.js')
|
|
16
25
|
const fromPath = require('../from-path.js')
|
|
@@ -19,20 +28,9 @@ const Shrinkwrap = require('../shrinkwrap.js')
|
|
|
19
28
|
const Node = require('../node.js')
|
|
20
29
|
const Link = require('../link.js')
|
|
21
30
|
const addRmPkgDeps = require('../add-rm-pkg-deps.js')
|
|
22
|
-
const gatherDepSet = require('../gather-dep-set.js')
|
|
23
31
|
const optionalSet = require('../optional-set.js')
|
|
24
32
|
const {checkEngine, checkPlatform} = require('npm-install-checks')
|
|
25
33
|
|
|
26
|
-
// enum of return values for canPlaceDep.
|
|
27
|
-
// No, this is a conflict, you may not put that package here
|
|
28
|
-
const CONFLICT = Symbol('CONFLICT')
|
|
29
|
-
// Yes, this is fine, and should not be a problem
|
|
30
|
-
const OK = Symbol('OK')
|
|
31
|
-
// No need, because the package already here is fine
|
|
32
|
-
const KEEP = Symbol('KEEP')
|
|
33
|
-
// Yes, clobber the package that is already here
|
|
34
|
-
const REPLACE = Symbol('REPLACE')
|
|
35
|
-
|
|
36
34
|
const relpath = require('../relpath.js')
|
|
37
35
|
|
|
38
36
|
// note: some of these symbols are shared so we can hit
|
|
@@ -47,7 +45,6 @@ const _flagsSuspect = Symbol.for('flagsSuspect')
|
|
|
47
45
|
const _workspaces = Symbol.for('workspaces')
|
|
48
46
|
const _prune = Symbol('prune')
|
|
49
47
|
const _preferDedupe = Symbol('preferDedupe')
|
|
50
|
-
const _pruneDedupable = Symbol('pruneDedupable')
|
|
51
48
|
const _legacyBundling = Symbol('legacyBundling')
|
|
52
49
|
const _parseSettings = Symbol('parseSettings')
|
|
53
50
|
const _initTree = Symbol('initTree')
|
|
@@ -65,10 +62,6 @@ const _loadWorkspaces = Symbol.for('loadWorkspaces')
|
|
|
65
62
|
const _linkFromSpec = Symbol('linkFromSpec')
|
|
66
63
|
const _loadPeerSet = Symbol('loadPeerSet')
|
|
67
64
|
const _updateNames = Symbol.for('updateNames')
|
|
68
|
-
const _placeDep = Symbol.for('placeDep')
|
|
69
|
-
const _canPlaceDep = Symbol.for('canPlaceDep')
|
|
70
|
-
const _canPlacePeers = Symbol('canPlacePeers')
|
|
71
|
-
const _pruneForReplacement = Symbol('pruneForReplacement')
|
|
72
65
|
const _fixDepFlags = Symbol('fixDepFlags')
|
|
73
66
|
const _resolveLinks = Symbol('resolveLinks')
|
|
74
67
|
const _rootNodeFromPackage = Symbol('rootNodeFromPackage')
|
|
@@ -100,12 +93,8 @@ const _checkPlatform = Symbol('checkPlatform')
|
|
|
100
93
|
const _virtualRoots = Symbol('virtualRoots')
|
|
101
94
|
const _virtualRoot = Symbol('virtualRoot')
|
|
102
95
|
|
|
103
|
-
// used for the ERESOLVE error to show the last peer conflict encountered
|
|
104
|
-
const _peerConflict = Symbol('peerConflict')
|
|
105
|
-
|
|
106
96
|
const _failPeerConflict = Symbol('failPeerConflict')
|
|
107
97
|
const _explainPeerConflict = Symbol('explainPeerConflict')
|
|
108
|
-
const _warnPeerConflict = Symbol('warnPeerConflict')
|
|
109
98
|
const _edgesOverridden = Symbol('edgesOverridden')
|
|
110
99
|
// exposed symbol for unit testing the placeDep method directly
|
|
111
100
|
const _peerSetSource = Symbol.for('peerSetSource')
|
|
@@ -148,8 +137,9 @@ module.exports = cls => class IdealTreeBuilder extends cls {
|
|
|
148
137
|
this[_globalStyle] = this[_global] || globalStyle
|
|
149
138
|
this[_follow] = !!follow
|
|
150
139
|
|
|
151
|
-
if (this[_workspaces].length && this[_global])
|
|
140
|
+
if (this[_workspaces].length && this[_global]) {
|
|
152
141
|
throw new Error('Cannot operate on workspaces in global mode')
|
|
142
|
+
}
|
|
153
143
|
|
|
154
144
|
this[_explicitRequests] = new Set()
|
|
155
145
|
this[_preferDedupe] = false
|
|
@@ -163,7 +153,6 @@ module.exports = cls => class IdealTreeBuilder extends cls {
|
|
|
163
153
|
this[_loadFailures] = new Set()
|
|
164
154
|
this[_linkNodes] = new Set()
|
|
165
155
|
this[_manifests] = new Map()
|
|
166
|
-
this[_peerConflict] = null
|
|
167
156
|
this[_edgesOverridden] = new Set()
|
|
168
157
|
this[_resolvedAdd] = []
|
|
169
158
|
|
|
@@ -180,18 +169,21 @@ module.exports = cls => class IdealTreeBuilder extends cls {
|
|
|
180
169
|
|
|
181
170
|
// public method
|
|
182
171
|
async buildIdealTree (options = {}) {
|
|
183
|
-
if (this.idealTree)
|
|
172
|
+
if (this.idealTree) {
|
|
184
173
|
return Promise.resolve(this.idealTree)
|
|
174
|
+
}
|
|
185
175
|
|
|
186
176
|
// allow the user to set reify options on the ctor as well.
|
|
187
177
|
// XXX: deprecate separate reify() options object.
|
|
188
178
|
options = { ...this.options, ...options }
|
|
189
179
|
|
|
190
180
|
// an empty array or any falsey value is the same as null
|
|
191
|
-
if (!options.add || options.add.length === 0)
|
|
181
|
+
if (!options.add || options.add.length === 0) {
|
|
192
182
|
options.add = null
|
|
193
|
-
|
|
183
|
+
}
|
|
184
|
+
if (!options.rm || options.rm.length === 0) {
|
|
194
185
|
options.rm = null
|
|
186
|
+
}
|
|
195
187
|
|
|
196
188
|
process.emit('time', 'idealTree')
|
|
197
189
|
|
|
@@ -227,17 +219,13 @@ module.exports = cls => class IdealTreeBuilder extends cls {
|
|
|
227
219
|
return treeCheck(this.idealTree)
|
|
228
220
|
}
|
|
229
221
|
|
|
230
|
-
[_checkEngineAndPlatform] () {
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
this[_checkPlatform](node)
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
})
|
|
222
|
+
async [_checkEngineAndPlatform] () {
|
|
223
|
+
for (const node of this.idealTree.inventory.values()) {
|
|
224
|
+
if (!node.optional) {
|
|
225
|
+
this[_checkEngine](node)
|
|
226
|
+
this[_checkPlatform](node)
|
|
227
|
+
}
|
|
228
|
+
}
|
|
241
229
|
}
|
|
242
230
|
|
|
243
231
|
[_checkPlatform] (node) {
|
|
@@ -246,11 +234,12 @@ module.exports = cls => class IdealTreeBuilder extends cls {
|
|
|
246
234
|
|
|
247
235
|
[_checkEngine] (node) {
|
|
248
236
|
const { engineStrict, npmVersion, nodeVersion } = this.options
|
|
249
|
-
const c = () =>
|
|
237
|
+
const c = () =>
|
|
238
|
+
checkEngine(node.package, npmVersion, nodeVersion, this[_force])
|
|
250
239
|
|
|
251
|
-
if (engineStrict)
|
|
240
|
+
if (engineStrict) {
|
|
252
241
|
c()
|
|
253
|
-
else {
|
|
242
|
+
} else {
|
|
254
243
|
try {
|
|
255
244
|
c()
|
|
256
245
|
} catch (er) {
|
|
@@ -268,8 +257,9 @@ module.exports = cls => class IdealTreeBuilder extends cls {
|
|
|
268
257
|
: Array.isArray(options.update) ? { names: options.update }
|
|
269
258
|
: options.update || {}
|
|
270
259
|
|
|
271
|
-
if (update.all || !Array.isArray(update.names))
|
|
260
|
+
if (update.all || !Array.isArray(update.names)) {
|
|
272
261
|
update.names = []
|
|
262
|
+
}
|
|
273
263
|
|
|
274
264
|
this[_complete] = !!options.complete
|
|
275
265
|
this[_preferDedupe] = !!options.preferDedupe
|
|
@@ -299,8 +289,9 @@ module.exports = cls => class IdealTreeBuilder extends cls {
|
|
|
299
289
|
: rpj(this.path + '/package.json').then(
|
|
300
290
|
pkg => this[_rootNodeFromPackage](pkg),
|
|
301
291
|
er => {
|
|
302
|
-
if (er.code === 'EJSONPARSE')
|
|
292
|
+
if (er.code === 'EJSONPARSE') {
|
|
303
293
|
throw er
|
|
294
|
+
}
|
|
304
295
|
return this[_rootNodeFromPackage]({})
|
|
305
296
|
}
|
|
306
297
|
))
|
|
@@ -328,8 +319,9 @@ module.exports = cls => class IdealTreeBuilder extends cls {
|
|
|
328
319
|
// even though we didn't load it from a package-lock.json FILE,
|
|
329
320
|
// we still loaded it "from disk", meaning we have to reset
|
|
330
321
|
// dep flags before assuming that any mutations were reflected.
|
|
331
|
-
if (tree.children.size)
|
|
322
|
+
if (tree.children.size) {
|
|
332
323
|
root.meta.loadedFromDisk = true
|
|
324
|
+
}
|
|
333
325
|
}
|
|
334
326
|
return root
|
|
335
327
|
})
|
|
@@ -398,9 +390,9 @@ module.exports = cls => class IdealTreeBuilder extends cls {
|
|
|
398
390
|
process.emit('time', 'idealTree:userRequests')
|
|
399
391
|
const tree = this.idealTree.target
|
|
400
392
|
|
|
401
|
-
if (!this[_workspaces].length)
|
|
393
|
+
if (!this[_workspaces].length) {
|
|
402
394
|
await this[_applyUserRequestsToNode](tree, options)
|
|
403
|
-
else {
|
|
395
|
+
} else {
|
|
404
396
|
await Promise.all(this.workspaceNodes(tree, this[_workspaces])
|
|
405
397
|
.map(node => this[_applyUserRequestsToNode](node, options)))
|
|
406
398
|
}
|
|
@@ -412,8 +404,9 @@ module.exports = cls => class IdealTreeBuilder extends cls {
|
|
|
412
404
|
// If we have a list of package names to update, and we know it's
|
|
413
405
|
// going to update them wherever they are, add any paths into those
|
|
414
406
|
// named nodes to the buildIdealTree queue.
|
|
415
|
-
if (!this[_global] && this[_updateNames].length)
|
|
407
|
+
if (!this[_global] && this[_updateNames].length) {
|
|
416
408
|
this[_queueNamedUpdates]()
|
|
409
|
+
}
|
|
417
410
|
|
|
418
411
|
// global updates only update the globalTop nodes, but we need to know
|
|
419
412
|
// that they're there, and not reinstall the world unnecessarily.
|
|
@@ -424,39 +417,55 @@ module.exports = cls => class IdealTreeBuilder extends cls {
|
|
|
424
417
|
tree.package.dependencies = tree.package.dependencies || {}
|
|
425
418
|
const updateName = this[_updateNames].includes(name)
|
|
426
419
|
if (this[_updateAll] || updateName) {
|
|
427
|
-
if (updateName)
|
|
420
|
+
if (updateName) {
|
|
428
421
|
globalExplicitUpdateNames.push(name)
|
|
429
|
-
|
|
422
|
+
}
|
|
423
|
+
const dir = resolve(nm, name)
|
|
424
|
+
const st = await lstat(dir)
|
|
425
|
+
.catch(/* istanbul ignore next */ er => null)
|
|
426
|
+
if (st && st.isSymbolicLink()) {
|
|
427
|
+
const target = await readlink(dir)
|
|
428
|
+
const real = resolve(dirname(dir), target)
|
|
429
|
+
tree.package.dependencies[name] = `file:${real}`
|
|
430
|
+
} else {
|
|
431
|
+
tree.package.dependencies[name] = '*'
|
|
432
|
+
}
|
|
430
433
|
}
|
|
431
434
|
}
|
|
432
435
|
}
|
|
433
436
|
|
|
434
|
-
if (this.auditReport && this.auditReport.size > 0)
|
|
437
|
+
if (this.auditReport && this.auditReport.size > 0) {
|
|
435
438
|
await this[_queueVulnDependents](options)
|
|
439
|
+
}
|
|
436
440
|
|
|
437
441
|
const { add, rm } = options
|
|
438
442
|
|
|
439
443
|
if (rm && rm.length) {
|
|
440
444
|
addRmPkgDeps.rm(tree.package, rm)
|
|
441
|
-
for (const name of rm)
|
|
445
|
+
for (const name of rm) {
|
|
442
446
|
this[_explicitRequests].add({ from: tree, name, action: 'DELETE' })
|
|
447
|
+
}
|
|
443
448
|
}
|
|
444
449
|
|
|
445
|
-
if (add && add.length)
|
|
450
|
+
if (add && add.length) {
|
|
446
451
|
await this[_add](tree, options)
|
|
452
|
+
}
|
|
447
453
|
|
|
448
454
|
// triggers a refresh of all edgesOut. this has to be done BEFORE
|
|
449
455
|
// adding the edges to explicitRequests, because the package setter
|
|
450
456
|
// resets all edgesOut.
|
|
451
|
-
if (add && add.length || rm && rm.length || this[_global])
|
|
457
|
+
if (add && add.length || rm && rm.length || this[_global]) {
|
|
452
458
|
tree.package = tree.package
|
|
459
|
+
}
|
|
453
460
|
|
|
454
461
|
for (const spec of this[_resolvedAdd]) {
|
|
455
|
-
if (spec.tree === tree)
|
|
462
|
+
if (spec.tree === tree) {
|
|
456
463
|
this[_explicitRequests].add(tree.edgesOut.get(spec.name))
|
|
464
|
+
}
|
|
457
465
|
}
|
|
458
|
-
for (const name of globalExplicitUpdateNames)
|
|
466
|
+
for (const name of globalExplicitUpdateNames) {
|
|
459
467
|
this[_explicitRequests].add(tree.edgesOut.get(name))
|
|
468
|
+
}
|
|
460
469
|
|
|
461
470
|
this[_depsQueue].push(tree)
|
|
462
471
|
}
|
|
@@ -496,21 +505,24 @@ module.exports = cls => class IdealTreeBuilder extends cls {
|
|
|
496
505
|
// if it's an explicit tag, we need to install that specific tag version
|
|
497
506
|
const isTag = spec.rawSpec && spec.type === 'tag'
|
|
498
507
|
|
|
499
|
-
if (spec.name && !isTag)
|
|
508
|
+
if (spec.name && !isTag) {
|
|
500
509
|
return spec
|
|
510
|
+
}
|
|
501
511
|
|
|
502
512
|
const mani = await pacote.manifest(spec, { ...this.options })
|
|
503
513
|
// if it's a tag type, then we need to run it down to an actual version
|
|
504
|
-
if (isTag)
|
|
514
|
+
if (isTag) {
|
|
505
515
|
return npa(`${mani.name}@${mani.version}`)
|
|
516
|
+
}
|
|
506
517
|
|
|
507
518
|
spec.name = mani.name
|
|
508
519
|
return spec
|
|
509
520
|
}
|
|
510
521
|
|
|
511
522
|
async [_updateFilePath] (spec) {
|
|
512
|
-
if (spec.type === 'file')
|
|
523
|
+
if (spec.type === 'file') {
|
|
513
524
|
return this[_getRelpathSpec](spec, spec.fetchSpec)
|
|
525
|
+
}
|
|
514
526
|
|
|
515
527
|
return spec
|
|
516
528
|
}
|
|
@@ -610,8 +622,9 @@ module.exports = cls => class IdealTreeBuilder extends cls {
|
|
|
610
622
|
nodesTouched.add(node)
|
|
611
623
|
}
|
|
612
624
|
}
|
|
613
|
-
for (const node of nodesTouched)
|
|
625
|
+
for (const node of nodesTouched) {
|
|
614
626
|
node.package = node.package
|
|
627
|
+
}
|
|
615
628
|
}
|
|
616
629
|
}
|
|
617
630
|
|
|
@@ -620,11 +633,13 @@ module.exports = cls => class IdealTreeBuilder extends cls {
|
|
|
620
633
|
}
|
|
621
634
|
|
|
622
635
|
[_avoidRange] (name) {
|
|
623
|
-
if (!this.auditReport)
|
|
636
|
+
if (!this.auditReport) {
|
|
624
637
|
return null
|
|
638
|
+
}
|
|
625
639
|
const vuln = this.auditReport.get(name)
|
|
626
|
-
if (!vuln)
|
|
640
|
+
if (!vuln) {
|
|
627
641
|
return null
|
|
642
|
+
}
|
|
628
643
|
return vuln.range
|
|
629
644
|
}
|
|
630
645
|
|
|
@@ -661,8 +676,9 @@ module.exports = cls => class IdealTreeBuilder extends cls {
|
|
|
661
676
|
const ancient = meta.ancientLockfile
|
|
662
677
|
const old = meta.loadedFromDisk && !(meta.originalLockfileVersion >= 2)
|
|
663
678
|
|
|
664
|
-
if (inventory.size === 0 || !ancient && !old)
|
|
679
|
+
if (inventory.size === 0 || !ancient && !old) {
|
|
665
680
|
return
|
|
681
|
+
}
|
|
666
682
|
|
|
667
683
|
// if the lockfile is from node v5 or earlier, then we'll have to reload
|
|
668
684
|
// all the manifests of everything we encounter. this is costly, but at
|
|
@@ -681,8 +697,9 @@ This is a one-time fix-up, please be patient...
|
|
|
681
697
|
this.addTracker('idealTree:inflate')
|
|
682
698
|
const queue = []
|
|
683
699
|
for (const node of inventory.values()) {
|
|
684
|
-
if (node.isProjectRoot)
|
|
700
|
+
if (node.isProjectRoot) {
|
|
685
701
|
continue
|
|
702
|
+
}
|
|
686
703
|
|
|
687
704
|
queue.push(async () => {
|
|
688
705
|
this.log.silly('inflate', node.location)
|
|
@@ -747,8 +764,9 @@ This is a one-time fix-up, please be patient...
|
|
|
747
764
|
this[_currentDep] = null
|
|
748
765
|
}
|
|
749
766
|
|
|
750
|
-
if (!this[_depsQueue].length)
|
|
767
|
+
if (!this[_depsQueue].length) {
|
|
751
768
|
return this[_resolveLinks]()
|
|
769
|
+
}
|
|
752
770
|
|
|
753
771
|
// sort physically shallower deps up to the front of the queue,
|
|
754
772
|
// because they'll affect things deeper in, then alphabetical
|
|
@@ -766,8 +784,9 @@ This is a one-time fix-up, please be patient...
|
|
|
766
784
|
// satisfied by whatever's in that file anyway.
|
|
767
785
|
if (this[_depsSeen].has(node) ||
|
|
768
786
|
node.root !== this.idealTree ||
|
|
769
|
-
hasShrinkwrap && !this[_complete])
|
|
787
|
+
hasShrinkwrap && !this[_complete]) {
|
|
770
788
|
return this[_buildDepStep]()
|
|
789
|
+
}
|
|
771
790
|
|
|
772
791
|
this[_depsSeen].add(node)
|
|
773
792
|
this[_currentDep] = node
|
|
@@ -850,8 +869,9 @@ This is a one-time fix-up, please be patient...
|
|
|
850
869
|
const tasks = []
|
|
851
870
|
const peerSource = this[_peerSetSource].get(node) || node
|
|
852
871
|
for (const edge of this[_problemEdges](node)) {
|
|
853
|
-
if (
|
|
872
|
+
if (edge.overridden) {
|
|
854
873
|
continue
|
|
874
|
+
}
|
|
855
875
|
|
|
856
876
|
// peerSetSource is only relevant when we have a peerEntryEdge
|
|
857
877
|
// otherwise we're setting regular non-peer deps as if they have
|
|
@@ -887,41 +907,114 @@ This is a one-time fix-up, please be patient...
|
|
|
887
907
|
|
|
888
908
|
/* istanbul ignore next */
|
|
889
909
|
debug(() => {
|
|
890
|
-
if (!dep)
|
|
910
|
+
if (!dep) {
|
|
891
911
|
throw new Error('no dep??')
|
|
912
|
+
}
|
|
892
913
|
})
|
|
893
914
|
|
|
894
915
|
tasks.push({edge, dep})
|
|
895
916
|
}
|
|
896
917
|
|
|
897
|
-
const
|
|
918
|
+
const placeDeps = tasks
|
|
898
919
|
.sort((a, b) => a.edge.name.localeCompare(b.edge.name, 'en'))
|
|
899
|
-
.map(({ edge, dep }) =>
|
|
920
|
+
.map(({ edge, dep }) => new PlaceDep({
|
|
921
|
+
edge,
|
|
922
|
+
dep,
|
|
923
|
+
|
|
924
|
+
explicitRequest: this[_explicitRequests].has(edge),
|
|
925
|
+
updateNames: this[_updateNames],
|
|
926
|
+
auditReport: this.auditReport,
|
|
927
|
+
force: this[_force],
|
|
928
|
+
preferDedupe: this[_preferDedupe],
|
|
929
|
+
legacyBundling: this[_legacyBundling],
|
|
930
|
+
strictPeerDeps: this[_strictPeerDeps],
|
|
931
|
+
legacyPeerDeps: this.legacyPeerDeps,
|
|
932
|
+
globalStyle: this[_globalStyle],
|
|
933
|
+
}))
|
|
900
934
|
|
|
901
935
|
const promises = []
|
|
902
|
-
for (const
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
936
|
+
for (const pd of placeDeps) {
|
|
937
|
+
// placing a dep is actually a tree of placing the dep itself
|
|
938
|
+
// and all of its peer group that aren't already met by the tree
|
|
939
|
+
depth({
|
|
940
|
+
tree: pd,
|
|
941
|
+
getChildren: pd => pd.children,
|
|
942
|
+
visit: pd => {
|
|
943
|
+
const { placed, edge, canPlace: cpd } = pd
|
|
944
|
+
// if we didn't place anything, nothing to do here
|
|
945
|
+
if (!placed) {
|
|
946
|
+
return
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
// we placed something, that means we changed the tree
|
|
950
|
+
if (placed.errors.length) {
|
|
951
|
+
this[_loadFailures].add(placed)
|
|
952
|
+
}
|
|
953
|
+
this[_mutateTree] = true
|
|
954
|
+
if (cpd.canPlaceSelf === OK) {
|
|
955
|
+
for (const edgeIn of placed.edgesIn) {
|
|
956
|
+
if (edgeIn === edge) {
|
|
957
|
+
continue
|
|
958
|
+
}
|
|
959
|
+
const { from, valid, overridden } = edgeIn
|
|
960
|
+
if (!overridden && !valid && !this[_depsSeen].has(from)) {
|
|
961
|
+
this.addTracker('idealTree', from.name, from.location)
|
|
962
|
+
this[_depsQueue].push(edgeIn.from)
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
} else {
|
|
966
|
+
/* istanbul ignore else - should be only OK or REPLACE here */
|
|
967
|
+
if (cpd.canPlaceSelf === REPLACE) {
|
|
968
|
+
// this may also create some invalid edges, for example if we're
|
|
969
|
+
// intentionally causing something to get nested which was
|
|
970
|
+
// previously placed in this location.
|
|
971
|
+
for (const edgeIn of placed.edgesIn) {
|
|
972
|
+
if (edgeIn === edge) {
|
|
973
|
+
continue
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
const { valid, overridden } = edgeIn
|
|
977
|
+
if (!valid && !overridden) {
|
|
978
|
+
// if it's already been visited, we have to re-visit
|
|
979
|
+
// otherwise, just enqueue normally.
|
|
980
|
+
this[_depsSeen].delete(edgeIn.from)
|
|
981
|
+
this[_depsQueue].push(edgeIn.from)
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
/* istanbul ignore if - should be impossible */
|
|
988
|
+
if (cpd.canPlaceSelf === CONFLICT) {
|
|
989
|
+
debug(() => {
|
|
990
|
+
const er = new Error('placed with canPlaceSelf=CONFLICT')
|
|
991
|
+
throw Object.assign(er, { placeDep: pd })
|
|
992
|
+
})
|
|
993
|
+
return
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
// lastly, also check for the missing deps of the node we placed
|
|
997
|
+
this[_depsQueue].push(placed)
|
|
998
|
+
|
|
999
|
+
// pre-fetch any problem edges, since we'll need these soon
|
|
1000
|
+
// if it fails at this point, though, dont' worry because it
|
|
1001
|
+
// may well be an optional dep that has gone missing. it'll
|
|
1002
|
+
// fail later anyway.
|
|
1003
|
+
const from = fromPath(placed)
|
|
1004
|
+
promises.push(...this[_problemEdges](placed).map(e =>
|
|
1005
|
+
this[_fetchManifest](npa.resolve(e.name, e.spec, from))
|
|
1006
|
+
.catch(er => null)))
|
|
1007
|
+
},
|
|
1008
|
+
})
|
|
917
1009
|
}
|
|
918
|
-
await Promise.all(promises)
|
|
919
1010
|
|
|
920
1011
|
for (const { to } of node.edgesOut.values()) {
|
|
921
|
-
if (to && to.isLink && to.target)
|
|
1012
|
+
if (to && to.isLink && to.target) {
|
|
922
1013
|
this[_linkNodes].add(to)
|
|
1014
|
+
}
|
|
923
1015
|
}
|
|
924
1016
|
|
|
1017
|
+
await Promise.all(promises)
|
|
925
1018
|
return this[_buildDepStep]()
|
|
926
1019
|
}
|
|
927
1020
|
|
|
@@ -934,7 +1027,6 @@ This is a one-time fix-up, please be patient...
|
|
|
934
1027
|
// Note that the virtual root will also have virtual copies of the
|
|
935
1028
|
// targets of any child Links, so that they resolve appropriately.
|
|
936
1029
|
const parent = parent_ || this[_virtualRoot](edge.from)
|
|
937
|
-
const realParent = edge.peer ? edge.from.resolveParent : edge.from
|
|
938
1030
|
|
|
939
1031
|
const spec = npa.resolve(edge.name, edge.spec, edge.from.path)
|
|
940
1032
|
const first = await this[_nodeFromSpec](edge.name, spec, parent, edge)
|
|
@@ -962,28 +1054,30 @@ This is a one-time fix-up, please be patient...
|
|
|
962
1054
|
|
|
963
1055
|
if (required.has(edge.from) && edge.type !== 'peerOptional' ||
|
|
964
1056
|
secondEdge && (
|
|
965
|
-
required.has(secondEdge.from) && secondEdge.type !== 'peerOptional'))
|
|
1057
|
+
required.has(secondEdge.from) && secondEdge.type !== 'peerOptional')) {
|
|
966
1058
|
required.add(node)
|
|
967
|
-
|
|
968
|
-
// handle otherwise unresolvable dependency nesting loops by
|
|
969
|
-
// creating a symbolic link
|
|
970
|
-
// a1 -> b1 -> a2 -> b2 -> a1 -> ...
|
|
971
|
-
// instead of nesting forever, when the loop occurs, create
|
|
972
|
-
// a symbolic link to the earlier instance
|
|
973
|
-
for (let p = edge.from.resolveParent; p; p = p.resolveParent) {
|
|
974
|
-
if (p.matches(node) && !p.isTop)
|
|
975
|
-
return new Link({ parent: realParent, target: p })
|
|
976
1059
|
}
|
|
977
1060
|
|
|
978
1061
|
// keep track of the thing that caused this node to be included.
|
|
979
1062
|
const src = parent.sourceReference
|
|
980
1063
|
this[_peerSetSource].set(node, src)
|
|
1064
|
+
|
|
1065
|
+
// do not load the peers along with the set if this is a global top pkg
|
|
1066
|
+
// otherwise we'll be tempted to put peers as other top-level installed
|
|
1067
|
+
// things, potentially clobbering what's there already, which is not
|
|
1068
|
+
// what we want. the missing edges will be picked up on the next pass.
|
|
1069
|
+
if (this[_global] && edge.from.isProjectRoot) {
|
|
1070
|
+
return node
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
// otherwise, we have to make sure that our peers can go along with us.
|
|
981
1074
|
return this[_loadPeerSet](node, required)
|
|
982
1075
|
}
|
|
983
1076
|
|
|
984
1077
|
[_virtualRoot] (node, reuse = false) {
|
|
985
|
-
if (reuse && this[_virtualRoots].has(node))
|
|
1078
|
+
if (reuse && this[_virtualRoots].has(node)) {
|
|
986
1079
|
return this[_virtualRoots].get(node)
|
|
1080
|
+
}
|
|
987
1081
|
|
|
988
1082
|
const vr = new Node({
|
|
989
1083
|
path: node.realpath,
|
|
@@ -1025,16 +1119,19 @@ This is a one-time fix-up, please be patient...
|
|
|
1025
1119
|
return [...node.edgesOut.values()]
|
|
1026
1120
|
.filter(edge => {
|
|
1027
1121
|
// If it's included in a bundle, we take whatever is specified.
|
|
1028
|
-
if (bundled.has(edge.name))
|
|
1122
|
+
if (bundled.has(edge.name)) {
|
|
1029
1123
|
return false
|
|
1124
|
+
}
|
|
1030
1125
|
|
|
1031
1126
|
// If it's already been logged as a load failure, skip it.
|
|
1032
|
-
if (edge.to && this[_loadFailures].has(edge.to))
|
|
1127
|
+
if (edge.to && this[_loadFailures].has(edge.to)) {
|
|
1033
1128
|
return false
|
|
1129
|
+
}
|
|
1034
1130
|
|
|
1035
1131
|
// If it's shrinkwrapped, we use what the shrinkwap wants.
|
|
1036
|
-
if (edge.to && edge.to.inShrinkwrap)
|
|
1132
|
+
if (edge.to && edge.to.inShrinkwrap) {
|
|
1037
1133
|
return false
|
|
1134
|
+
}
|
|
1038
1135
|
|
|
1039
1136
|
// If the edge has no destination, that's a problem, unless
|
|
1040
1137
|
// if it's peerOptional and not explicitly requested.
|
|
@@ -1044,20 +1141,24 @@ This is a one-time fix-up, please be patient...
|
|
|
1044
1141
|
}
|
|
1045
1142
|
|
|
1046
1143
|
// If the edge has an error, there's a problem.
|
|
1047
|
-
if (!edge.valid)
|
|
1144
|
+
if (!edge.valid) {
|
|
1048
1145
|
return true
|
|
1146
|
+
}
|
|
1049
1147
|
|
|
1050
|
-
//
|
|
1051
|
-
if (this[_updateNames].includes(edge.name))
|
|
1148
|
+
// user explicitly asked to update this package by name, problem
|
|
1149
|
+
if (this[_updateNames].includes(edge.name)) {
|
|
1052
1150
|
return true
|
|
1151
|
+
}
|
|
1053
1152
|
|
|
1054
|
-
//
|
|
1055
|
-
if (this[_isVulnerable](edge.to))
|
|
1153
|
+
// fixing a security vulnerability with this package, problem
|
|
1154
|
+
if (this[_isVulnerable](edge.to)) {
|
|
1056
1155
|
return true
|
|
1156
|
+
}
|
|
1057
1157
|
|
|
1058
|
-
//
|
|
1059
|
-
if (this[_explicitRequests].has(edge))
|
|
1158
|
+
// user has explicitly asked to install this package, problem
|
|
1159
|
+
if (this[_explicitRequests].has(edge)) {
|
|
1060
1160
|
return true
|
|
1161
|
+
}
|
|
1061
1162
|
|
|
1062
1163
|
// No problems!
|
|
1063
1164
|
return false
|
|
@@ -1073,9 +1174,9 @@ This is a one-time fix-up, please be patient...
|
|
|
1073
1174
|
// if available and valid.
|
|
1074
1175
|
spec = this.idealTree.meta.checkYarnLock(spec, options)
|
|
1075
1176
|
|
|
1076
|
-
if (this[_manifests].has(spec.raw))
|
|
1177
|
+
if (this[_manifests].has(spec.raw)) {
|
|
1077
1178
|
return this[_manifests].get(spec.raw)
|
|
1078
|
-
else {
|
|
1179
|
+
} else {
|
|
1079
1180
|
this.log.silly('fetch manifest', spec.raw)
|
|
1080
1181
|
const p = pacote.manifest(spec, options)
|
|
1081
1182
|
.then(mani => {
|
|
@@ -1145,8 +1246,9 @@ This is a one-time fix-up, please be patient...
|
|
|
1145
1246
|
|
|
1146
1247
|
for (const edge of peerEdges) {
|
|
1147
1248
|
// already placed this one, and we're happy with it.
|
|
1148
|
-
if (edge.valid && edge.to)
|
|
1249
|
+
if (edge.valid && edge.to) {
|
|
1149
1250
|
continue
|
|
1251
|
+
}
|
|
1150
1252
|
|
|
1151
1253
|
const parentEdge = node.parent.edgesOut.get(edge.name)
|
|
1152
1254
|
const {isProjectRoot, isWorkspace} = node.parent.sourceReference
|
|
@@ -1167,17 +1269,25 @@ This is a one-time fix-up, please be patient...
|
|
|
1167
1269
|
// a conflict. this is always a problem in strict mode, never
|
|
1168
1270
|
// in force mode, and a problem in non-strict mode if this isn't
|
|
1169
1271
|
// on behalf of our project. in all such cases, we warn at least.
|
|
1170
|
-
const dep = await this[_nodeFromEdge](
|
|
1272
|
+
const dep = await this[_nodeFromEdge](
|
|
1273
|
+
parentEdge,
|
|
1274
|
+
node.parent,
|
|
1275
|
+
edge,
|
|
1276
|
+
required
|
|
1277
|
+
)
|
|
1171
1278
|
|
|
1172
1279
|
// hooray! that worked!
|
|
1173
|
-
if (edge.valid)
|
|
1280
|
+
if (edge.valid) {
|
|
1174
1281
|
continue
|
|
1282
|
+
}
|
|
1175
1283
|
|
|
1176
1284
|
// allow it. either we're overriding, or it's not something
|
|
1177
1285
|
// that will be installed by default anyway, and we'll fail when
|
|
1178
1286
|
// we get to the point where we need to, if we need to.
|
|
1179
|
-
if (conflictOK || !required.has(dep))
|
|
1287
|
+
if (conflictOK || !required.has(dep)) {
|
|
1288
|
+
edge.overridden = true
|
|
1180
1289
|
continue
|
|
1290
|
+
}
|
|
1181
1291
|
|
|
1182
1292
|
// problem
|
|
1183
1293
|
this[_failPeerConflict](edge, parentEdge)
|
|
@@ -1202,8 +1312,9 @@ This is a one-time fix-up, please be patient...
|
|
|
1202
1312
|
// isn't also required, then there's a good chance we won't need it,
|
|
1203
1313
|
// so allow it for now and let it conflict if it turns out to actually
|
|
1204
1314
|
// be necessary for the installation.
|
|
1205
|
-
if (conflictOK || !required.has(edge.from))
|
|
1315
|
+
if (conflictOK || !required.has(edge.from)) {
|
|
1206
1316
|
continue
|
|
1317
|
+
}
|
|
1207
1318
|
|
|
1208
1319
|
// ok, it's the root, or we're in unforced strict mode, so this is bad
|
|
1209
1320
|
this[_failPeerConflict](edge, parentEdge)
|
|
@@ -1219,9 +1330,7 @@ This is a one-time fix-up, please be patient...
|
|
|
1219
1330
|
[_explainPeerConflict] (edge, currentEdge) {
|
|
1220
1331
|
const node = edge.from
|
|
1221
1332
|
const curNode = node.resolve(edge.name)
|
|
1222
|
-
const
|
|
1223
|
-
const current = curNode ? curNode.explain() : pc.current
|
|
1224
|
-
const peerConflict = pc.peer
|
|
1333
|
+
const current = curNode.explain()
|
|
1225
1334
|
return {
|
|
1226
1335
|
code: 'ERESOLVE',
|
|
1227
1336
|
current,
|
|
@@ -1230,640 +1339,11 @@ This is a one-time fix-up, please be patient...
|
|
|
1230
1339
|
// the tree handling logic.
|
|
1231
1340
|
currentEdge: currentEdge ? currentEdge.explain() : null,
|
|
1232
1341
|
edge: edge.explain(),
|
|
1233
|
-
peerConflict,
|
|
1234
1342
|
strictPeerDeps: this[_strictPeerDeps],
|
|
1235
1343
|
force: this[_force],
|
|
1236
1344
|
}
|
|
1237
1345
|
}
|
|
1238
1346
|
|
|
1239
|
-
[_warnPeerConflict] (edge) {
|
|
1240
|
-
// track that we've overridden this edge, so that we don't keep trying
|
|
1241
|
-
// to re-resolve it in an infinite loop.
|
|
1242
|
-
this[_edgesOverridden].add(edge)
|
|
1243
|
-
const expl = this[_explainPeerConflict](edge)
|
|
1244
|
-
this.log.warn('ERESOLVE', 'overriding peer dependency', expl)
|
|
1245
|
-
}
|
|
1246
|
-
|
|
1247
|
-
// starting from either node, or in the case of non-root peer deps,
|
|
1248
|
-
// the node's parent, walk up the tree until we find the first spot
|
|
1249
|
-
// where this dep cannot be placed, and use the one right before that.
|
|
1250
|
-
// place dep, requested by node, to satisfy edge
|
|
1251
|
-
// XXX split this out into a separate method or mixin? It's quite a lot
|
|
1252
|
-
// of functionality that ought to have its own unit tests more conveniently.
|
|
1253
|
-
[_placeDep] (dep, node, edge, peerEntryEdge = null, peerPath = []) {
|
|
1254
|
-
if (edge.to &&
|
|
1255
|
-
!edge.error &&
|
|
1256
|
-
!this[_explicitRequests].has(edge) &&
|
|
1257
|
-
!this[_updateNames].includes(edge.name) &&
|
|
1258
|
-
!this[_isVulnerable](edge.to))
|
|
1259
|
-
return []
|
|
1260
|
-
|
|
1261
|
-
// top nodes should still get peer deps from their fsParent if possible,
|
|
1262
|
-
// and only install locally if there's no other option, eg for a link
|
|
1263
|
-
// outside of the project root, or for a conflicted dep.
|
|
1264
|
-
const start = edge.peer && !node.isProjectRoot ? node.resolveParent || node
|
|
1265
|
-
: node
|
|
1266
|
-
|
|
1267
|
-
let target
|
|
1268
|
-
let canPlace = null
|
|
1269
|
-
let isSource = false
|
|
1270
|
-
const source = this[_peerSetSource].get(dep)
|
|
1271
|
-
for (let check = start; check; check = check.resolveParent) {
|
|
1272
|
-
// we always give the FIRST place we possibly *can* put this a little
|
|
1273
|
-
// extra prioritization with peer dep overrides and deduping
|
|
1274
|
-
if (check === source)
|
|
1275
|
-
isSource = true
|
|
1276
|
-
|
|
1277
|
-
// if the current location has a peerDep on it, then we can't place here
|
|
1278
|
-
// this is pretty rare to hit, since we always prefer deduping peers.
|
|
1279
|
-
const checkEdge = check.edgesOut.get(edge.name)
|
|
1280
|
-
if (!check.isTop && checkEdge && checkEdge.peer)
|
|
1281
|
-
continue
|
|
1282
|
-
|
|
1283
|
-
const cp = this[_canPlaceDep](dep, check, edge, peerEntryEdge, peerPath, isSource)
|
|
1284
|
-
isSource = false
|
|
1285
|
-
|
|
1286
|
-
// anything other than a conflict is fine to proceed with
|
|
1287
|
-
if (cp !== CONFLICT) {
|
|
1288
|
-
canPlace = cp
|
|
1289
|
-
target = check
|
|
1290
|
-
} else
|
|
1291
|
-
break
|
|
1292
|
-
|
|
1293
|
-
// nest packages like npm v1 and v2
|
|
1294
|
-
// very disk-inefficient
|
|
1295
|
-
if (this[_legacyBundling])
|
|
1296
|
-
break
|
|
1297
|
-
|
|
1298
|
-
// when installing globally, or just in global style, we never place
|
|
1299
|
-
// deps above the first level.
|
|
1300
|
-
const tree = this.idealTree && this.idealTree.target
|
|
1301
|
-
if (this[_globalStyle] && check.resolveParent === tree)
|
|
1302
|
-
break
|
|
1303
|
-
}
|
|
1304
|
-
|
|
1305
|
-
// if we can't find a target, that means that the last placed checked
|
|
1306
|
-
// (and all the places before it) had a copy already. if we're in
|
|
1307
|
-
// --force mode, then the user has explicitly said that they're ok
|
|
1308
|
-
// with conflicts. This can only occur in --force mode in the case
|
|
1309
|
-
// when a node was added to the tree with a peerOptional dep that we
|
|
1310
|
-
// ignored, and then later, that edge became invalid, and we fail to
|
|
1311
|
-
// resolve it. We will warn about it in a moment.
|
|
1312
|
-
if (!target) {
|
|
1313
|
-
if (this[_force]) {
|
|
1314
|
-
// we know that there is a dep (not the root) which is the target
|
|
1315
|
-
// of this edge, or else it wouldn't have been a conflict.
|
|
1316
|
-
target = edge.to.resolveParent
|
|
1317
|
-
canPlace = KEEP
|
|
1318
|
-
} else
|
|
1319
|
-
this[_failPeerConflict](edge)
|
|
1320
|
-
} else {
|
|
1321
|
-
// it worked, so we clearly have no peer conflicts at this point.
|
|
1322
|
-
this[_peerConflict] = null
|
|
1323
|
-
}
|
|
1324
|
-
|
|
1325
|
-
this.log.silly(
|
|
1326
|
-
'placeDep',
|
|
1327
|
-
target.location || 'ROOT',
|
|
1328
|
-
`${dep.name}@${dep.version}`,
|
|
1329
|
-
canPlace.description || /* istanbul ignore next */ canPlace,
|
|
1330
|
-
`for: ${node.package._id || node.location}`,
|
|
1331
|
-
`want: ${edge.spec || '*'}`
|
|
1332
|
-
)
|
|
1333
|
-
|
|
1334
|
-
// Can only get KEEP here if the original edge was valid,
|
|
1335
|
-
// and we're checking for an update but it's already up to date.
|
|
1336
|
-
if (canPlace === KEEP) {
|
|
1337
|
-
if (edge.peer && !target.children.get(edge.name).satisfies(edge)) {
|
|
1338
|
-
// this is an overridden peer dep
|
|
1339
|
-
this[_warnPeerConflict](edge)
|
|
1340
|
-
}
|
|
1341
|
-
|
|
1342
|
-
// if we get a KEEP in a update scenario, then we MAY have something
|
|
1343
|
-
// already duplicating this unnecessarily! For example:
|
|
1344
|
-
// ```
|
|
1345
|
-
// root
|
|
1346
|
-
// +-- x (dep: y@1.x)
|
|
1347
|
-
// | +-- y@1.0.0
|
|
1348
|
-
// +-- y@1.1.0
|
|
1349
|
-
// ```
|
|
1350
|
-
// Now say we do `reify({update:['y']})`, and the latest version is
|
|
1351
|
-
// 1.1.0, which we already have in the root. We'll try to place y@1.1.0
|
|
1352
|
-
// first in x, then in the root, ending with KEEP, because we already
|
|
1353
|
-
// have it. In that case, we ought to REMOVE the nm/x/nm/y node, because
|
|
1354
|
-
// it is an unnecessary duplicate.
|
|
1355
|
-
this[_pruneDedupable](target)
|
|
1356
|
-
return []
|
|
1357
|
-
}
|
|
1358
|
-
|
|
1359
|
-
// figure out which of this node's peer deps will get placed as well
|
|
1360
|
-
const virtualRoot = dep.parent
|
|
1361
|
-
|
|
1362
|
-
const newDep = new dep.constructor({
|
|
1363
|
-
name: dep.name,
|
|
1364
|
-
pkg: dep.package,
|
|
1365
|
-
resolved: dep.resolved,
|
|
1366
|
-
integrity: dep.integrity,
|
|
1367
|
-
legacyPeerDeps: this.legacyPeerDeps,
|
|
1368
|
-
error: dep.errors[0],
|
|
1369
|
-
...(dep.isLink ? { target: dep.target, realpath: dep.target.path } : {}),
|
|
1370
|
-
})
|
|
1371
|
-
if (this[_loadFailures].has(dep))
|
|
1372
|
-
this[_loadFailures].add(newDep)
|
|
1373
|
-
|
|
1374
|
-
const placed = [newDep]
|
|
1375
|
-
const oldChild = target.children.get(edge.name)
|
|
1376
|
-
if (oldChild) {
|
|
1377
|
-
// if we're replacing, we should also remove any nodes for edges that
|
|
1378
|
-
// are now invalid, and where this (or its deps) is the only dependent,
|
|
1379
|
-
// and also recurse on that pruning. Otherwise leaving that dep node
|
|
1380
|
-
// around can result in spurious conflicts pushing nodes deeper into
|
|
1381
|
-
// the tree than needed in the case of cycles that will be removed
|
|
1382
|
-
// later anyway.
|
|
1383
|
-
const oldDeps = []
|
|
1384
|
-
for (const [name, edge] of oldChild.edgesOut.entries()) {
|
|
1385
|
-
if (!newDep.edgesOut.has(name) && edge.to)
|
|
1386
|
-
oldDeps.push(...gatherDepSet([edge.to], e => e.to !== edge.to))
|
|
1387
|
-
}
|
|
1388
|
-
newDep.replace(oldChild)
|
|
1389
|
-
this[_pruneForReplacement](newDep, oldDeps)
|
|
1390
|
-
// this may also create some invalid edges, for example if we're
|
|
1391
|
-
// intentionally causing something to get nested which was previously
|
|
1392
|
-
// placed in this location.
|
|
1393
|
-
for (const edgeIn of newDep.edgesIn) {
|
|
1394
|
-
if (edgeIn.invalid && edgeIn !== edge) {
|
|
1395
|
-
this[_depsQueue].push(edgeIn.from)
|
|
1396
|
-
this[_depsSeen].delete(edgeIn.from)
|
|
1397
|
-
}
|
|
1398
|
-
}
|
|
1399
|
-
} else
|
|
1400
|
-
newDep.parent = target
|
|
1401
|
-
|
|
1402
|
-
if (edge.peer && !newDep.satisfies(edge)) {
|
|
1403
|
-
// this is an overridden peer dep
|
|
1404
|
-
this[_warnPeerConflict](edge)
|
|
1405
|
-
}
|
|
1406
|
-
|
|
1407
|
-
// If the edge is not an error, then we're updating something, and
|
|
1408
|
-
// MAY end up putting a better/identical node further up the tree in
|
|
1409
|
-
// a way that causes an unnecessary duplication. If so, remove the
|
|
1410
|
-
// now-unnecessary node.
|
|
1411
|
-
if (edge.valid && edge.to && edge.to !== newDep)
|
|
1412
|
-
this[_pruneDedupable](edge.to, false)
|
|
1413
|
-
|
|
1414
|
-
// visit any dependents who are upset by this change
|
|
1415
|
-
// if it's an angry overridden peer edge, however, make sure we
|
|
1416
|
-
// skip over it!
|
|
1417
|
-
for (const edgeIn of newDep.edgesIn) {
|
|
1418
|
-
if (edgeIn !== edge && !edgeIn.valid && !this[_depsSeen].has(edge.from)) {
|
|
1419
|
-
this.addTracker('idealTree', edgeIn.from.name, edgeIn.from.location)
|
|
1420
|
-
this[_depsQueue].push(edgeIn.from)
|
|
1421
|
-
}
|
|
1422
|
-
}
|
|
1423
|
-
|
|
1424
|
-
// in case we just made some duplicates that can be removed,
|
|
1425
|
-
// prune anything deeper in the tree that can be replaced by this
|
|
1426
|
-
if (this.idealTree) {
|
|
1427
|
-
for (const node of this.idealTree.inventory.query('name', newDep.name)) {
|
|
1428
|
-
if (!node.isTop && node.isDescendantOf(target))
|
|
1429
|
-
this[_pruneDedupable](node, false)
|
|
1430
|
-
}
|
|
1431
|
-
}
|
|
1432
|
-
|
|
1433
|
-
// also place its unmet or invalid peer deps at this location
|
|
1434
|
-
// note that newDep has now been removed from the virtualRoot set
|
|
1435
|
-
// by virtue of being placed in the target's node_modules.
|
|
1436
|
-
// loop through any peer deps from the thing we just placed, and place
|
|
1437
|
-
// those ones as well. it's safe to do this with the virtual nodes,
|
|
1438
|
-
// because we're copying rather than moving them out of the virtual root,
|
|
1439
|
-
// otherwise they'd be gone and the peer set would change throughout
|
|
1440
|
-
// this loop.
|
|
1441
|
-
for (const peerEdge of newDep.edgesOut.values()) {
|
|
1442
|
-
const peer = virtualRoot.children.get(peerEdge.name)
|
|
1443
|
-
|
|
1444
|
-
// Note: if the virtualRoot *doesn't* have the peer, then that means
|
|
1445
|
-
// it's an optional peer dep. If it's not being properly met (ie,
|
|
1446
|
-
// peerEdge.valid is false), that this is likely heading for an
|
|
1447
|
-
// ERESOLVE error, unless it can walk further up the tree.
|
|
1448
|
-
if (!peerEdge.peer || peerEdge.valid || !peer)
|
|
1449
|
-
continue
|
|
1450
|
-
|
|
1451
|
-
const peerPlaced = this[_placeDep](
|
|
1452
|
-
peer, newDep, peerEdge, peerEntryEdge || edge, peerPath)
|
|
1453
|
-
placed.push(...peerPlaced)
|
|
1454
|
-
}
|
|
1455
|
-
|
|
1456
|
-
// we're done with this now, clean it up.
|
|
1457
|
-
this[_virtualRoots].delete(virtualRoot.sourceReference)
|
|
1458
|
-
|
|
1459
|
-
return placed
|
|
1460
|
-
}
|
|
1461
|
-
|
|
1462
|
-
// prune all the nodes in a branch of the tree that can be safely removed
|
|
1463
|
-
// This is only the most basic duplication detection; it finds if there
|
|
1464
|
-
// is another satisfying node further up the tree, and if so, dedupes.
|
|
1465
|
-
// Even in legacyBundling mode, we do this amount of deduplication.
|
|
1466
|
-
[_pruneDedupable] (node, descend = true) {
|
|
1467
|
-
if (node.canDedupe(this[_preferDedupe])) {
|
|
1468
|
-
node.root = null
|
|
1469
|
-
return
|
|
1470
|
-
}
|
|
1471
|
-
if (descend) {
|
|
1472
|
-
// sort these so that they're deterministically ordered
|
|
1473
|
-
// otherwise, resulting tree shape is dependent on the order
|
|
1474
|
-
// in which they happened to be resolved.
|
|
1475
|
-
const nodeSort = (a, b) => a.location.localeCompare(b.location, 'en')
|
|
1476
|
-
|
|
1477
|
-
const children = [...node.children.values()].sort(nodeSort)
|
|
1478
|
-
const fsChildren = [...node.fsChildren].sort(nodeSort)
|
|
1479
|
-
for (const child of children)
|
|
1480
|
-
this[_pruneDedupable](child)
|
|
1481
|
-
for (const topNode of fsChildren) {
|
|
1482
|
-
const children = [...topNode.children.values()].sort(nodeSort)
|
|
1483
|
-
for (const child of children)
|
|
1484
|
-
this[_pruneDedupable](child)
|
|
1485
|
-
}
|
|
1486
|
-
}
|
|
1487
|
-
}
|
|
1488
|
-
|
|
1489
|
-
[_pruneForReplacement] (node, oldDeps) {
|
|
1490
|
-
// gather up all the invalid edgesOut, and any now-extraneous
|
|
1491
|
-
// deps that the new node doesn't depend on but the old one did.
|
|
1492
|
-
const invalidDeps = new Set([...node.edgesOut.values()]
|
|
1493
|
-
.filter(e => e.to && !e.valid).map(e => e.to))
|
|
1494
|
-
for (const dep of oldDeps) {
|
|
1495
|
-
const set = gatherDepSet([dep], e => e.to !== dep && e.valid)
|
|
1496
|
-
for (const dep of set)
|
|
1497
|
-
invalidDeps.add(dep)
|
|
1498
|
-
}
|
|
1499
|
-
|
|
1500
|
-
// ignore dependency edges from the node being replaced, but
|
|
1501
|
-
// otherwise filter the set down to just the set with no
|
|
1502
|
-
// dependencies from outside the set, except the node in question.
|
|
1503
|
-
const deps = gatherDepSet(invalidDeps, edge =>
|
|
1504
|
-
edge.from !== node && edge.to !== node && edge.valid)
|
|
1505
|
-
|
|
1506
|
-
// now just delete whatever's left, because it's junk
|
|
1507
|
-
for (const dep of deps)
|
|
1508
|
-
dep.parent = null
|
|
1509
|
-
}
|
|
1510
|
-
|
|
1511
|
-
// check if we can place DEP in TARGET to satisfy EDGE
|
|
1512
|
-
// Need to verify:
|
|
1513
|
-
// - no child by that name there already
|
|
1514
|
-
// - target does not have a peer dep on name
|
|
1515
|
-
// - no higher-level pkg by that name and incompatible spec is depended on
|
|
1516
|
-
// by anything lower in the tree.
|
|
1517
|
-
// - node's peer deps and meta-peer deps are siblings in a virtual root at
|
|
1518
|
-
// this point. make sure that the whole family can come along, so apply
|
|
1519
|
-
// the same checks to each of them. They may land higher up in the tree,
|
|
1520
|
-
// but we need to know that they CAN live here.
|
|
1521
|
-
// Responses:
|
|
1522
|
-
// - OK - Yes, because there is nothing there and no conflicts caused
|
|
1523
|
-
// - REPLACE - Yes, and you can clobber what's there
|
|
1524
|
-
// - KEEP - No, but what's there is fine
|
|
1525
|
-
// - CONFLICT - You may not put that there
|
|
1526
|
-
//
|
|
1527
|
-
// Check peers on OK or REPLACE. KEEP and CONFLICT do not require peer
|
|
1528
|
-
// checking, because either we're leaving it alone, or it won't work anyway.
|
|
1529
|
-
// When we check peers, we pass along the peerEntryEdge to track the
|
|
1530
|
-
// original edge that caused us to load the family of peer dependencies.
|
|
1531
|
-
[_canPlaceDep] (dep, target, edge, peerEntryEdge = null, peerPath = [], isSource = false) {
|
|
1532
|
-
/* istanbul ignore next */
|
|
1533
|
-
debug(() => {
|
|
1534
|
-
if (!dep)
|
|
1535
|
-
throw new Error('no dep??')
|
|
1536
|
-
})
|
|
1537
|
-
const entryEdge = peerEntryEdge || edge
|
|
1538
|
-
const source = this[_peerSetSource].get(dep)
|
|
1539
|
-
|
|
1540
|
-
isSource = isSource || target === source
|
|
1541
|
-
// if we're overriding the source, then we care if the *target* is
|
|
1542
|
-
// ours, even if it wasn't actually the original source, since we
|
|
1543
|
-
// are depending on something that has a dep that can't go in its own
|
|
1544
|
-
// folder. for example, a -> b, b -> PEER(a). Even though a is the
|
|
1545
|
-
// source, b has to be installed up a level, and if the root package
|
|
1546
|
-
// depends on a, and it has a conflict, it's our problem. So, the root
|
|
1547
|
-
// (or whatever is bringing in a) becomes the "effective source" for
|
|
1548
|
-
// the purposes of this calculation.
|
|
1549
|
-
const { isProjectRoot, isWorkspace } = isSource ? target : source || {}
|
|
1550
|
-
const isMine = isProjectRoot || isWorkspace
|
|
1551
|
-
|
|
1552
|
-
// Useful testing thingie right here.
|
|
1553
|
-
// peerEntryEdge should *always* be a non-peer dependency, or a peer
|
|
1554
|
-
// dependency from the root node. When we get spurious ERESOLVE errors,
|
|
1555
|
-
// or *don't* get ERESOLVE errors when we should, check to see if this
|
|
1556
|
-
// fails, because it MAY mean we got off track somehow.
|
|
1557
|
-
/* istanbul ignore next - debug check, should be impossible */
|
|
1558
|
-
debug(() => {
|
|
1559
|
-
if (peerEntryEdge && peerEntryEdge.peer && !peerEntryEdge.from.isTop)
|
|
1560
|
-
throw new Error('lost original peerEntryEdge somehow?')
|
|
1561
|
-
})
|
|
1562
|
-
|
|
1563
|
-
if (target.children.has(edge.name)) {
|
|
1564
|
-
const current = target.children.get(edge.name)
|
|
1565
|
-
|
|
1566
|
-
// same thing = keep, UNLESS the current doesn't satisfy and new
|
|
1567
|
-
// one does satisfy. This can happen if it's a link to a matching target
|
|
1568
|
-
// at a different location, which satisfies a version dep, but not a
|
|
1569
|
-
// file: dep. If neither of them satisfy, then we can replace it,
|
|
1570
|
-
// because presumably it's better for a peer or something.
|
|
1571
|
-
if (dep.matches(current)) {
|
|
1572
|
-
if (current.satisfies(edge) || !dep.satisfies(edge))
|
|
1573
|
-
return KEEP
|
|
1574
|
-
}
|
|
1575
|
-
|
|
1576
|
-
const { version: curVer } = current
|
|
1577
|
-
const { version: newVer } = dep
|
|
1578
|
-
const tryReplace = curVer && newVer && semver.gte(newVer, curVer)
|
|
1579
|
-
if (tryReplace && dep.canReplace(current)) {
|
|
1580
|
-
const res = this[_canPlacePeers](dep, target, edge, REPLACE, peerEntryEdge, peerPath, isSource)
|
|
1581
|
-
/* istanbul ignore else - It's extremely rare that a replaceable
|
|
1582
|
-
* node would be a conflict, if the current one wasn't a conflict,
|
|
1583
|
-
* but it is theoretically possible if peer deps are pinned. In
|
|
1584
|
-
* that case we treat it like any other conflict, and keep trying */
|
|
1585
|
-
if (res !== CONFLICT)
|
|
1586
|
-
return res
|
|
1587
|
-
}
|
|
1588
|
-
|
|
1589
|
-
// ok, can't replace the current with new one, but maybe current is ok?
|
|
1590
|
-
// no need to check if it's a peer that's valid to be here, because
|
|
1591
|
-
// peers are always placed along with their entry source
|
|
1592
|
-
if (edge.satisfiedBy(current))
|
|
1593
|
-
return KEEP
|
|
1594
|
-
|
|
1595
|
-
// if we prefer deduping, then try replacing newer with older
|
|
1596
|
-
// we always prefer to dedupe peers, because they are trying
|
|
1597
|
-
// a bit harder to be singletons.
|
|
1598
|
-
const preferDedupe = this[_preferDedupe] || edge.peer
|
|
1599
|
-
if (preferDedupe && !tryReplace && dep.canReplace(current)) {
|
|
1600
|
-
const res = this[_canPlacePeers](dep, target, edge, REPLACE, peerEntryEdge, peerPath, isSource)
|
|
1601
|
-
/* istanbul ignore else - It's extremely rare that a replaceable
|
|
1602
|
-
* node would be a conflict, if the current one wasn't a conflict,
|
|
1603
|
-
* but it is theoretically possible if peer deps are pinned. In
|
|
1604
|
-
* that case we treat it like any other conflict, and keep trying */
|
|
1605
|
-
if (res !== CONFLICT)
|
|
1606
|
-
return res
|
|
1607
|
-
}
|
|
1608
|
-
|
|
1609
|
-
// check for conflict override cases.
|
|
1610
|
-
// first: is this the only place this thing can go? If the target is
|
|
1611
|
-
// the source, then one of these things are true.
|
|
1612
|
-
//
|
|
1613
|
-
// 1. the conflicted dep was deduped up to here from a lower dependency
|
|
1614
|
-
// w -> (x,y)
|
|
1615
|
-
// x -> (z)
|
|
1616
|
-
// y -> PEER(p@1)
|
|
1617
|
-
// z -> (q)
|
|
1618
|
-
// q -> (p@2)
|
|
1619
|
-
//
|
|
1620
|
-
// When building, let's say that x is fully placed, with all of its
|
|
1621
|
-
// deps, and we're _adding_ y. Since the peer on p@1 was not initially
|
|
1622
|
-
// present, it's been deduped up to w, and now needs to be pushed out.
|
|
1623
|
-
// Replace it, and potentially also replace its peer set (though that'll
|
|
1624
|
-
// be accomplished by making the same determination when we call
|
|
1625
|
-
// _canPlacePeers)
|
|
1626
|
-
//
|
|
1627
|
-
// 2. the dep we're TRYING to place here ought to be overridden by the
|
|
1628
|
-
// one that's here now, because current is (a) a direct dep of the
|
|
1629
|
-
// source, or (b) an already-placed peer in a conflicted peer set, or
|
|
1630
|
-
// (c) an already-placed peer in a different peer set at the same level.
|
|
1631
|
-
// If strict or ours, conflict. Otherwise, keep.
|
|
1632
|
-
if (isSource) {
|
|
1633
|
-
// check to see if the current module could go deeper in the tree
|
|
1634
|
-
let canReplace = true
|
|
1635
|
-
// only do this check when we're placing peers. when we're placing
|
|
1636
|
-
// the original in the source, we know that the edge from the source
|
|
1637
|
-
// is the thing we're trying to place, so its peer set will need to be
|
|
1638
|
-
// placed here as well. the virtualRoot already has the appropriate
|
|
1639
|
-
// overrides applied.
|
|
1640
|
-
if (peerEntryEdge) {
|
|
1641
|
-
const currentPeerSet = getPeerSet(current)
|
|
1642
|
-
|
|
1643
|
-
// We are effectively replacing currentPeerSet with newPeerSet
|
|
1644
|
-
// If there are any non-peer deps coming into the currentPeerSet,
|
|
1645
|
-
// which are currently valid, and are from the target, then that
|
|
1646
|
-
// means that we have to ensure that they're not going to be made
|
|
1647
|
-
// invalid by putting the newPeerSet in place.
|
|
1648
|
-
// If the edge comes from somewhere deeper than the target, then
|
|
1649
|
-
// that's fine, because we'll create an invalid edge, detect it,
|
|
1650
|
-
// and duplicate the node further into the tree.
|
|
1651
|
-
// loop through the currentPeerSet checking for valid edges on
|
|
1652
|
-
// the members of the peer set which will be made invalid.
|
|
1653
|
-
const targetEdges = new Set()
|
|
1654
|
-
for (const p of currentPeerSet) {
|
|
1655
|
-
for (const edge of p.edgesIn) {
|
|
1656
|
-
// edge from within the peerSet, ignore
|
|
1657
|
-
if (currentPeerSet.has(edge.from))
|
|
1658
|
-
continue
|
|
1659
|
-
// only care about valid edges from target.
|
|
1660
|
-
// edges from elsewhere can dupe if offended, invalid edges
|
|
1661
|
-
// are already being fixed or will be later.
|
|
1662
|
-
if (edge.from !== target || !edge.valid)
|
|
1663
|
-
continue
|
|
1664
|
-
targetEdges.add(edge)
|
|
1665
|
-
}
|
|
1666
|
-
}
|
|
1667
|
-
|
|
1668
|
-
for (const edge of targetEdges) {
|
|
1669
|
-
// see if we intend to replace this one anyway
|
|
1670
|
-
const rep = dep.parent.children.get(edge.name)
|
|
1671
|
-
const current = edge.to
|
|
1672
|
-
if (!rep) {
|
|
1673
|
-
// this isn't one we're replacing. but it WAS included in the
|
|
1674
|
-
// peerSet for some reason, so make sure that it's still
|
|
1675
|
-
// ok with the replacements in the new peerSet
|
|
1676
|
-
for (const curEdge of current.edgesOut.values()) {
|
|
1677
|
-
const newRepDep = dep.parent.children.get(curEdge.name)
|
|
1678
|
-
if (curEdge.valid && newRepDep && !newRepDep.satisfies(curEdge)) {
|
|
1679
|
-
canReplace = false
|
|
1680
|
-
break
|
|
1681
|
-
}
|
|
1682
|
-
}
|
|
1683
|
-
continue
|
|
1684
|
-
}
|
|
1685
|
-
|
|
1686
|
-
// was this replacement already an override of some sort?
|
|
1687
|
-
const override = [...rep.edgesIn].some(e => !e.valid)
|
|
1688
|
-
// if we have a rep, and it's ok to put in this location, and
|
|
1689
|
-
// it's not already part of an override in the peerSet, then
|
|
1690
|
-
// we can continue with it.
|
|
1691
|
-
if (rep.satisfies(edge) && !override)
|
|
1692
|
-
continue
|
|
1693
|
-
// Otherwise, we cannot replace.
|
|
1694
|
-
canReplace = false
|
|
1695
|
-
break
|
|
1696
|
-
}
|
|
1697
|
-
// if we're going to be replacing the peerSet, we have to remove
|
|
1698
|
-
// and re-resolve any members of the old peerSet that are not
|
|
1699
|
-
// present in the new one, and which will have invalid edges.
|
|
1700
|
-
// We know that they're not depended upon by the target, or else
|
|
1701
|
-
// they would have caused a conflict, so they'll get landed deeper
|
|
1702
|
-
// in the tree, if possible.
|
|
1703
|
-
if (canReplace) {
|
|
1704
|
-
let needNesting = false
|
|
1705
|
-
OUTER: for (const node of currentPeerSet) {
|
|
1706
|
-
const rep = dep.parent.children.get(node.name)
|
|
1707
|
-
// has a replacement, already addressed above
|
|
1708
|
-
if (rep)
|
|
1709
|
-
continue
|
|
1710
|
-
|
|
1711
|
-
// ok, it has been placed here to dedupe, see if it needs to go
|
|
1712
|
-
// back deeper within the tree.
|
|
1713
|
-
for (const edge of node.edgesOut.values()) {
|
|
1714
|
-
const repDep = dep.parent.children.get(edge.name)
|
|
1715
|
-
// not in new peerSet, maybe fine.
|
|
1716
|
-
if (!repDep)
|
|
1717
|
-
continue
|
|
1718
|
-
|
|
1719
|
-
// new thing will be fine, no worries
|
|
1720
|
-
if (repDep.satisfies(edge))
|
|
1721
|
-
continue
|
|
1722
|
-
|
|
1723
|
-
// uhoh, we'll have to nest them.
|
|
1724
|
-
needNesting = true
|
|
1725
|
-
break OUTER
|
|
1726
|
-
}
|
|
1727
|
-
}
|
|
1728
|
-
|
|
1729
|
-
// to nest, just delete everything without a target dep
|
|
1730
|
-
// that's in the current peerSet, and add their dependants
|
|
1731
|
-
// to the _depsQueue for evaluation. Some of these MAY end
|
|
1732
|
-
// up in the same location again, and that's fine.
|
|
1733
|
-
if (needNesting) {
|
|
1734
|
-
// avoid mutating the tree while we're examining it
|
|
1735
|
-
const dependants = new Set()
|
|
1736
|
-
const reresolve = new Set()
|
|
1737
|
-
OUTER: for (const node of currentPeerSet) {
|
|
1738
|
-
const rep = dep.parent.children.get(node.name)
|
|
1739
|
-
if (rep)
|
|
1740
|
-
continue
|
|
1741
|
-
// create a separate set for each one, so we can skip any
|
|
1742
|
-
// that might somehow have an incoming target edge
|
|
1743
|
-
const deps = new Set()
|
|
1744
|
-
for (const edge of node.edgesIn) {
|
|
1745
|
-
// a target dep, skip this dep entirely, already addressed
|
|
1746
|
-
// ignoring for coverage, because it really ought to be
|
|
1747
|
-
// impossible, but I can't prove it yet, so this is here
|
|
1748
|
-
// for safety.
|
|
1749
|
-
/* istanbul ignore if - should be impossible */
|
|
1750
|
-
if (edge.from === target)
|
|
1751
|
-
continue OUTER
|
|
1752
|
-
// ignore this edge, it'll either be replaced or re-resolved
|
|
1753
|
-
if (currentPeerSet.has(edge.from))
|
|
1754
|
-
continue
|
|
1755
|
-
// ok, we care about this one.
|
|
1756
|
-
deps.add(edge.from)
|
|
1757
|
-
}
|
|
1758
|
-
reresolve.add(node)
|
|
1759
|
-
for (const d of deps)
|
|
1760
|
-
dependants.add(d)
|
|
1761
|
-
}
|
|
1762
|
-
for (const dependant of dependants) {
|
|
1763
|
-
this[_depsQueue].push(dependant)
|
|
1764
|
-
this[_depsSeen].delete(dependant)
|
|
1765
|
-
}
|
|
1766
|
-
for (const node of reresolve)
|
|
1767
|
-
node.root = null
|
|
1768
|
-
}
|
|
1769
|
-
}
|
|
1770
|
-
}
|
|
1771
|
-
|
|
1772
|
-
if (canReplace) {
|
|
1773
|
-
const ret = this[_canPlacePeers](dep, target, edge, REPLACE, peerEntryEdge, peerPath, isSource)
|
|
1774
|
-
/* istanbul ignore else - extremely rare that the peer set would
|
|
1775
|
-
* conflict if we can replace the node in question, but theoretically
|
|
1776
|
-
* possible, if peer deps are pinned aggressively. */
|
|
1777
|
-
if (ret !== CONFLICT)
|
|
1778
|
-
return ret
|
|
1779
|
-
}
|
|
1780
|
-
|
|
1781
|
-
// so it's not a deeper dep that's been deduped. That means that the
|
|
1782
|
-
// only way it could have ended up here is if it's a conflicted peer.
|
|
1783
|
-
/* istanbul ignore else - would have already crashed if not forced,
|
|
1784
|
-
* and either mine or strict, when creating the peerSet. Keeping this
|
|
1785
|
-
* check so that we're not only relying on action at a distance. */
|
|
1786
|
-
if (!this[_strictPeerDeps] && !isMine || this[_force]) {
|
|
1787
|
-
this[_warnPeerConflict](edge, dep)
|
|
1788
|
-
return KEEP
|
|
1789
|
-
}
|
|
1790
|
-
}
|
|
1791
|
-
|
|
1792
|
-
// no justification for overriding, and no agreement possible.
|
|
1793
|
-
return CONFLICT
|
|
1794
|
-
}
|
|
1795
|
-
|
|
1796
|
-
// no existing node at this location!
|
|
1797
|
-
// check to see if the target doesn't have a child by that name,
|
|
1798
|
-
// but WANTS one, and won't be happy with this one. if this is the
|
|
1799
|
-
// edge we're looking to resolve, then not relevant, of course.
|
|
1800
|
-
if (target !== entryEdge.from && target.edgesOut.has(dep.name)) {
|
|
1801
|
-
const targetEdge = target.edgesOut.get(dep.name)
|
|
1802
|
-
// It might be that the dep would not be valid here, BUT some other
|
|
1803
|
-
// version would. Could to try to resolve that, but that makes this no
|
|
1804
|
-
// longer a pure synchronous function. ugh.
|
|
1805
|
-
// This is a pretty unlikely scenario in a normal install, because we
|
|
1806
|
-
// resolve the peer dep set against the parent dependencies, and
|
|
1807
|
-
// presumably they all worked together SOMEWHERE to get published in the
|
|
1808
|
-
// first place, and since we resolve shallower deps before deeper ones,
|
|
1809
|
-
// this can only occur by a child having a peer dep that does not satisfy
|
|
1810
|
-
// the parent. It can happen if we're doing a deep update limited by
|
|
1811
|
-
// a specific name, however, or if a dep makes an incompatible change
|
|
1812
|
-
// to its peer dep in a non-semver-major version bump, or if the parent
|
|
1813
|
-
// is unbounded in its dependency list.
|
|
1814
|
-
if (!targetEdge.satisfiedBy(dep))
|
|
1815
|
-
return CONFLICT
|
|
1816
|
-
}
|
|
1817
|
-
|
|
1818
|
-
// check to see what that name resolves to here, and who may depend on
|
|
1819
|
-
// being able to reach it by crawling up past this parent. we know
|
|
1820
|
-
// at this point that it's not the target's direct child node. if it's
|
|
1821
|
-
// a direct dep of the target, we just make the invalid edge and
|
|
1822
|
-
// resolve it later.
|
|
1823
|
-
const current = target !== entryEdge.from && target.resolve(dep.name)
|
|
1824
|
-
if (current) {
|
|
1825
|
-
for (const edge of current.edgesIn.values()) {
|
|
1826
|
-
if (!edge.from.isTop && edge.from.isDescendantOf(target) && edge.valid) {
|
|
1827
|
-
if (!edge.satisfiedBy(dep))
|
|
1828
|
-
return CONFLICT
|
|
1829
|
-
}
|
|
1830
|
-
}
|
|
1831
|
-
}
|
|
1832
|
-
|
|
1833
|
-
// no objections! ok to place here
|
|
1834
|
-
return this[_canPlacePeers](dep, target, edge, OK, peerEntryEdge, peerPath, isSource)
|
|
1835
|
-
}
|
|
1836
|
-
|
|
1837
|
-
// make sure the family of peer deps can live here alongside it.
|
|
1838
|
-
// this doesn't guarantee that THIS solution will be the one we take,
|
|
1839
|
-
// but it does establish that SOME solution exists at this level in
|
|
1840
|
-
// the tree.
|
|
1841
|
-
[_canPlacePeers] (dep, target, edge, ret, peerEntryEdge, peerPath, isSource) {
|
|
1842
|
-
// do not go in cycles when we're resolving a peer group
|
|
1843
|
-
if (!dep.parent || peerEntryEdge && peerPath.includes(dep))
|
|
1844
|
-
return ret
|
|
1845
|
-
|
|
1846
|
-
const entryEdge = peerEntryEdge || edge
|
|
1847
|
-
peerPath = [...peerPath, dep]
|
|
1848
|
-
|
|
1849
|
-
for (const peerEdge of dep.edgesOut.values()) {
|
|
1850
|
-
if (!peerEdge.peer || !peerEdge.to)
|
|
1851
|
-
continue
|
|
1852
|
-
const peer = peerEdge.to
|
|
1853
|
-
const canPlacePeer = this[_canPlaceDep](peer, target, peerEdge, entryEdge, peerPath, isSource)
|
|
1854
|
-
if (canPlacePeer !== CONFLICT)
|
|
1855
|
-
continue
|
|
1856
|
-
|
|
1857
|
-
const current = target.resolve(peer.name)
|
|
1858
|
-
this[_peerConflict] = {
|
|
1859
|
-
peer: peer.explain(peerEdge),
|
|
1860
|
-
current: current && current.explain(),
|
|
1861
|
-
}
|
|
1862
|
-
return CONFLICT
|
|
1863
|
-
}
|
|
1864
|
-
return ret
|
|
1865
|
-
}
|
|
1866
|
-
|
|
1867
1347
|
// go through all the links in the this[_linkNodes] set
|
|
1868
1348
|
// for each one:
|
|
1869
1349
|
// - if outside the root, ignore it, assume it's fine, it's not our problem
|
|
@@ -1877,15 +1357,17 @@ This is a one-time fix-up, please be patient...
|
|
|
1877
1357
|
this[_linkNodes].delete(link)
|
|
1878
1358
|
|
|
1879
1359
|
// link we never ended up placing, skip it
|
|
1880
|
-
if (link.root !== this.idealTree)
|
|
1360
|
+
if (link.root !== this.idealTree) {
|
|
1881
1361
|
continue
|
|
1362
|
+
}
|
|
1882
1363
|
|
|
1883
1364
|
const tree = this.idealTree.target
|
|
1884
1365
|
const external = !link.target.isDescendantOf(tree)
|
|
1885
1366
|
|
|
1886
1367
|
// outside the root, somebody else's problem, ignore it
|
|
1887
|
-
if (external && !this[_follow])
|
|
1368
|
+
if (external && !this[_follow]) {
|
|
1888
1369
|
continue
|
|
1370
|
+
}
|
|
1889
1371
|
|
|
1890
1372
|
// didn't find a parent for it or it has not been seen yet
|
|
1891
1373
|
// so go ahead and process it.
|
|
@@ -1901,8 +1383,9 @@ This is a one-time fix-up, please be patient...
|
|
|
1901
1383
|
}
|
|
1902
1384
|
}
|
|
1903
1385
|
|
|
1904
|
-
if (this[_depsQueue].length)
|
|
1386
|
+
if (this[_depsQueue].length) {
|
|
1905
1387
|
return this[_buildDepStep]()
|
|
1388
|
+
}
|
|
1906
1389
|
}
|
|
1907
1390
|
|
|
1908
1391
|
[_fixDepFlags] () {
|
|
@@ -1917,8 +1400,9 @@ This is a one-time fix-up, please be patient...
|
|
|
1917
1400
|
// all set to true, and there can be nothing extraneous, so there's
|
|
1918
1401
|
// nothing to prune, because we built it from scratch. if we didn't
|
|
1919
1402
|
// add or remove anything, then also nothing to do.
|
|
1920
|
-
if (metaFromDisk && mutateTree)
|
|
1403
|
+
if (metaFromDisk && mutateTree) {
|
|
1921
1404
|
resetDepFlags(this.idealTree)
|
|
1405
|
+
}
|
|
1922
1406
|
|
|
1923
1407
|
// update all the dev/optional/etc flags in the tree
|
|
1924
1408
|
// either we started with a fresh tree, or we
|
|
@@ -1926,9 +1410,9 @@ This is a one-time fix-up, please be patient...
|
|
|
1926
1410
|
//
|
|
1927
1411
|
// if we started from a blank slate, or changed something, then
|
|
1928
1412
|
// the dep flags will be all set to true.
|
|
1929
|
-
if (!metaFromDisk || mutateTree)
|
|
1413
|
+
if (!metaFromDisk || mutateTree) {
|
|
1930
1414
|
calcDepFlags(this.idealTree)
|
|
1931
|
-
else {
|
|
1415
|
+
} else {
|
|
1932
1416
|
// otherwise just unset all the flags on the root node
|
|
1933
1417
|
// since they will sometimes have the default value
|
|
1934
1418
|
this.idealTree.extraneous = false
|
|
@@ -1943,24 +1427,29 @@ This is a one-time fix-up, please be patient...
|
|
|
1943
1427
|
// then the tree is suspect. Prune what is marked as extraneous.
|
|
1944
1428
|
// otherwise, don't bother.
|
|
1945
1429
|
const needPrune = metaFromDisk && (mutateTree || flagsSuspect)
|
|
1946
|
-
if (this[_prune] && needPrune)
|
|
1430
|
+
if (this[_prune] && needPrune) {
|
|
1947
1431
|
this[_idealTreePrune]()
|
|
1432
|
+
}
|
|
1433
|
+
|
|
1948
1434
|
process.emit('timeEnd', 'idealTree:fixDepFlags')
|
|
1949
1435
|
}
|
|
1950
1436
|
|
|
1951
1437
|
[_idealTreePrune] () {
|
|
1952
|
-
for (const node of this.idealTree.inventory.filter(n => n.extraneous))
|
|
1438
|
+
for (const node of this.idealTree.inventory.filter(n => n.extraneous)) {
|
|
1953
1439
|
node.parent = null
|
|
1440
|
+
}
|
|
1954
1441
|
}
|
|
1955
1442
|
|
|
1956
1443
|
[_pruneFailedOptional] () {
|
|
1957
1444
|
for (const node of this[_loadFailures]) {
|
|
1958
|
-
if (!node.optional)
|
|
1445
|
+
if (!node.optional) {
|
|
1959
1446
|
throw node.errors[0]
|
|
1447
|
+
}
|
|
1960
1448
|
|
|
1961
1449
|
const set = optionalSet(node)
|
|
1962
|
-
for (const node of set)
|
|
1450
|
+
for (const node of set) {
|
|
1963
1451
|
node.parent = null
|
|
1452
|
+
}
|
|
1964
1453
|
}
|
|
1965
1454
|
}
|
|
1966
1455
|
}
|