@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
package/lib/arborist/reify.js
CHANGED
|
@@ -5,11 +5,14 @@ const pacote = require('pacote')
|
|
|
5
5
|
const AuditReport = require('../audit-report.js')
|
|
6
6
|
const {subset, intersects} = require('semver')
|
|
7
7
|
const npa = require('npm-package-arg')
|
|
8
|
+
const debug = require('../debug.js')
|
|
9
|
+
const walkUp = require('walk-up-path')
|
|
8
10
|
|
|
9
11
|
const {dirname, resolve, relative} = require('path')
|
|
10
12
|
const {depth: dfwalk} = require('treeverse')
|
|
11
13
|
const fs = require('fs')
|
|
12
14
|
const {promisify} = require('util')
|
|
15
|
+
const lstat = promisify(fs.lstat)
|
|
13
16
|
const symlink = promisify(fs.symlink)
|
|
14
17
|
const mkdirp = require('mkdirp-infer-owner')
|
|
15
18
|
const justMkdirp = require('mkdirp')
|
|
@@ -18,6 +21,7 @@ const rimraf = promisify(require('rimraf'))
|
|
|
18
21
|
const PackageJson = require('@npmcli/package-json')
|
|
19
22
|
const packageContents = require('@npmcli/installed-package-contents')
|
|
20
23
|
const { checkEngine, checkPlatform } = require('npm-install-checks')
|
|
24
|
+
const _force = Symbol.for('force')
|
|
21
25
|
|
|
22
26
|
const treeCheck = require('../tree-check.js')
|
|
23
27
|
const relpath = require('../relpath.js')
|
|
@@ -50,6 +54,7 @@ const _createSparseTree = Symbol.for('createSparseTree')
|
|
|
50
54
|
const _loadShrinkwrapsAndUpdateTrees = Symbol.for('loadShrinkwrapsAndUpdateTrees')
|
|
51
55
|
const _shrinkwrapInflated = Symbol('shrinkwrapInflated')
|
|
52
56
|
const _bundleUnpacked = Symbol('bundleUnpacked')
|
|
57
|
+
const _bundleMissing = Symbol('bundleMissing')
|
|
53
58
|
const _reifyNode = Symbol.for('reifyNode')
|
|
54
59
|
const _extractOrLink = Symbol('extractOrLink')
|
|
55
60
|
// defined by rebuild mixin
|
|
@@ -74,8 +79,10 @@ const _copyIdealToActual = Symbol('copyIdealToActual')
|
|
|
74
79
|
const _addOmitsToTrashList = Symbol('addOmitsToTrashList')
|
|
75
80
|
const _packageLockOnly = Symbol('packageLockOnly')
|
|
76
81
|
const _dryRun = Symbol('dryRun')
|
|
82
|
+
const _validateNodeModules = Symbol('validateNodeModules')
|
|
83
|
+
const _nmValidated = Symbol('nmValidated')
|
|
77
84
|
const _validatePath = Symbol('validatePath')
|
|
78
|
-
const _reifyPackages = Symbol('reifyPackages')
|
|
85
|
+
const _reifyPackages = Symbol.for('reifyPackages')
|
|
79
86
|
|
|
80
87
|
const _omitDev = Symbol('omitDev')
|
|
81
88
|
const _omitOptional = Symbol('omitOptional')
|
|
@@ -83,8 +90,9 @@ const _omitPeer = Symbol('omitPeer')
|
|
|
83
90
|
|
|
84
91
|
const _global = Symbol.for('global')
|
|
85
92
|
|
|
93
|
+
const _pruneBundledMetadeps = Symbol('pruneBundledMetadeps')
|
|
94
|
+
|
|
86
95
|
// defined by Ideal mixin
|
|
87
|
-
const _pruneBundledMetadeps = Symbol.for('pruneBundledMetadeps')
|
|
88
96
|
const _resolvedAdd = Symbol.for('resolvedAdd')
|
|
89
97
|
const _usePackageLock = Symbol.for('usePackageLock')
|
|
90
98
|
const _formatPackageLock = Symbol.for('formatPackageLock')
|
|
@@ -112,6 +120,11 @@ module.exports = cls => class Reifier extends cls {
|
|
|
112
120
|
this[_sparseTreeDirs] = new Set()
|
|
113
121
|
this[_sparseTreeRoots] = new Set()
|
|
114
122
|
this[_trashList] = new Set()
|
|
123
|
+
// the nodes we unpack to read their bundles
|
|
124
|
+
this[_bundleUnpacked] = new Set()
|
|
125
|
+
// child nodes we'd EXPECT to be included in a bundle, but aren't
|
|
126
|
+
this[_bundleMissing] = new Set()
|
|
127
|
+
this[_nmValidated] = new Set()
|
|
115
128
|
}
|
|
116
129
|
|
|
117
130
|
// public method
|
|
@@ -145,19 +158,24 @@ module.exports = cls => class Reifier extends cls {
|
|
|
145
158
|
|
|
146
159
|
async [_validatePath] () {
|
|
147
160
|
// don't create missing dirs on dry runs
|
|
148
|
-
if (this[_packageLockOnly] || this[_dryRun])
|
|
161
|
+
if (this[_packageLockOnly] || this[_dryRun]) {
|
|
149
162
|
return
|
|
163
|
+
}
|
|
150
164
|
|
|
151
165
|
// we do NOT want to set ownership on this folder, especially
|
|
152
166
|
// recursively, because it can have other side effects to do that
|
|
153
167
|
// in a project directory. We just want to make it if it's missing.
|
|
154
168
|
await justMkdirp(resolve(this.path))
|
|
169
|
+
|
|
170
|
+
// do not allow the top-level node_modules to be a symlink
|
|
171
|
+
await this[_validateNodeModules](resolve(this.path, 'node_modules'))
|
|
155
172
|
}
|
|
156
173
|
|
|
157
174
|
async [_reifyPackages] () {
|
|
158
175
|
// we don't submit the audit report or write to disk on dry runs
|
|
159
|
-
if (this[_dryRun])
|
|
176
|
+
if (this[_dryRun]) {
|
|
160
177
|
return
|
|
178
|
+
}
|
|
161
179
|
|
|
162
180
|
if (this[_packageLockOnly]) {
|
|
163
181
|
// we already have the complete tree, so just audit it now,
|
|
@@ -204,8 +222,9 @@ module.exports = cls => class Reifier extends cls {
|
|
|
204
222
|
for (const action of actions) {
|
|
205
223
|
try {
|
|
206
224
|
await this[action]()
|
|
207
|
-
if (reifyTerminated)
|
|
225
|
+
if (reifyTerminated) {
|
|
208
226
|
throw reifyTerminated
|
|
227
|
+
}
|
|
209
228
|
} catch (er) {
|
|
210
229
|
await this[rollback](er)
|
|
211
230
|
/* istanbul ignore next - rollback throws, should never hit this */
|
|
@@ -217,8 +236,9 @@ module.exports = cls => class Reifier extends cls {
|
|
|
217
236
|
// no rollback for this one, just exit with the error, since the
|
|
218
237
|
// install completed and can't be safely recovered at this point.
|
|
219
238
|
await this[_removeTrash]()
|
|
220
|
-
if (reifyTerminated)
|
|
239
|
+
if (reifyTerminated) {
|
|
221
240
|
throw reifyTerminated
|
|
241
|
+
}
|
|
222
242
|
|
|
223
243
|
// done modifying the file system, no need to keep listening for sigs
|
|
224
244
|
removeHandler()
|
|
@@ -245,18 +265,21 @@ module.exports = cls => class Reifier extends cls {
|
|
|
245
265
|
filter: (node, kid) => {
|
|
246
266
|
// if it's not the project root, and we have no explicit requests,
|
|
247
267
|
// then we're already into a nested dep, so we keep it
|
|
248
|
-
if (this.explicitRequests.size === 0 || !node.isProjectRoot)
|
|
268
|
+
if (this.explicitRequests.size === 0 || !node.isProjectRoot) {
|
|
249
269
|
return true
|
|
270
|
+
}
|
|
250
271
|
|
|
251
272
|
// if we added it as an edgeOut, then we want it
|
|
252
|
-
if (this.idealTree.edgesOut.has(kid))
|
|
273
|
+
if (this.idealTree.edgesOut.has(kid)) {
|
|
253
274
|
return true
|
|
275
|
+
}
|
|
254
276
|
|
|
255
277
|
// if it's an explicit request, then we want it
|
|
256
278
|
const hasExplicit = [...this.explicitRequests]
|
|
257
279
|
.some(edge => edge.name === kid)
|
|
258
|
-
if (hasExplicit)
|
|
280
|
+
if (hasExplicit) {
|
|
259
281
|
return true
|
|
282
|
+
}
|
|
260
283
|
|
|
261
284
|
// ignore the rest of the global install folder
|
|
262
285
|
return false
|
|
@@ -264,8 +287,10 @@ module.exports = cls => class Reifier extends cls {
|
|
|
264
287
|
} : { ignoreMissing: true }
|
|
265
288
|
|
|
266
289
|
if (!this[_global]) {
|
|
267
|
-
return Promise.all([
|
|
268
|
-
.
|
|
290
|
+
return Promise.all([
|
|
291
|
+
this.loadActual(actualOpt),
|
|
292
|
+
this.buildIdealTree(bitOpt),
|
|
293
|
+
]).then(() => process.emit('timeEnd', 'reify:loadTrees'))
|
|
269
294
|
}
|
|
270
295
|
|
|
271
296
|
// the global install space tends to have a lot of stuff in it. don't
|
|
@@ -279,8 +304,9 @@ module.exports = cls => class Reifier extends cls {
|
|
|
279
304
|
}
|
|
280
305
|
|
|
281
306
|
[_diffTrees] () {
|
|
282
|
-
if (this[_packageLockOnly])
|
|
307
|
+
if (this[_packageLockOnly]) {
|
|
283
308
|
return
|
|
309
|
+
}
|
|
284
310
|
|
|
285
311
|
process.emit('time', 'reify:diffTrees')
|
|
286
312
|
// XXX if we have an existing diff already, there should be a way
|
|
@@ -295,20 +321,24 @@ module.exports = cls => class Reifier extends cls {
|
|
|
295
321
|
// children where there's an explicit request.
|
|
296
322
|
for (const { name } of this.explicitRequests) {
|
|
297
323
|
const ideal = idealTree.children.get(name)
|
|
298
|
-
if (ideal)
|
|
324
|
+
if (ideal) {
|
|
299
325
|
filterNodes.push(ideal)
|
|
326
|
+
}
|
|
300
327
|
const actual = actualTree.children.get(name)
|
|
301
|
-
if (actual)
|
|
328
|
+
if (actual) {
|
|
302
329
|
filterNodes.push(actual)
|
|
330
|
+
}
|
|
303
331
|
}
|
|
304
332
|
} else {
|
|
305
333
|
for (const ws of this[_workspaces]) {
|
|
306
334
|
const ideal = this.idealTree.children.get(ws)
|
|
307
|
-
if (ideal)
|
|
335
|
+
if (ideal) {
|
|
308
336
|
filterNodes.push(ideal)
|
|
337
|
+
}
|
|
309
338
|
const actual = this.actualTree.children.get(ws)
|
|
310
|
-
if (actual)
|
|
339
|
+
if (actual) {
|
|
311
340
|
filterNodes.push(actual)
|
|
341
|
+
}
|
|
312
342
|
}
|
|
313
343
|
}
|
|
314
344
|
|
|
@@ -321,12 +351,13 @@ module.exports = cls => class Reifier extends cls {
|
|
|
321
351
|
ideal: this.idealTree,
|
|
322
352
|
})
|
|
323
353
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
354
|
+
// we don't have to add 'removed' folders to the trashlist, because
|
|
355
|
+
// they'll be moved aside to a retirement folder, and then the retired
|
|
356
|
+
// folder will be deleted at the end. This is important when we have
|
|
357
|
+
// a folder like FOO being "removed" in favor of a folder like "foo",
|
|
358
|
+
// because if we remove node_modules/FOO on case-insensitive systems,
|
|
359
|
+
// it will remove the dep that we *want* at node_modules/foo.
|
|
360
|
+
|
|
330
361
|
process.emit('timeEnd', 'reify:diffTrees')
|
|
331
362
|
}
|
|
332
363
|
|
|
@@ -334,7 +365,7 @@ module.exports = cls => class Reifier extends cls {
|
|
|
334
365
|
// removed later on in the process. optionally, also mark them
|
|
335
366
|
// as a retired paths, so that we move them out of the way and
|
|
336
367
|
// replace them when rolling back on failure.
|
|
337
|
-
[_addNodeToTrashList] (node, retire) {
|
|
368
|
+
[_addNodeToTrashList] (node, retire = false) {
|
|
338
369
|
const paths = [node.path, ...node.binPaths]
|
|
339
370
|
const moves = this[_retiredPaths]
|
|
340
371
|
this.log.silly('reify', 'mark', retire ? 'retired' : 'deleted', paths)
|
|
@@ -343,8 +374,9 @@ module.exports = cls => class Reifier extends cls {
|
|
|
343
374
|
const retired = retirePath(path)
|
|
344
375
|
moves[path] = retired
|
|
345
376
|
this[_trashList].add(retired)
|
|
346
|
-
} else
|
|
377
|
+
} else {
|
|
347
378
|
this[_trashList].add(path)
|
|
379
|
+
}
|
|
348
380
|
}
|
|
349
381
|
}
|
|
350
382
|
|
|
@@ -376,10 +408,11 @@ module.exports = cls => class Reifier extends cls {
|
|
|
376
408
|
if (er.code === 'ENOENT') {
|
|
377
409
|
return didMkdirp ? null : mkdirp(dirname(to)).then(() =>
|
|
378
410
|
this[_renamePath](from, to, true))
|
|
379
|
-
} else if (er.code === 'EEXIST')
|
|
411
|
+
} else if (er.code === 'EEXIST') {
|
|
380
412
|
return rimraf(to).then(() => moveFile(from, to))
|
|
381
|
-
else
|
|
413
|
+
} else {
|
|
382
414
|
throw er
|
|
415
|
+
}
|
|
383
416
|
})
|
|
384
417
|
}
|
|
385
418
|
|
|
@@ -400,8 +433,9 @@ module.exports = cls => class Reifier extends cls {
|
|
|
400
433
|
// adding to the trash list will skip reifying, and delete them
|
|
401
434
|
// if they are currently in the tree and otherwise untouched.
|
|
402
435
|
[_addOmitsToTrashList] () {
|
|
403
|
-
if (!this[_omitDev] && !this[_omitOptional] && !this[_omitPeer])
|
|
436
|
+
if (!this[_omitDev] && !this[_omitOptional] && !this[_omitPeer]) {
|
|
404
437
|
return
|
|
438
|
+
}
|
|
405
439
|
|
|
406
440
|
process.emit('time', 'reify:trashOmits')
|
|
407
441
|
|
|
@@ -412,8 +446,9 @@ module.exports = cls => class Reifier extends cls {
|
|
|
412
446
|
node.optional && this[_omitOptional] ||
|
|
413
447
|
node.devOptional && this[_omitOptional] && this[_omitDev])
|
|
414
448
|
|
|
415
|
-
for (const node of this.idealTree.inventory.filter(filter))
|
|
449
|
+
for (const node of this.idealTree.inventory.filter(filter)) {
|
|
416
450
|
this[_addNodeToTrashList](node)
|
|
451
|
+
}
|
|
417
452
|
|
|
418
453
|
process.emit('timeEnd', 'reify:trashOmits')
|
|
419
454
|
}
|
|
@@ -422,19 +457,42 @@ module.exports = cls => class Reifier extends cls {
|
|
|
422
457
|
process.emit('time', 'reify:createSparse')
|
|
423
458
|
// if we call this fn again, we look for the previous list
|
|
424
459
|
// so that we can avoid making the same directory multiple times
|
|
425
|
-
const
|
|
460
|
+
const leaves = this.diff.leaves
|
|
426
461
|
.filter(diff => {
|
|
427
462
|
return (diff.action === 'ADD' || diff.action === 'CHANGE') &&
|
|
428
463
|
!this[_sparseTreeDirs].has(diff.ideal.path) &&
|
|
429
464
|
!diff.ideal.isLink
|
|
430
465
|
})
|
|
431
|
-
.map(diff => diff.ideal
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
466
|
+
.map(diff => diff.ideal)
|
|
467
|
+
|
|
468
|
+
// we check this in parallel, so guard against multiple attempts to
|
|
469
|
+
// retire the same path at the same time.
|
|
470
|
+
const dirsChecked = new Set()
|
|
471
|
+
return promiseAllRejectLate(leaves.map(async node => {
|
|
472
|
+
for (const d of walkUp(node.path)) {
|
|
473
|
+
if (d === node.top.path) {
|
|
474
|
+
break
|
|
475
|
+
}
|
|
476
|
+
if (dirsChecked.has(d)) {
|
|
477
|
+
continue
|
|
478
|
+
}
|
|
479
|
+
dirsChecked.add(d)
|
|
480
|
+
const st = await lstat(d).catch(er => null)
|
|
481
|
+
// this can happen if we have a link to a package with a name
|
|
482
|
+
// that the filesystem treats as if it is the same thing.
|
|
483
|
+
// would be nice to have conditional istanbul ignores here...
|
|
484
|
+
/* istanbul ignore next - defense in depth */
|
|
485
|
+
if (st && !st.isDirectory()) {
|
|
486
|
+
const retired = retirePath(d)
|
|
487
|
+
this[_retiredPaths][d] = retired
|
|
488
|
+
this[_trashList].add(retired)
|
|
489
|
+
await this[_renamePath](d, retired)
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
const made = await mkdirp(node.path)
|
|
493
|
+
this[_sparseTreeDirs].add(node.path)
|
|
494
|
+
this[_sparseTreeRoots].add(made)
|
|
495
|
+
}))
|
|
438
496
|
.then(() => process.emit('timeEnd', 'reify:createSparse'))
|
|
439
497
|
}
|
|
440
498
|
|
|
@@ -449,8 +507,9 @@ module.exports = cls => class Reifier extends cls {
|
|
|
449
507
|
.map(path => rimraf(path).catch(er => failures.push([path, er])))
|
|
450
508
|
return promiseAllRejectLate(unlinks)
|
|
451
509
|
.then(() => {
|
|
452
|
-
if (failures.length)
|
|
510
|
+
if (failures.length) {
|
|
453
511
|
this.log.warn('cleanup', 'Failed to remove some directories', failures)
|
|
512
|
+
}
|
|
454
513
|
})
|
|
455
514
|
.then(() => process.emit('timeEnd', 'reify:rollback:createSparse'))
|
|
456
515
|
.then(() => this[_rollbackRetireShallowNodes](er))
|
|
@@ -466,8 +525,9 @@ module.exports = cls => class Reifier extends cls {
|
|
|
466
525
|
d.ideal.hasShrinkwrap && !seen.has(d.ideal) &&
|
|
467
526
|
!this[_trashList].has(d.ideal.path))
|
|
468
527
|
|
|
469
|
-
if (!shrinkwraps.length)
|
|
528
|
+
if (!shrinkwraps.length) {
|
|
470
529
|
return
|
|
530
|
+
}
|
|
471
531
|
|
|
472
532
|
process.emit('time', 'reify:loadShrinkwraps')
|
|
473
533
|
|
|
@@ -497,8 +557,9 @@ module.exports = cls => class Reifier extends cls {
|
|
|
497
557
|
// to the trash list
|
|
498
558
|
// Always return the node.
|
|
499
559
|
[_reifyNode] (node) {
|
|
500
|
-
if (this[_trashList].has(node.path))
|
|
560
|
+
if (this[_trashList].has(node.path)) {
|
|
501
561
|
return node
|
|
562
|
+
}
|
|
502
563
|
|
|
503
564
|
const timer = `reifyNode:${node.location}`
|
|
504
565
|
process.emit('time', timer)
|
|
@@ -529,7 +590,21 @@ module.exports = cls => class Reifier extends cls {
|
|
|
529
590
|
})
|
|
530
591
|
}
|
|
531
592
|
|
|
532
|
-
|
|
593
|
+
// do not allow node_modules to be a symlink
|
|
594
|
+
async [_validateNodeModules] (nm) {
|
|
595
|
+
if (this[_force] || this[_nmValidated].has(nm)) {
|
|
596
|
+
return
|
|
597
|
+
}
|
|
598
|
+
const st = await lstat(nm).catch(() => null)
|
|
599
|
+
if (!st || st.isDirectory()) {
|
|
600
|
+
this[_nmValidated].add(nm)
|
|
601
|
+
return
|
|
602
|
+
}
|
|
603
|
+
this.log.warn('reify', 'Removing non-directory', nm)
|
|
604
|
+
await rimraf(nm)
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
async [_extractOrLink] (node) {
|
|
533
608
|
// in normal cases, node.resolved should *always* be set by now.
|
|
534
609
|
// however, it is possible when a lockfile is damaged, or very old,
|
|
535
610
|
// or in some other race condition bugs in npm v6, that a previously
|
|
@@ -556,13 +631,29 @@ module.exports = cls => class Reifier extends cls {
|
|
|
556
631
|
return
|
|
557
632
|
}
|
|
558
633
|
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
634
|
+
const nm = resolve(node.parent.path, 'node_modules')
|
|
635
|
+
await this[_validateNodeModules](nm)
|
|
636
|
+
|
|
637
|
+
if (node.isLink) {
|
|
638
|
+
await rimraf(node.path)
|
|
639
|
+
await this[_symlink](node)
|
|
640
|
+
} else {
|
|
641
|
+
await debug(async () => {
|
|
642
|
+
const st = await lstat(node.path).catch(e => null)
|
|
643
|
+
if (st && !st.isDirectory()) {
|
|
644
|
+
debug.log('unpacking into a non-directory', node)
|
|
645
|
+
throw Object.assign(new Error('ENOTDIR: not a directory'), {
|
|
646
|
+
code: 'ENOTDIR',
|
|
647
|
+
path: node.path,
|
|
648
|
+
})
|
|
649
|
+
}
|
|
650
|
+
})
|
|
651
|
+
await pacote.extract(res, node.path, {
|
|
562
652
|
...this.options,
|
|
563
653
|
resolved: node.resolved,
|
|
564
654
|
integrity: node.integrity,
|
|
565
655
|
})
|
|
656
|
+
}
|
|
566
657
|
}
|
|
567
658
|
|
|
568
659
|
async [_symlink] (node) {
|
|
@@ -575,8 +666,9 @@ module.exports = cls => class Reifier extends cls {
|
|
|
575
666
|
|
|
576
667
|
[_warnDeprecated] (node) {
|
|
577
668
|
const {_id, deprecated} = node.package
|
|
578
|
-
if (deprecated)
|
|
669
|
+
if (deprecated) {
|
|
579
670
|
this.log.warn('deprecated', `${_id}: ${deprecated}`)
|
|
671
|
+
}
|
|
580
672
|
}
|
|
581
673
|
|
|
582
674
|
// if the node is optional, then the failure of the promise is nonfatal
|
|
@@ -611,9 +703,9 @@ module.exports = cls => class Reifier extends cls {
|
|
|
611
703
|
depth = 0, bundlesByDepth = this[_getBundlesByDepth]()
|
|
612
704
|
) {
|
|
613
705
|
if (depth === 0) {
|
|
614
|
-
this[_bundleUnpacked] = new Set()
|
|
615
706
|
process.emit('time', 'reify:loadBundles')
|
|
616
707
|
}
|
|
708
|
+
|
|
617
709
|
const maxBundleDepth = bundlesByDepth.get('maxBundleDepth')
|
|
618
710
|
if (depth > maxBundleDepth) {
|
|
619
711
|
// if we did something, then prune the tree and update the diffs
|
|
@@ -632,8 +724,9 @@ module.exports = cls => class Reifier extends cls {
|
|
|
632
724
|
node.target !== node.root &&
|
|
633
725
|
!this[_trashList].has(node.path))
|
|
634
726
|
|
|
635
|
-
if (!set.length)
|
|
727
|
+
if (!set.length) {
|
|
636
728
|
return this[_loadBundlesAndUpdateTrees](depth + 1, bundlesByDepth)
|
|
729
|
+
}
|
|
637
730
|
|
|
638
731
|
// extract all the nodes with bundles
|
|
639
732
|
return promiseAllRejectLate(set.map(node => {
|
|
@@ -642,14 +735,32 @@ module.exports = cls => class Reifier extends cls {
|
|
|
642
735
|
}))
|
|
643
736
|
// then load their unpacked children and move into the ideal tree
|
|
644
737
|
.then(nodes =>
|
|
645
|
-
promiseAllRejectLate(nodes.map(node =>
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
738
|
+
promiseAllRejectLate(nodes.map(async node => {
|
|
739
|
+
const arb = new this.constructor({
|
|
740
|
+
...this.options,
|
|
741
|
+
path: node.path,
|
|
742
|
+
})
|
|
743
|
+
const notTransplanted = new Set(node.children.keys())
|
|
744
|
+
await arb.loadActual({
|
|
745
|
+
root: node,
|
|
746
|
+
// don't transplant any sparse folders we created
|
|
747
|
+
// loadActual will set node.package to {} for empty directories
|
|
748
|
+
// if by chance there are some empty folders in the node_modules
|
|
749
|
+
// tree for some other reason, then ok, ignore those too.
|
|
750
|
+
transplantFilter: node => {
|
|
751
|
+
if (node.package._id) {
|
|
752
|
+
// it's actually in the bundle if it gets transplanted
|
|
753
|
+
notTransplanted.delete(node.name)
|
|
754
|
+
return true
|
|
755
|
+
} else {
|
|
756
|
+
return false
|
|
757
|
+
}
|
|
758
|
+
},
|
|
759
|
+
})
|
|
760
|
+
for (const name of notTransplanted) {
|
|
761
|
+
this[_bundleMissing].add(node.children.get(name))
|
|
762
|
+
}
|
|
763
|
+
})))
|
|
653
764
|
// move onto the next level of bundled items
|
|
654
765
|
.then(() => this[_loadBundlesAndUpdateTrees](depth + 1, bundlesByDepth))
|
|
655
766
|
}
|
|
@@ -661,18 +772,21 @@ module.exports = cls => class Reifier extends cls {
|
|
|
661
772
|
tree: this.diff,
|
|
662
773
|
visit: diff => {
|
|
663
774
|
const node = diff.ideal
|
|
664
|
-
if (!node)
|
|
775
|
+
if (!node) {
|
|
665
776
|
return
|
|
666
|
-
|
|
777
|
+
}
|
|
778
|
+
if (node.isProjectRoot) {
|
|
667
779
|
return
|
|
780
|
+
}
|
|
668
781
|
|
|
669
782
|
const { bundleDependencies } = node.package
|
|
670
783
|
if (bundleDependencies && bundleDependencies.length) {
|
|
671
784
|
maxBundleDepth = Math.max(maxBundleDepth, node.depth)
|
|
672
|
-
if (!bundlesByDepth.has(node.depth))
|
|
785
|
+
if (!bundlesByDepth.has(node.depth)) {
|
|
673
786
|
bundlesByDepth.set(node.depth, [node])
|
|
674
|
-
else
|
|
787
|
+
} else {
|
|
675
788
|
bundlesByDepth.get(node.depth).push(node)
|
|
789
|
+
}
|
|
676
790
|
}
|
|
677
791
|
},
|
|
678
792
|
getChildren: diff => diff.children,
|
|
@@ -685,57 +799,95 @@ module.exports = cls => class Reifier extends cls {
|
|
|
685
799
|
// https://github.com/npm/cli/issues/1597#issuecomment-667639545
|
|
686
800
|
[_pruneBundledMetadeps] (bundlesByDepth) {
|
|
687
801
|
const bundleShadowed = new Set()
|
|
802
|
+
|
|
803
|
+
// Example dep graph:
|
|
804
|
+
// root -> (a, c)
|
|
805
|
+
// a -> BUNDLE(b)
|
|
806
|
+
// b -> c
|
|
807
|
+
// c -> b
|
|
808
|
+
//
|
|
809
|
+
// package tree:
|
|
810
|
+
// root
|
|
811
|
+
// +-- a
|
|
812
|
+
// | +-- b(1)
|
|
813
|
+
// | +-- c(1)
|
|
814
|
+
// +-- b(2)
|
|
815
|
+
// +-- c(2)
|
|
816
|
+
// 1. mark everything that's shadowed by anything in the bundle. This
|
|
817
|
+
// marks b(2) and c(2).
|
|
818
|
+
// 2. anything with edgesIn from outside the set, mark not-extraneous,
|
|
819
|
+
// remove from set. This unmarks c(2).
|
|
820
|
+
// 3. continue until no change
|
|
821
|
+
// 4. remove everything in the set from the tree. b(2) is pruned
|
|
822
|
+
|
|
688
823
|
// create the list of nodes shadowed by children of bundlers
|
|
689
824
|
for (const bundles of bundlesByDepth.values()) {
|
|
690
825
|
// skip the 'maxBundleDepth' item
|
|
691
|
-
if (!Array.isArray(bundles))
|
|
826
|
+
if (!Array.isArray(bundles)) {
|
|
692
827
|
continue
|
|
828
|
+
}
|
|
693
829
|
for (const node of bundles) {
|
|
694
830
|
for (const name of node.children.keys()) {
|
|
695
831
|
const shadow = node.parent.resolve(name)
|
|
696
|
-
if (!shadow)
|
|
832
|
+
if (!shadow) {
|
|
697
833
|
continue
|
|
834
|
+
}
|
|
698
835
|
bundleShadowed.add(shadow)
|
|
699
836
|
shadow.extraneous = true
|
|
700
837
|
}
|
|
701
838
|
}
|
|
702
839
|
}
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
840
|
+
|
|
841
|
+
// lib -> (a@1.x) BUNDLE(a@1.2.3 (b@1.2.3))
|
|
842
|
+
// a@1.2.3 -> (b@1.2.3)
|
|
843
|
+
// a@1.3.0 -> (b@2)
|
|
844
|
+
// b@1.2.3 -> ()
|
|
845
|
+
// b@2 -> (c@2)
|
|
846
|
+
//
|
|
847
|
+
// root
|
|
848
|
+
// +-- lib
|
|
849
|
+
// | +-- a@1.2.3
|
|
850
|
+
// | +-- b@1.2.3
|
|
851
|
+
// +-- b@2 <-- shadowed, now extraneous
|
|
852
|
+
// +-- c@2 <-- also shadowed, because only dependent is shadowed
|
|
853
|
+
for (const shadow of bundleShadowed) {
|
|
854
|
+
for (const shadDep of shadow.edgesOut.values()) {
|
|
855
|
+
/* istanbul ignore else - pretty unusual situation, just being
|
|
856
|
+
* defensive here. Would mean that a bundled dep has a dependency
|
|
857
|
+
* that is unmet. which, weird, but if you bundle it, we take
|
|
858
|
+
* whatever you put there and assume the publisher knows best. */
|
|
859
|
+
if (shadDep.to) {
|
|
860
|
+
bundleShadowed.add(shadDep.to)
|
|
861
|
+
shadDep.to.extraneous = true
|
|
710
862
|
}
|
|
863
|
+
}
|
|
864
|
+
}
|
|
711
865
|
|
|
866
|
+
let changed
|
|
867
|
+
do {
|
|
868
|
+
changed = false
|
|
869
|
+
for (const shadow of bundleShadowed) {
|
|
712
870
|
for (const edge of shadow.edgesIn) {
|
|
713
|
-
if (!edge.from
|
|
871
|
+
if (!bundleShadowed.has(edge.from)) {
|
|
714
872
|
shadow.extraneous = false
|
|
715
873
|
bundleShadowed.delete(shadow)
|
|
716
874
|
changed = true
|
|
717
|
-
|
|
718
|
-
for (const shadDep of shadow.edgesOut.values()) {
|
|
719
|
-
/* istanbul ignore else - pretty unusual situation, just being
|
|
720
|
-
* defensive here. Would mean that a bundled dep has a dependency
|
|
721
|
-
* that is unmet. which, weird, but if you bundle it, we take
|
|
722
|
-
* whatever you put there and assume the publisher knows best. */
|
|
723
|
-
if (shadDep.to)
|
|
724
|
-
bundleShadowed.add(shadDep.to)
|
|
725
|
-
}
|
|
875
|
+
break
|
|
726
876
|
}
|
|
727
877
|
}
|
|
728
878
|
}
|
|
729
|
-
}
|
|
879
|
+
} while (changed)
|
|
880
|
+
|
|
730
881
|
for (const shadow of bundleShadowed) {
|
|
731
|
-
shadow.parent = null
|
|
732
882
|
this[_addNodeToTrashList](shadow)
|
|
883
|
+
shadow.root = null
|
|
733
884
|
}
|
|
734
885
|
}
|
|
735
886
|
|
|
736
887
|
[_submitQuickAudit] () {
|
|
737
|
-
if (this.options.audit === false)
|
|
888
|
+
if (this.options.audit === false) {
|
|
738
889
|
return this.auditReport = null
|
|
890
|
+
}
|
|
739
891
|
|
|
740
892
|
// we submit the quick audit at this point in the process, as soon as
|
|
741
893
|
// we have all the deps resolved, so that it can overlap with the other
|
|
@@ -748,8 +900,9 @@ module.exports = cls => class Reifier extends cls {
|
|
|
748
900
|
const tree = this.idealTree
|
|
749
901
|
|
|
750
902
|
// if we're operating on a workspace, only audit the workspace deps
|
|
751
|
-
if (this[_workspaces] && this[_workspaces].length)
|
|
903
|
+
if (this[_workspaces] && this[_workspaces].length) {
|
|
752
904
|
options.filterSet = this.workspaceDependencySet(tree, this[_workspaces])
|
|
905
|
+
}
|
|
753
906
|
|
|
754
907
|
this.auditReport = AuditReport.load(tree, options)
|
|
755
908
|
.then(res => {
|
|
@@ -774,23 +927,30 @@ module.exports = cls => class Reifier extends cls {
|
|
|
774
927
|
tree: this.diff,
|
|
775
928
|
visit: diff => {
|
|
776
929
|
// no unpacking if we don't want to change this thing
|
|
777
|
-
if (diff.action !== 'CHANGE' && diff.action !== 'ADD')
|
|
930
|
+
if (diff.action !== 'CHANGE' && diff.action !== 'ADD') {
|
|
778
931
|
return
|
|
932
|
+
}
|
|
779
933
|
|
|
780
934
|
const node = diff.ideal
|
|
781
935
|
const bd = this[_bundleUnpacked].has(node)
|
|
782
936
|
const sw = this[_shrinkwrapInflated].has(node)
|
|
937
|
+
const bundleMissing = this[_bundleMissing].has(node)
|
|
783
938
|
|
|
784
939
|
// check whether we still need to unpack this one.
|
|
785
940
|
// test the inDepBundle last, since that's potentially a tree walk.
|
|
786
941
|
const doUnpack = node && // can't unpack if removed!
|
|
787
|
-
|
|
788
|
-
!
|
|
789
|
-
|
|
790
|
-
!
|
|
791
|
-
|
|
792
|
-
|
|
942
|
+
// root node already exists
|
|
943
|
+
!node.isRoot &&
|
|
944
|
+
// already unpacked to read bundle
|
|
945
|
+
!bd &&
|
|
946
|
+
// already unpacked to read sw
|
|
947
|
+
!sw &&
|
|
948
|
+
// already unpacked by another dep's bundle
|
|
949
|
+
(bundleMissing || !node.inDepBundle)
|
|
950
|
+
|
|
951
|
+
if (doUnpack) {
|
|
793
952
|
unpacks.push(this[_reifyNode](node))
|
|
953
|
+
}
|
|
794
954
|
},
|
|
795
955
|
getChildren: diff => diff.children,
|
|
796
956
|
})
|
|
@@ -814,17 +974,38 @@ module.exports = cls => class Reifier extends cls {
|
|
|
814
974
|
const moves = this[_retiredPaths]
|
|
815
975
|
this[_retiredUnchanged] = {}
|
|
816
976
|
return promiseAllRejectLate(this.diff.children.map(diff => {
|
|
817
|
-
|
|
977
|
+
// skip if nothing was retired
|
|
978
|
+
if (diff.action !== 'CHANGE' && diff.action !== 'REMOVE') {
|
|
979
|
+
return
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
const { path: realFolder } = diff.actual
|
|
818
983
|
const retireFolder = moves[realFolder]
|
|
984
|
+
/* istanbul ignore next - should be impossible */
|
|
985
|
+
debug(() => {
|
|
986
|
+
if (!retireFolder) {
|
|
987
|
+
const er = new Error('trying to un-retire but not retired')
|
|
988
|
+
throw Object.assign(er, {
|
|
989
|
+
realFolder,
|
|
990
|
+
retireFolder,
|
|
991
|
+
actual: diff.actual,
|
|
992
|
+
ideal: diff.ideal,
|
|
993
|
+
action: diff.action,
|
|
994
|
+
})
|
|
995
|
+
}
|
|
996
|
+
})
|
|
997
|
+
|
|
819
998
|
this[_retiredUnchanged][retireFolder] = []
|
|
820
999
|
return promiseAllRejectLate(diff.unchanged.map(node => {
|
|
821
1000
|
// no need to roll back links, since we'll just delete them anyway
|
|
822
|
-
if (node.isLink)
|
|
1001
|
+
if (node.isLink) {
|
|
823
1002
|
return mkdirp(dirname(node.path)).then(() => this[_reifyNode](node))
|
|
1003
|
+
}
|
|
824
1004
|
|
|
825
1005
|
// will have been moved/unpacked along with bundler
|
|
826
|
-
if (node.inDepBundle)
|
|
1006
|
+
if (node.inDepBundle && !this[_bundleMissing].has(node)) {
|
|
827
1007
|
return
|
|
1008
|
+
}
|
|
828
1009
|
|
|
829
1010
|
this[_retiredUnchanged][retireFolder].push(node)
|
|
830
1011
|
|
|
@@ -878,8 +1059,9 @@ module.exports = cls => class Reifier extends cls {
|
|
|
878
1059
|
dfwalk({
|
|
879
1060
|
tree: this.diff,
|
|
880
1061
|
leave: diff => {
|
|
881
|
-
if (!diff.ideal.isProjectRoot)
|
|
1062
|
+
if (!diff.ideal.isProjectRoot) {
|
|
882
1063
|
nodes.push(diff.ideal)
|
|
1064
|
+
}
|
|
883
1065
|
},
|
|
884
1066
|
// process adds before changes, ignore removals
|
|
885
1067
|
getChildren: diff => diff && diff.children,
|
|
@@ -894,8 +1076,9 @@ module.exports = cls => class Reifier extends cls {
|
|
|
894
1076
|
// skip links that only live within node_modules as they are most
|
|
895
1077
|
// likely managed by packages we installed, we only want to rebuild
|
|
896
1078
|
// unchanged links we directly manage
|
|
897
|
-
if (node.isLink && node.target.fsTop === tree)
|
|
1079
|
+
if (node.isLink && node.target.fsTop === tree) {
|
|
898
1080
|
nodes.push(node)
|
|
1081
|
+
}
|
|
899
1082
|
}
|
|
900
1083
|
|
|
901
1084
|
return this.rebuild({ nodes, handleOptionalFailure: true })
|
|
@@ -912,12 +1095,14 @@ module.exports = cls => class Reifier extends cls {
|
|
|
912
1095
|
const failures = []
|
|
913
1096
|
const rm = path => rimraf(path).catch(er => failures.push([path, er]))
|
|
914
1097
|
|
|
915
|
-
for (const path of this[_trashList])
|
|
1098
|
+
for (const path of this[_trashList]) {
|
|
916
1099
|
promises.push(rm(path))
|
|
1100
|
+
}
|
|
917
1101
|
|
|
918
1102
|
return promiseAllRejectLate(promises).then(() => {
|
|
919
|
-
if (failures.length)
|
|
1103
|
+
if (failures.length) {
|
|
920
1104
|
this.log.warn('cleanup', 'Failed to remove some directories', failures)
|
|
1105
|
+
}
|
|
921
1106
|
})
|
|
922
1107
|
.then(() => process.emit('timeEnd', 'reify:trash'))
|
|
923
1108
|
}
|
|
@@ -931,8 +1116,9 @@ module.exports = cls => class Reifier extends cls {
|
|
|
931
1116
|
// save it first, then prune out the optional trash, and then return it.
|
|
932
1117
|
|
|
933
1118
|
// support save=false option
|
|
934
|
-
if (options.save === false || this[_global] || this[_dryRun])
|
|
1119
|
+
if (options.save === false || this[_global] || this[_dryRun]) {
|
|
935
1120
|
return false
|
|
1121
|
+
}
|
|
936
1122
|
|
|
937
1123
|
process.emit('time', 'reify:save')
|
|
938
1124
|
|
|
@@ -953,6 +1139,14 @@ module.exports = cls => class Reifier extends cls {
|
|
|
953
1139
|
const spec = subSpec ? subSpec.rawSpec : rawSpec
|
|
954
1140
|
const child = edge.to
|
|
955
1141
|
|
|
1142
|
+
// if we tried to install an optional dep, but it was a version
|
|
1143
|
+
// that we couldn't resolve, this MAY be missing. if we haven't
|
|
1144
|
+
// blown up by now, it's because it was not a problem, though, so
|
|
1145
|
+
// just move on.
|
|
1146
|
+
if (!child) {
|
|
1147
|
+
continue
|
|
1148
|
+
}
|
|
1149
|
+
|
|
956
1150
|
let newSpec
|
|
957
1151
|
if (req.registry) {
|
|
958
1152
|
const version = child.version
|
|
@@ -969,8 +1163,9 @@ module.exports = cls => class Reifier extends cls {
|
|
|
969
1163
|
!isRange ||
|
|
970
1164
|
spec === '*' ||
|
|
971
1165
|
subset(prefixRange, spec, { loose: true })
|
|
972
|
-
)
|
|
1166
|
+
) {
|
|
973
1167
|
range = prefixRange
|
|
1168
|
+
}
|
|
974
1169
|
|
|
975
1170
|
const pname = child.packageName
|
|
976
1171
|
const alias = name !== pname
|
|
@@ -979,10 +1174,11 @@ module.exports = cls => class Reifier extends cls {
|
|
|
979
1174
|
// save the git+https url if it has auth, otherwise shortcut
|
|
980
1175
|
const h = req.hosted
|
|
981
1176
|
const opt = { noCommittish: false }
|
|
982
|
-
if (h.https && h.auth)
|
|
1177
|
+
if (h.https && h.auth) {
|
|
983
1178
|
newSpec = `git+${h.https(opt)}`
|
|
984
|
-
else
|
|
1179
|
+
} else {
|
|
985
1180
|
newSpec = h.shortcut(opt)
|
|
1181
|
+
}
|
|
986
1182
|
} else if (req.type === 'directory' || req.type === 'file') {
|
|
987
1183
|
// save the relative path in package.json
|
|
988
1184
|
// Normally saveSpec is updated with the proper relative
|
|
@@ -992,34 +1188,41 @@ module.exports = cls => class Reifier extends cls {
|
|
|
992
1188
|
const p = req.fetchSpec.replace(/^file:/, '')
|
|
993
1189
|
const rel = relpath(addTree.realpath, p)
|
|
994
1190
|
newSpec = `file:${rel}`
|
|
995
|
-
} else
|
|
1191
|
+
} else {
|
|
996
1192
|
newSpec = req.saveSpec
|
|
1193
|
+
}
|
|
997
1194
|
|
|
998
1195
|
if (options.saveType) {
|
|
999
1196
|
const depType = saveTypeMap.get(options.saveType)
|
|
1000
1197
|
pkg[depType][name] = newSpec
|
|
1001
1198
|
// rpj will have moved it here if it was in both
|
|
1002
1199
|
// if it is empty it will be deleted later
|
|
1003
|
-
if (options.saveType === 'prod' && pkg.optionalDependencies)
|
|
1200
|
+
if (options.saveType === 'prod' && pkg.optionalDependencies) {
|
|
1004
1201
|
delete pkg.optionalDependencies[name]
|
|
1202
|
+
}
|
|
1005
1203
|
} else {
|
|
1006
|
-
if (hasSubKey(pkg, 'dependencies', name))
|
|
1204
|
+
if (hasSubKey(pkg, 'dependencies', name)) {
|
|
1007
1205
|
pkg.dependencies[name] = newSpec
|
|
1206
|
+
}
|
|
1008
1207
|
|
|
1009
1208
|
if (hasSubKey(pkg, 'devDependencies', name)) {
|
|
1010
1209
|
pkg.devDependencies[name] = newSpec
|
|
1011
1210
|
// don't update peer or optional if we don't have to
|
|
1012
|
-
if (hasSubKey(pkg, 'peerDependencies', name) && !intersects(newSpec, pkg.peerDependencies[name]))
|
|
1211
|
+
if (hasSubKey(pkg, 'peerDependencies', name) && !intersects(newSpec, pkg.peerDependencies[name])) {
|
|
1013
1212
|
pkg.peerDependencies[name] = newSpec
|
|
1213
|
+
}
|
|
1014
1214
|
|
|
1015
|
-
if (hasSubKey(pkg, 'optionalDependencies', name) && !intersects(newSpec, pkg.optionalDependencies[name]))
|
|
1215
|
+
if (hasSubKey(pkg, 'optionalDependencies', name) && !intersects(newSpec, pkg.optionalDependencies[name])) {
|
|
1016
1216
|
pkg.optionalDependencies[name] = newSpec
|
|
1217
|
+
}
|
|
1017
1218
|
} else {
|
|
1018
|
-
if (hasSubKey(pkg, 'peerDependencies', name))
|
|
1219
|
+
if (hasSubKey(pkg, 'peerDependencies', name)) {
|
|
1019
1220
|
pkg.peerDependencies[name] = newSpec
|
|
1221
|
+
}
|
|
1020
1222
|
|
|
1021
|
-
if (hasSubKey(pkg, 'optionalDependencies', name))
|
|
1223
|
+
if (hasSubKey(pkg, 'optionalDependencies', name)) {
|
|
1022
1224
|
pkg.optionalDependencies[name] = newSpec
|
|
1225
|
+
}
|
|
1023
1226
|
}
|
|
1024
1227
|
}
|
|
1025
1228
|
|
|
@@ -1060,8 +1263,9 @@ module.exports = cls => class Reifier extends cls {
|
|
|
1060
1263
|
}
|
|
1061
1264
|
|
|
1062
1265
|
// grab any from explicitRequests that had deps removed
|
|
1063
|
-
for (const { from: tree } of this.explicitRequests)
|
|
1266
|
+
for (const { from: tree } of this.explicitRequests) {
|
|
1064
1267
|
updatedTrees.add(tree)
|
|
1268
|
+
}
|
|
1065
1269
|
|
|
1066
1270
|
for (const tree of updatedTrees) {
|
|
1067
1271
|
// refresh the edges so they have the correct specs
|
|
@@ -1075,8 +1279,9 @@ module.exports = cls => class Reifier extends cls {
|
|
|
1075
1279
|
}
|
|
1076
1280
|
|
|
1077
1281
|
async [_saveLockFile] (saveOpt) {
|
|
1078
|
-
if (!this[_usePackageLock])
|
|
1282
|
+
if (!this[_usePackageLock]) {
|
|
1079
1283
|
return
|
|
1284
|
+
}
|
|
1080
1285
|
|
|
1081
1286
|
const { meta } = this.idealTree
|
|
1082
1287
|
|
|
@@ -1088,8 +1293,9 @@ module.exports = cls => class Reifier extends cls {
|
|
|
1088
1293
|
for (const path of this[_trashList]) {
|
|
1089
1294
|
const loc = relpath(this.idealTree.realpath, path)
|
|
1090
1295
|
const node = this.idealTree.inventory.get(loc)
|
|
1091
|
-
if (node && node.root === this.idealTree)
|
|
1296
|
+
if (node && node.root === this.idealTree) {
|
|
1092
1297
|
node.parent = null
|
|
1298
|
+
}
|
|
1093
1299
|
}
|
|
1094
1300
|
|
|
1095
1301
|
// if we filtered to only certain nodes, then anything ELSE needs
|
|
@@ -1108,54 +1314,60 @@ module.exports = cls => class Reifier extends cls {
|
|
|
1108
1314
|
|
|
1109
1315
|
// if it's an ideal node from the filter set, then skip it
|
|
1110
1316
|
// because we already made whatever changes were necessary
|
|
1111
|
-
if (filterSet.has(ideal))
|
|
1317
|
+
if (filterSet.has(ideal)) {
|
|
1112
1318
|
continue
|
|
1319
|
+
}
|
|
1113
1320
|
|
|
1114
1321
|
// otherwise, if it's not in the actualTree, then it's not a thing
|
|
1115
1322
|
// that we actually added. And if it IS in the actualTree, then
|
|
1116
1323
|
// it's something that we left untouched, so we need to record
|
|
1117
1324
|
// that.
|
|
1118
1325
|
const actual = this.actualTree.inventory.get(loc)
|
|
1119
|
-
if (!actual)
|
|
1326
|
+
if (!actual) {
|
|
1120
1327
|
ideal.root = null
|
|
1121
|
-
else {
|
|
1328
|
+
} else {
|
|
1122
1329
|
if ([...actual.linksIn].some(link => filterSet.has(link))) {
|
|
1123
1330
|
seen.add(actual.location)
|
|
1124
1331
|
continue
|
|
1125
1332
|
}
|
|
1126
1333
|
const { realpath, isLink } = actual
|
|
1127
|
-
if (isLink && ideal.isLink && ideal.realpath === realpath)
|
|
1334
|
+
if (isLink && ideal.isLink && ideal.realpath === realpath) {
|
|
1128
1335
|
continue
|
|
1129
|
-
else
|
|
1336
|
+
} else {
|
|
1130
1337
|
reroot.add(actual)
|
|
1338
|
+
}
|
|
1131
1339
|
}
|
|
1132
1340
|
}
|
|
1133
1341
|
|
|
1134
1342
|
// now find any actual nodes that may not be present in the ideal
|
|
1135
1343
|
// tree, but were left behind by virtue of not being in the filter
|
|
1136
1344
|
for (const [loc, actual] of this.actualTree.inventory.entries()) {
|
|
1137
|
-
if (seen.has(loc))
|
|
1345
|
+
if (seen.has(loc)) {
|
|
1138
1346
|
continue
|
|
1347
|
+
}
|
|
1139
1348
|
seen.add(loc)
|
|
1140
1349
|
|
|
1141
1350
|
// we know that this is something that ISN'T in the idealTree,
|
|
1142
1351
|
// or else we will have addressed it in the previous loop.
|
|
1143
1352
|
// If it's in the filterSet, that means we intentionally removed
|
|
1144
1353
|
// it, so nothing to do here.
|
|
1145
|
-
if (filterSet.has(actual))
|
|
1354
|
+
if (filterSet.has(actual)) {
|
|
1146
1355
|
continue
|
|
1356
|
+
}
|
|
1147
1357
|
|
|
1148
1358
|
reroot.add(actual)
|
|
1149
1359
|
}
|
|
1150
1360
|
|
|
1151
1361
|
// go through the rerooted actual nodes, and move them over.
|
|
1152
|
-
for (const actual of reroot)
|
|
1362
|
+
for (const actual of reroot) {
|
|
1153
1363
|
actual.root = this.idealTree
|
|
1364
|
+
}
|
|
1154
1365
|
|
|
1155
1366
|
// prune out any tops that lack a linkIn, they are no longer relevant.
|
|
1156
1367
|
for (const top of this.idealTree.tops) {
|
|
1157
|
-
if (top.linksIn.size === 0)
|
|
1368
|
+
if (top.linksIn.size === 0) {
|
|
1158
1369
|
top.root = null
|
|
1370
|
+
}
|
|
1159
1371
|
}
|
|
1160
1372
|
|
|
1161
1373
|
// need to calculate dep flags, since nodes may have been marked
|
|
@@ -1171,7 +1383,8 @@ module.exports = cls => class Reifier extends cls {
|
|
|
1171
1383
|
this.actualTree = this.idealTree
|
|
1172
1384
|
this.idealTree = null
|
|
1173
1385
|
|
|
1174
|
-
if (!this[_global])
|
|
1386
|
+
if (!this[_global]) {
|
|
1175
1387
|
await this.actualTree.meta.save()
|
|
1388
|
+
}
|
|
1176
1389
|
}
|
|
1177
1390
|
}
|