@npmcli/arborist 7.5.0 → 7.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/arborist/build-ideal-tree.js +20 -46
- package/lib/arborist/index.js +28 -3
- package/lib/arborist/isolated-reifier.js +1 -1
- package/lib/arborist/load-actual.js +2 -2
- package/lib/arborist/load-virtual.js +1 -1
- package/lib/arborist/reify.js +254 -318
- package/lib/dep-valid.js +1 -1
- package/lib/inventory.js +1 -1
- package/lib/node.js +2 -0
- package/lib/packument-cache.js +77 -0
- package/lib/query-selector-all.js +6 -6
- package/lib/shrinkwrap.js +2 -1
- package/package.json +18 -17
package/lib/arborist/reify.js
CHANGED
|
@@ -38,119 +38,96 @@ const { saveTypeMap, hasSubKey } = require('../add-rm-pkg-deps.js')
|
|
|
38
38
|
const Shrinkwrap = require('../shrinkwrap.js')
|
|
39
39
|
const { defaultLockfileVersion } = Shrinkwrap
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
const _retiredUnchanged = Symbol('retiredUnchanged')
|
|
43
|
-
const _sparseTreeDirs = Symbol('sparseTreeDirs')
|
|
44
|
-
const _sparseTreeRoots = Symbol('sparseTreeRoots')
|
|
45
|
-
const _savePrefix = Symbol('savePrefix')
|
|
41
|
+
// Part of steps (steps need refactoring before we can do anything about these)
|
|
46
42
|
const _retireShallowNodes = Symbol.for('retireShallowNodes')
|
|
47
|
-
const
|
|
48
|
-
const
|
|
49
|
-
const
|
|
43
|
+
const _loadBundlesAndUpdateTrees = Symbol.for('loadBundlesAndUpdateTrees')
|
|
44
|
+
const _submitQuickAudit = Symbol('submitQuickAudit')
|
|
45
|
+
const _addOmitsToTrashList = Symbol('addOmitsToTrashList')
|
|
46
|
+
const _unpackNewModules = Symbol.for('unpackNewModules')
|
|
47
|
+
const _build = Symbol.for('build')
|
|
50
48
|
|
|
51
49
|
// shared by rebuild mixin
|
|
52
50
|
const _trashList = Symbol.for('trashList')
|
|
53
51
|
const _handleOptionalFailure = Symbol.for('handleOptionalFailure')
|
|
54
52
|
const _loadTrees = Symbol.for('loadTrees')
|
|
53
|
+
// defined by rebuild mixin
|
|
54
|
+
const _checkBins = Symbol.for('checkBins')
|
|
55
55
|
|
|
56
56
|
// shared symbols for swapping out when testing
|
|
57
|
+
// TODO tests should not be this deep into internals
|
|
57
58
|
const _diffTrees = Symbol.for('diffTrees')
|
|
58
59
|
const _createSparseTree = Symbol.for('createSparseTree')
|
|
59
60
|
const _loadShrinkwrapsAndUpdateTrees = Symbol.for('loadShrinkwrapsAndUpdateTrees')
|
|
60
|
-
const _shrinkwrapInflated = Symbol('shrinkwrapInflated')
|
|
61
|
-
const _bundleUnpacked = Symbol('bundleUnpacked')
|
|
62
|
-
const _bundleMissing = Symbol('bundleMissing')
|
|
63
61
|
const _reifyNode = Symbol.for('reifyNode')
|
|
64
|
-
const _extractOrLink = Symbol('extractOrLink')
|
|
65
62
|
const _updateAll = Symbol.for('updateAll')
|
|
66
63
|
const _updateNames = Symbol.for('updateNames')
|
|
67
|
-
// defined by rebuild mixin
|
|
68
|
-
const _checkBins = Symbol.for('checkBins')
|
|
69
|
-
const _symlink = Symbol('symlink')
|
|
70
|
-
const _warnDeprecated = Symbol('warnDeprecated')
|
|
71
|
-
const _loadBundlesAndUpdateTrees = Symbol.for('loadBundlesAndUpdateTrees')
|
|
72
|
-
const _submitQuickAudit = Symbol('submitQuickAudit')
|
|
73
|
-
const _unpackNewModules = Symbol.for('unpackNewModules')
|
|
74
64
|
const _moveContents = Symbol.for('moveContents')
|
|
75
65
|
const _moveBackRetiredUnchanged = Symbol.for('moveBackRetiredUnchanged')
|
|
76
|
-
const _build = Symbol.for('build')
|
|
77
66
|
const _removeTrash = Symbol.for('removeTrash')
|
|
78
67
|
const _renamePath = Symbol.for('renamePath')
|
|
79
68
|
const _rollbackRetireShallowNodes = Symbol.for('rollbackRetireShallowNodes')
|
|
80
69
|
const _rollbackCreateSparseTree = Symbol.for('rollbackCreateSparseTree')
|
|
81
70
|
const _rollbackMoveBackRetiredUnchanged = Symbol.for('rollbackMoveBackRetiredUnchanged')
|
|
82
71
|
const _saveIdealTree = Symbol.for('saveIdealTree')
|
|
83
|
-
const _copyIdealToActual = Symbol('copyIdealToActual')
|
|
84
|
-
const _addOmitsToTrashList = Symbol('addOmitsToTrashList')
|
|
85
|
-
const _packageLockOnly = Symbol('packageLockOnly')
|
|
86
|
-
const _dryRun = Symbol('dryRun')
|
|
87
|
-
const _validateNodeModules = Symbol('validateNodeModules')
|
|
88
|
-
const _nmValidated = Symbol('nmValidated')
|
|
89
|
-
const _validatePath = Symbol('validatePath')
|
|
90
72
|
const _reifyPackages = Symbol.for('reifyPackages')
|
|
91
73
|
|
|
92
|
-
|
|
93
|
-
const _omitOptional = Symbol('omitOptional')
|
|
94
|
-
const _omitPeer = Symbol('omitPeer')
|
|
95
|
-
|
|
96
|
-
const _pruneBundledMetadeps = Symbol('pruneBundledMetadeps')
|
|
97
|
-
|
|
98
|
-
// defined by Ideal mixin
|
|
74
|
+
// defined by build-ideal-tree mixin
|
|
99
75
|
const _resolvedAdd = Symbol.for('resolvedAdd')
|
|
100
76
|
const _usePackageLock = Symbol.for('usePackageLock')
|
|
101
|
-
|
|
77
|
+
// used by build-ideal-tree mixin
|
|
78
|
+
const _addNodeToTrashList = Symbol.for('addNodeToTrashList')
|
|
102
79
|
|
|
103
80
|
const _createIsolatedTree = Symbol.for('createIsolatedTree')
|
|
104
81
|
|
|
105
82
|
module.exports = cls => class Reifier extends cls {
|
|
83
|
+
#bundleMissing = new Set() // child nodes we'd EXPECT to be included in a bundle, but aren't
|
|
84
|
+
#bundleUnpacked = new Set() // the nodes we unpack to read their bundles
|
|
85
|
+
#dryRun
|
|
86
|
+
#nmValidated = new Set()
|
|
87
|
+
#omitDev
|
|
88
|
+
#omitPeer
|
|
89
|
+
#omitOptional
|
|
90
|
+
#retiredPaths = {}
|
|
91
|
+
#retiredUnchanged = {}
|
|
92
|
+
#savePrefix
|
|
93
|
+
#shrinkwrapInflated = new Set()
|
|
94
|
+
#sparseTreeDirs = new Set()
|
|
95
|
+
#sparseTreeRoots = new Set()
|
|
96
|
+
|
|
106
97
|
constructor (options) {
|
|
107
98
|
super(options)
|
|
108
99
|
|
|
109
|
-
const {
|
|
110
|
-
savePrefix = '^',
|
|
111
|
-
packageLockOnly = false,
|
|
112
|
-
dryRun = false,
|
|
113
|
-
formatPackageLock = true,
|
|
114
|
-
} = options
|
|
115
|
-
|
|
116
|
-
this[_dryRun] = !!dryRun
|
|
117
|
-
this[_packageLockOnly] = !!packageLockOnly
|
|
118
|
-
this[_savePrefix] = savePrefix
|
|
119
|
-
this[_formatPackageLock] = !!formatPackageLock
|
|
120
|
-
|
|
121
|
-
this.diff = null
|
|
122
|
-
this[_retiredPaths] = {}
|
|
123
|
-
this[_shrinkwrapInflated] = new Set()
|
|
124
|
-
this[_retiredUnchanged] = {}
|
|
125
|
-
this[_sparseTreeDirs] = new Set()
|
|
126
|
-
this[_sparseTreeRoots] = new Set()
|
|
127
100
|
this[_trashList] = new Set()
|
|
128
|
-
// the nodes we unpack to read their bundles
|
|
129
|
-
this[_bundleUnpacked] = new Set()
|
|
130
|
-
// child nodes we'd EXPECT to be included in a bundle, but aren't
|
|
131
|
-
this[_bundleMissing] = new Set()
|
|
132
|
-
this[_nmValidated] = new Set()
|
|
133
101
|
}
|
|
134
102
|
|
|
135
103
|
// public method
|
|
136
104
|
async reify (options = {}) {
|
|
137
105
|
const linked = (options.installStrategy || this.options.installStrategy) === 'linked'
|
|
138
106
|
|
|
139
|
-
if (this
|
|
107
|
+
if (this.options.packageLockOnly && this.options.global) {
|
|
140
108
|
const er = new Error('cannot generate lockfile for global packages')
|
|
141
109
|
er.code = 'ESHRINKWRAPGLOBAL'
|
|
142
110
|
throw er
|
|
143
111
|
}
|
|
144
112
|
|
|
145
113
|
const omit = new Set(options.omit || [])
|
|
146
|
-
this
|
|
147
|
-
this
|
|
148
|
-
this
|
|
114
|
+
this.#omitDev = omit.has('dev')
|
|
115
|
+
this.#omitOptional = omit.has('optional')
|
|
116
|
+
this.#omitPeer = omit.has('peer')
|
|
149
117
|
|
|
150
118
|
// start tracker block
|
|
151
119
|
this.addTracker('reify')
|
|
152
120
|
const timeEnd = time.start('reify')
|
|
153
|
-
|
|
121
|
+
// don't create missing dirs on dry runs
|
|
122
|
+
if (!this.options.packageLockOnly && !this.options.dryRun) {
|
|
123
|
+
// we do NOT want to set ownership on this folder, especially
|
|
124
|
+
// recursively, because it can have other side effects to do that
|
|
125
|
+
// in a project directory. We just want to make it if it's missing.
|
|
126
|
+
await mkdir(resolve(this.path), { recursive: true })
|
|
127
|
+
|
|
128
|
+
// do not allow the top-level node_modules to be a symlink
|
|
129
|
+
await this.#validateNodeModules(resolve(this.path, 'node_modules'))
|
|
130
|
+
}
|
|
154
131
|
await this[_loadTrees](options)
|
|
155
132
|
|
|
156
133
|
const oldTree = this.idealTree
|
|
@@ -159,7 +136,7 @@ module.exports = cls => class Reifier extends cls {
|
|
|
159
136
|
// this is currently technical debt which will be resolved in a refactor
|
|
160
137
|
// of Node/Link trees
|
|
161
138
|
log.warn('reify', 'The "linked" install strategy is EXPERIMENTAL and may contain bugs.')
|
|
162
|
-
this.idealTree = await this[_createIsolatedTree](
|
|
139
|
+
this.idealTree = await this[_createIsolatedTree]()
|
|
163
140
|
}
|
|
164
141
|
await this[_diffTrees]()
|
|
165
142
|
await this[_reifyPackages]()
|
|
@@ -169,7 +146,124 @@ module.exports = cls => class Reifier extends cls {
|
|
|
169
146
|
this.idealTree = oldTree
|
|
170
147
|
}
|
|
171
148
|
await this[_saveIdealTree](options)
|
|
172
|
-
|
|
149
|
+
// clean up any trash that is still in the tree
|
|
150
|
+
for (const path of this[_trashList]) {
|
|
151
|
+
const loc = relpath(this.idealTree.realpath, path)
|
|
152
|
+
const node = this.idealTree.inventory.get(loc)
|
|
153
|
+
if (node && node.root === this.idealTree) {
|
|
154
|
+
node.parent = null
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// if we filtered to only certain nodes, then anything ELSE needs
|
|
159
|
+
// to be untouched in the resulting actual tree, even if it differs
|
|
160
|
+
// in the idealTree. Copy over anything that was in the actual and
|
|
161
|
+
// was not changed, delete anything in the ideal and not actual.
|
|
162
|
+
// Then we move the entire idealTree over to this.actualTree, and
|
|
163
|
+
// save the hidden lockfile.
|
|
164
|
+
if (this.diff && this.diff.filterSet.size) {
|
|
165
|
+
const reroot = new Set()
|
|
166
|
+
|
|
167
|
+
const { filterSet } = this.diff
|
|
168
|
+
const seen = new Set()
|
|
169
|
+
for (const [loc, ideal] of this.idealTree.inventory.entries()) {
|
|
170
|
+
seen.add(loc)
|
|
171
|
+
|
|
172
|
+
// if it's an ideal node from the filter set, then skip it
|
|
173
|
+
// because we already made whatever changes were necessary
|
|
174
|
+
if (filterSet.has(ideal)) {
|
|
175
|
+
continue
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// otherwise, if it's not in the actualTree, then it's not a thing
|
|
179
|
+
// that we actually added. And if it IS in the actualTree, then
|
|
180
|
+
// it's something that we left untouched, so we need to record
|
|
181
|
+
// that.
|
|
182
|
+
const actual = this.actualTree.inventory.get(loc)
|
|
183
|
+
if (!actual) {
|
|
184
|
+
ideal.root = null
|
|
185
|
+
} else {
|
|
186
|
+
if ([...actual.linksIn].some(link => filterSet.has(link))) {
|
|
187
|
+
seen.add(actual.location)
|
|
188
|
+
continue
|
|
189
|
+
}
|
|
190
|
+
const { realpath, isLink } = actual
|
|
191
|
+
if (isLink && ideal.isLink && ideal.realpath === realpath) {
|
|
192
|
+
continue
|
|
193
|
+
} else {
|
|
194
|
+
reroot.add(actual)
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// now find any actual nodes that may not be present in the ideal
|
|
200
|
+
// tree, but were left behind by virtue of not being in the filter
|
|
201
|
+
for (const [loc, actual] of this.actualTree.inventory.entries()) {
|
|
202
|
+
if (seen.has(loc)) {
|
|
203
|
+
continue
|
|
204
|
+
}
|
|
205
|
+
seen.add(loc)
|
|
206
|
+
|
|
207
|
+
// we know that this is something that ISN'T in the idealTree,
|
|
208
|
+
// or else we will have addressed it in the previous loop.
|
|
209
|
+
// If it's in the filterSet, that means we intentionally removed
|
|
210
|
+
// it, so nothing to do here.
|
|
211
|
+
if (filterSet.has(actual)) {
|
|
212
|
+
continue
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
reroot.add(actual)
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// go through the rerooted actual nodes, and move them over.
|
|
219
|
+
for (const actual of reroot) {
|
|
220
|
+
actual.root = this.idealTree
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// prune out any tops that lack a linkIn, they are no longer relevant.
|
|
224
|
+
for (const top of this.idealTree.tops) {
|
|
225
|
+
if (top.linksIn.size === 0) {
|
|
226
|
+
top.root = null
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// need to calculate dep flags, since nodes may have been marked
|
|
231
|
+
// as extraneous or otherwise incorrect during transit.
|
|
232
|
+
calcDepFlags(this.idealTree)
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// save the ideal's meta as a hidden lockfile after we actualize it
|
|
236
|
+
this.idealTree.meta.filename =
|
|
237
|
+
this.idealTree.realpath + '/node_modules/.package-lock.json'
|
|
238
|
+
this.idealTree.meta.hiddenLockfile = true
|
|
239
|
+
this.idealTree.meta.lockfileVersion = defaultLockfileVersion
|
|
240
|
+
|
|
241
|
+
this.actualTree = this.idealTree
|
|
242
|
+
this.idealTree = null
|
|
243
|
+
|
|
244
|
+
if (!this.options.global) {
|
|
245
|
+
await this.actualTree.meta.save()
|
|
246
|
+
const ignoreScripts = !!this.options.ignoreScripts
|
|
247
|
+
// if we aren't doing a dry run or ignoring scripts and we actually made changes to the dep
|
|
248
|
+
// tree, then run the dependencies scripts
|
|
249
|
+
if (!this.options.dryRun && !ignoreScripts && this.diff && this.diff.children.length) {
|
|
250
|
+
const { path, package: pkg } = this.actualTree.target
|
|
251
|
+
const stdio = this.options.foregroundScripts ? 'inherit' : 'pipe'
|
|
252
|
+
const { scripts = {} } = pkg
|
|
253
|
+
for (const event of ['predependencies', 'dependencies', 'postdependencies']) {
|
|
254
|
+
if (Object.prototype.hasOwnProperty.call(scripts, event)) {
|
|
255
|
+
log.info('run', pkg._id, event, scripts[event])
|
|
256
|
+
await time.start(`reify:run:${event}`, () => runScript({
|
|
257
|
+
event,
|
|
258
|
+
path,
|
|
259
|
+
pkg,
|
|
260
|
+
stdio,
|
|
261
|
+
scriptShell: this.options.scriptShell,
|
|
262
|
+
}))
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
173
267
|
// This is a very bad pattern and I can't wait to stop doing it
|
|
174
268
|
this.auditReport = await this.auditReport
|
|
175
269
|
|
|
@@ -178,28 +272,13 @@ module.exports = cls => class Reifier extends cls {
|
|
|
178
272
|
return treeCheck(this.actualTree)
|
|
179
273
|
}
|
|
180
274
|
|
|
181
|
-
async [_validatePath] () {
|
|
182
|
-
// don't create missing dirs on dry runs
|
|
183
|
-
if (this[_packageLockOnly] || this[_dryRun]) {
|
|
184
|
-
return
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
// we do NOT want to set ownership on this folder, especially
|
|
188
|
-
// recursively, because it can have other side effects to do that
|
|
189
|
-
// in a project directory. We just want to make it if it's missing.
|
|
190
|
-
await mkdir(resolve(this.path), { recursive: true })
|
|
191
|
-
|
|
192
|
-
// do not allow the top-level node_modules to be a symlink
|
|
193
|
-
await this[_validateNodeModules](resolve(this.path, 'node_modules'))
|
|
194
|
-
}
|
|
195
|
-
|
|
196
275
|
async [_reifyPackages] () {
|
|
197
276
|
// we don't submit the audit report or write to disk on dry runs
|
|
198
|
-
if (this
|
|
277
|
+
if (this.options.dryRun) {
|
|
199
278
|
return
|
|
200
279
|
}
|
|
201
280
|
|
|
202
|
-
if (this
|
|
281
|
+
if (this.options.packageLockOnly) {
|
|
203
282
|
// we already have the complete tree, so just audit it now,
|
|
204
283
|
// and that's all we have to do here.
|
|
205
284
|
return this[_submitQuickAudit]()
|
|
@@ -248,6 +327,7 @@ module.exports = cls => class Reifier extends cls {
|
|
|
248
327
|
throw reifyTerminated
|
|
249
328
|
}
|
|
250
329
|
} catch (er) {
|
|
330
|
+
// TODO rollbacks shouldn't be relied on to throw err
|
|
251
331
|
await this[rollback](er)
|
|
252
332
|
/* istanbul ignore next - rollback throws, should never hit this */
|
|
253
333
|
throw er
|
|
@@ -272,11 +352,11 @@ module.exports = cls => class Reifier extends cls {
|
|
|
272
352
|
const timeEnd = time.start('reify:loadTrees')
|
|
273
353
|
const bitOpt = {
|
|
274
354
|
...options,
|
|
275
|
-
complete: this
|
|
355
|
+
complete: this.options.packageLockOnly || this.options.dryRun,
|
|
276
356
|
}
|
|
277
357
|
|
|
278
358
|
// if we're only writing a package lock, then it doesn't matter what's here
|
|
279
|
-
if (this
|
|
359
|
+
if (this.options.packageLockOnly) {
|
|
280
360
|
return this.buildIdealTree(bitOpt).then(timeEnd)
|
|
281
361
|
}
|
|
282
362
|
|
|
@@ -325,7 +405,7 @@ module.exports = cls => class Reifier extends cls {
|
|
|
325
405
|
}
|
|
326
406
|
|
|
327
407
|
[_diffTrees] () {
|
|
328
|
-
if (this
|
|
408
|
+
if (this.options.packageLockOnly) {
|
|
329
409
|
return
|
|
330
410
|
}
|
|
331
411
|
|
|
@@ -383,7 +463,7 @@ module.exports = cls => class Reifier extends cls {
|
|
|
383
463
|
// find all the nodes that need to change between the actual
|
|
384
464
|
// and ideal trees.
|
|
385
465
|
this.diff = Diff.calculate({
|
|
386
|
-
shrinkwrapInflated: this
|
|
466
|
+
shrinkwrapInflated: this.#shrinkwrapInflated,
|
|
387
467
|
filterNodes,
|
|
388
468
|
actual: this.actualTree,
|
|
389
469
|
ideal: this.idealTree,
|
|
@@ -405,7 +485,7 @@ module.exports = cls => class Reifier extends cls {
|
|
|
405
485
|
// replace them when rolling back on failure.
|
|
406
486
|
[_addNodeToTrashList] (node, retire = false) {
|
|
407
487
|
const paths = [node.path, ...node.binPaths]
|
|
408
|
-
const moves = this
|
|
488
|
+
const moves = this.#retiredPaths
|
|
409
489
|
log.silly('reify', 'mark', retire ? 'retired' : 'deleted', paths)
|
|
410
490
|
for (const path of paths) {
|
|
411
491
|
if (retire) {
|
|
@@ -422,7 +502,7 @@ module.exports = cls => class Reifier extends cls {
|
|
|
422
502
|
// changed or removed, so that we can rollback if necessary.
|
|
423
503
|
[_retireShallowNodes] () {
|
|
424
504
|
const timeEnd = time.start('reify:retireShallow')
|
|
425
|
-
const moves = this
|
|
505
|
+
const moves = this.#retiredPaths = {}
|
|
426
506
|
for (const diff of this.diff.children) {
|
|
427
507
|
if (diff.action === 'CHANGE' || diff.action === 'REMOVE') {
|
|
428
508
|
// we'll have to clean these up at the end, so add them to the list
|
|
@@ -455,12 +535,12 @@ module.exports = cls => class Reifier extends cls {
|
|
|
455
535
|
|
|
456
536
|
[_rollbackRetireShallowNodes] (er) {
|
|
457
537
|
const timeEnd = time.start('reify:rollback:retireShallow')
|
|
458
|
-
const moves = this
|
|
538
|
+
const moves = this.#retiredPaths
|
|
459
539
|
const movePromises = Object.entries(moves)
|
|
460
540
|
.map(([from, to]) => this[_renamePath](to, from))
|
|
461
541
|
return promiseAllRejectLate(movePromises)
|
|
462
542
|
// ignore subsequent rollback errors
|
|
463
|
-
.catch(
|
|
543
|
+
.catch(() => {})
|
|
464
544
|
.then(timeEnd)
|
|
465
545
|
.then(() => {
|
|
466
546
|
throw er
|
|
@@ -470,7 +550,7 @@ module.exports = cls => class Reifier extends cls {
|
|
|
470
550
|
// adding to the trash list will skip reifying, and delete them
|
|
471
551
|
// if they are currently in the tree and otherwise untouched.
|
|
472
552
|
[_addOmitsToTrashList] () {
|
|
473
|
-
if (!this
|
|
553
|
+
if (!this.#omitDev && !this.#omitOptional && !this.#omitPeer) {
|
|
474
554
|
return
|
|
475
555
|
}
|
|
476
556
|
|
|
@@ -492,10 +572,10 @@ module.exports = cls => class Reifier extends cls {
|
|
|
492
572
|
|
|
493
573
|
// omit node if the dep type matches any omit flags that were set
|
|
494
574
|
if (
|
|
495
|
-
node.peer && this
|
|
496
|
-
node.dev && this
|
|
497
|
-
node.optional && this
|
|
498
|
-
node.devOptional && this
|
|
575
|
+
node.peer && this.#omitPeer ||
|
|
576
|
+
node.dev && this.#omitDev ||
|
|
577
|
+
node.optional && this.#omitOptional ||
|
|
578
|
+
node.devOptional && this.#omitOptional && this.#omitDev
|
|
499
579
|
) {
|
|
500
580
|
this[_addNodeToTrashList](node)
|
|
501
581
|
}
|
|
@@ -511,7 +591,7 @@ module.exports = cls => class Reifier extends cls {
|
|
|
511
591
|
const leaves = this.diff.leaves
|
|
512
592
|
.filter(diff => {
|
|
513
593
|
return (diff.action === 'ADD' || diff.action === 'CHANGE') &&
|
|
514
|
-
!this
|
|
594
|
+
!this.#sparseTreeDirs.has(diff.ideal.path) &&
|
|
515
595
|
!diff.ideal.isLink
|
|
516
596
|
})
|
|
517
597
|
.map(diff => diff.ideal)
|
|
@@ -528,25 +608,25 @@ module.exports = cls => class Reifier extends cls {
|
|
|
528
608
|
continue
|
|
529
609
|
}
|
|
530
610
|
dirsChecked.add(d)
|
|
531
|
-
const st = await lstat(d).catch(
|
|
611
|
+
const st = await lstat(d).catch(() => null)
|
|
532
612
|
// this can happen if we have a link to a package with a name
|
|
533
613
|
// that the filesystem treats as if it is the same thing.
|
|
534
614
|
// would be nice to have conditional istanbul ignores here...
|
|
535
615
|
/* istanbul ignore next - defense in depth */
|
|
536
616
|
if (st && !st.isDirectory()) {
|
|
537
617
|
const retired = retirePath(d)
|
|
538
|
-
this[
|
|
618
|
+
this.#retiredPaths[d] = retired
|
|
539
619
|
this[_trashList].add(retired)
|
|
540
620
|
await this[_renamePath](d, retired)
|
|
541
621
|
}
|
|
542
622
|
}
|
|
543
|
-
this
|
|
623
|
+
this.#sparseTreeDirs.add(node.path)
|
|
544
624
|
const made = await mkdir(node.path, { recursive: true })
|
|
545
625
|
// if the directory already exists, made will be undefined. if that's the case
|
|
546
626
|
// we don't want to remove it because we aren't the ones who created it so we
|
|
547
|
-
// omit it from the
|
|
627
|
+
// omit it from the #sparseTreeRoots
|
|
548
628
|
if (made) {
|
|
549
|
-
this
|
|
629
|
+
this.#sparseTreeRoots.add(made)
|
|
550
630
|
}
|
|
551
631
|
})).then(timeEnd)
|
|
552
632
|
}
|
|
@@ -554,10 +634,10 @@ module.exports = cls => class Reifier extends cls {
|
|
|
554
634
|
[_rollbackCreateSparseTree] (er) {
|
|
555
635
|
const timeEnd = time.start('reify:rollback:createSparse')
|
|
556
636
|
// cut the roots of the sparse tree that were created, not the leaves
|
|
557
|
-
const roots = this
|
|
637
|
+
const roots = this.#sparseTreeRoots
|
|
558
638
|
// also delete the moves that we retired, so that we can move them back
|
|
559
639
|
const failures = []
|
|
560
|
-
const targets = [...roots, ...Object.keys(this
|
|
640
|
+
const targets = [...roots, ...Object.keys(this.#retiredPaths)]
|
|
561
641
|
const unlinks = targets
|
|
562
642
|
.map(path => rm(path, { recursive: true, force: true }).catch(er => failures.push([path, er])))
|
|
563
643
|
return promiseAllRejectLate(unlinks).then(() => {
|
|
@@ -574,7 +654,7 @@ module.exports = cls => class Reifier extends cls {
|
|
|
574
654
|
// we need to unpack them, read that shrinkwrap file, and then update
|
|
575
655
|
// the tree by calling loadVirtual with the node as the root.
|
|
576
656
|
[_loadShrinkwrapsAndUpdateTrees] () {
|
|
577
|
-
const seen = this
|
|
657
|
+
const seen = this.#shrinkwrapInflated
|
|
578
658
|
const shrinkwraps = this.diff.leaves
|
|
579
659
|
.filter(d => (d.action === 'CHANGE' || d.action === 'ADD' || !d.action) &&
|
|
580
660
|
d.ideal.hasShrinkwrap && !seen.has(d.ideal) &&
|
|
@@ -632,8 +712,13 @@ module.exports = cls => class Reifier extends cls {
|
|
|
632
712
|
checkPlatform(node.package, false, { cpu, os, libc })
|
|
633
713
|
}
|
|
634
714
|
await this[_checkBins](node)
|
|
635
|
-
await this
|
|
636
|
-
|
|
715
|
+
await this.#extractOrLink(node)
|
|
716
|
+
const { _id, deprecated } = node.package
|
|
717
|
+
// The .catch is in _handleOptionalFailure. Not ideal, this should be cleaned up.
|
|
718
|
+
// eslint-disable-next-line promise/always-return
|
|
719
|
+
if (deprecated) {
|
|
720
|
+
log.warn('deprecated', `${_id}: ${deprecated}`)
|
|
721
|
+
}
|
|
637
722
|
})
|
|
638
723
|
|
|
639
724
|
return this[_handleOptionalFailure](node, p)
|
|
@@ -645,22 +730,22 @@ module.exports = cls => class Reifier extends cls {
|
|
|
645
730
|
}
|
|
646
731
|
|
|
647
732
|
// do not allow node_modules to be a symlink
|
|
648
|
-
async
|
|
649
|
-
if (this.options.force || this
|
|
733
|
+
async #validateNodeModules (nm) {
|
|
734
|
+
if (this.options.force || this.#nmValidated.has(nm)) {
|
|
650
735
|
return
|
|
651
736
|
}
|
|
652
737
|
const st = await lstat(nm).catch(() => null)
|
|
653
738
|
if (!st || st.isDirectory()) {
|
|
654
|
-
this
|
|
739
|
+
this.#nmValidated.add(nm)
|
|
655
740
|
return
|
|
656
741
|
}
|
|
657
742
|
log.warn('reify', 'Removing non-directory', nm)
|
|
658
743
|
await rm(nm, { recursive: true, force: true })
|
|
659
744
|
}
|
|
660
745
|
|
|
661
|
-
async
|
|
746
|
+
async #extractOrLink (node) {
|
|
662
747
|
const nm = resolve(node.parent.path, 'node_modules')
|
|
663
|
-
await this
|
|
748
|
+
await this.#validateNodeModules(nm)
|
|
664
749
|
|
|
665
750
|
if (!node.isLink) {
|
|
666
751
|
// in normal cases, node.resolved should *always* be set by now.
|
|
@@ -672,7 +757,7 @@ module.exports = cls => class Reifier extends cls {
|
|
|
672
757
|
// entirely, since we can't possibly reify it.
|
|
673
758
|
let res = null
|
|
674
759
|
if (node.resolved) {
|
|
675
|
-
const registryResolved = this
|
|
760
|
+
const registryResolved = this.#registryResolved(node.resolved)
|
|
676
761
|
if (registryResolved) {
|
|
677
762
|
res = `${node.name}@${registryResolved}`
|
|
678
763
|
}
|
|
@@ -694,7 +779,7 @@ module.exports = cls => class Reifier extends cls {
|
|
|
694
779
|
return
|
|
695
780
|
}
|
|
696
781
|
await debug(async () => {
|
|
697
|
-
const st = await lstat(node.path).catch(
|
|
782
|
+
const st = await lstat(node.path).catch(() => null)
|
|
698
783
|
if (st && !st.isDirectory()) {
|
|
699
784
|
debug.log('unpacking into a non-directory', node)
|
|
700
785
|
throw Object.assign(new Error('ENOTDIR: not a directory'), {
|
|
@@ -718,10 +803,8 @@ module.exports = cls => class Reifier extends cls {
|
|
|
718
803
|
|
|
719
804
|
// node.isLink
|
|
720
805
|
await rm(node.path, { recursive: true, force: true })
|
|
721
|
-
await this[_symlink](node)
|
|
722
|
-
}
|
|
723
806
|
|
|
724
|
-
|
|
807
|
+
// symlink
|
|
725
808
|
const dir = dirname(node.path)
|
|
726
809
|
const target = node.realpath
|
|
727
810
|
const rel = relative(dir, target)
|
|
@@ -729,17 +812,10 @@ module.exports = cls => class Reifier extends cls {
|
|
|
729
812
|
return symlink(rel, node.path, 'junction')
|
|
730
813
|
}
|
|
731
814
|
|
|
732
|
-
[_warnDeprecated] (node) {
|
|
733
|
-
const { _id, deprecated } = node.package
|
|
734
|
-
if (deprecated) {
|
|
735
|
-
log.warn('deprecated', `${_id}: ${deprecated}`)
|
|
736
|
-
}
|
|
737
|
-
}
|
|
738
|
-
|
|
739
815
|
// if the node is optional, then the failure of the promise is nonfatal
|
|
740
816
|
// just add it and its optional set to the trash list.
|
|
741
817
|
[_handleOptionalFailure] (node, p) {
|
|
742
|
-
return (node.optional ? p.catch(
|
|
818
|
+
return (node.optional ? p.catch(() => {
|
|
743
819
|
const set = optionalSet(node)
|
|
744
820
|
for (node of set) {
|
|
745
821
|
log.verbose('reify', 'failed optional dependency', node.path)
|
|
@@ -748,7 +824,7 @@ module.exports = cls => class Reifier extends cls {
|
|
|
748
824
|
}) : p).then(() => node)
|
|
749
825
|
}
|
|
750
826
|
|
|
751
|
-
|
|
827
|
+
#registryResolved (resolved) {
|
|
752
828
|
// the default registry url is a magic value meaning "the currently
|
|
753
829
|
// configured registry".
|
|
754
830
|
// `resolved` must never be falsey.
|
|
@@ -778,18 +854,48 @@ module.exports = cls => class Reifier extends cls {
|
|
|
778
854
|
// by the contents of the package. however, in their case, rather than
|
|
779
855
|
// shipping a virtual tree that must be reified, they ship an entire
|
|
780
856
|
// reified actual tree that must be unpacked and not modified.
|
|
781
|
-
[_loadBundlesAndUpdateTrees] (
|
|
782
|
-
|
|
783
|
-
|
|
857
|
+
[_loadBundlesAndUpdateTrees] (depth = 0, bundlesByDepth) {
|
|
858
|
+
let maxBundleDepth
|
|
859
|
+
if (!bundlesByDepth) {
|
|
860
|
+
bundlesByDepth = new Map()
|
|
861
|
+
maxBundleDepth = -1
|
|
862
|
+
dfwalk({
|
|
863
|
+
tree: this.diff,
|
|
864
|
+
visit: diff => {
|
|
865
|
+
const node = diff.ideal
|
|
866
|
+
if (!node) {
|
|
867
|
+
return
|
|
868
|
+
}
|
|
869
|
+
if (node.isProjectRoot) {
|
|
870
|
+
return
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
const { bundleDependencies } = node.package
|
|
874
|
+
if (bundleDependencies && bundleDependencies.length) {
|
|
875
|
+
maxBundleDepth = Math.max(maxBundleDepth, node.depth)
|
|
876
|
+
if (!bundlesByDepth.has(node.depth)) {
|
|
877
|
+
bundlesByDepth.set(node.depth, [node])
|
|
878
|
+
} else {
|
|
879
|
+
bundlesByDepth.get(node.depth).push(node)
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
},
|
|
883
|
+
getChildren: diff => diff.children,
|
|
884
|
+
})
|
|
885
|
+
|
|
886
|
+
bundlesByDepth.set('maxBundleDepth', maxBundleDepth)
|
|
887
|
+
} else {
|
|
888
|
+
maxBundleDepth = bundlesByDepth.get('maxBundleDepth')
|
|
889
|
+
}
|
|
890
|
+
|
|
784
891
|
if (depth === 0) {
|
|
785
892
|
time.start('reify:loadBundles')
|
|
786
893
|
}
|
|
787
894
|
|
|
788
|
-
const maxBundleDepth = bundlesByDepth.get('maxBundleDepth')
|
|
789
895
|
if (depth > maxBundleDepth) {
|
|
790
896
|
// if we did something, then prune the tree and update the diffs
|
|
791
897
|
if (maxBundleDepth !== -1) {
|
|
792
|
-
this
|
|
898
|
+
this.#pruneBundledMetadeps(bundlesByDepth)
|
|
793
899
|
this[_diffTrees]()
|
|
794
900
|
}
|
|
795
901
|
time.end('reify:loadBundles')
|
|
@@ -810,7 +916,7 @@ module.exports = cls => class Reifier extends cls {
|
|
|
810
916
|
// extract all the nodes with bundles
|
|
811
917
|
return promiseCallLimit(set.map(node => {
|
|
812
918
|
return () => {
|
|
813
|
-
this
|
|
919
|
+
this.#bundleUnpacked.add(node)
|
|
814
920
|
return this[_reifyNode](node)
|
|
815
921
|
}
|
|
816
922
|
}), { rejectLate: true })
|
|
@@ -839,46 +945,15 @@ module.exports = cls => class Reifier extends cls {
|
|
|
839
945
|
},
|
|
840
946
|
})
|
|
841
947
|
for (const name of notTransplanted) {
|
|
842
|
-
this
|
|
948
|
+
this.#bundleMissing.add(node.children.get(name))
|
|
843
949
|
}
|
|
844
950
|
})))
|
|
845
951
|
// move onto the next level of bundled items
|
|
846
952
|
.then(() => this[_loadBundlesAndUpdateTrees](depth + 1, bundlesByDepth))
|
|
847
953
|
}
|
|
848
954
|
|
|
849
|
-
[_getBundlesByDepth] () {
|
|
850
|
-
const bundlesByDepth = new Map()
|
|
851
|
-
let maxBundleDepth = -1
|
|
852
|
-
dfwalk({
|
|
853
|
-
tree: this.diff,
|
|
854
|
-
visit: diff => {
|
|
855
|
-
const node = diff.ideal
|
|
856
|
-
if (!node) {
|
|
857
|
-
return
|
|
858
|
-
}
|
|
859
|
-
if (node.isProjectRoot) {
|
|
860
|
-
return
|
|
861
|
-
}
|
|
862
|
-
|
|
863
|
-
const { bundleDependencies } = node.package
|
|
864
|
-
if (bundleDependencies && bundleDependencies.length) {
|
|
865
|
-
maxBundleDepth = Math.max(maxBundleDepth, node.depth)
|
|
866
|
-
if (!bundlesByDepth.has(node.depth)) {
|
|
867
|
-
bundlesByDepth.set(node.depth, [node])
|
|
868
|
-
} else {
|
|
869
|
-
bundlesByDepth.get(node.depth).push(node)
|
|
870
|
-
}
|
|
871
|
-
}
|
|
872
|
-
},
|
|
873
|
-
getChildren: diff => diff.children,
|
|
874
|
-
})
|
|
875
|
-
|
|
876
|
-
bundlesByDepth.set('maxBundleDepth', maxBundleDepth)
|
|
877
|
-
return bundlesByDepth
|
|
878
|
-
}
|
|
879
|
-
|
|
880
955
|
// https://github.com/npm/cli/issues/1597#issuecomment-667639545
|
|
881
|
-
|
|
956
|
+
#pruneBundledMetadeps (bundlesByDepth) {
|
|
882
957
|
const bundleShadowed = new Set()
|
|
883
958
|
|
|
884
959
|
// Example dep graph:
|
|
@@ -1012,9 +1087,9 @@ module.exports = cls => class Reifier extends cls {
|
|
|
1012
1087
|
}
|
|
1013
1088
|
|
|
1014
1089
|
const node = diff.ideal
|
|
1015
|
-
const bd = this
|
|
1016
|
-
const sw = this
|
|
1017
|
-
const bundleMissing = this
|
|
1090
|
+
const bd = this.#bundleUnpacked.has(node)
|
|
1091
|
+
const sw = this.#shrinkwrapInflated.has(node)
|
|
1092
|
+
const bundleMissing = this.#bundleMissing.has(node)
|
|
1018
1093
|
|
|
1019
1094
|
// check whether we still need to unpack this one.
|
|
1020
1095
|
// test the inDepBundle last, since that's potentially a tree walk.
|
|
@@ -1050,8 +1125,8 @@ module.exports = cls => class Reifier extends cls {
|
|
|
1050
1125
|
// the actualTree and idealTree _don't_ differ, starting from the
|
|
1051
1126
|
// shallowest nodes that we moved aside in the first place.
|
|
1052
1127
|
const timeEnd = time.start('reify:unretire')
|
|
1053
|
-
const moves = this
|
|
1054
|
-
this
|
|
1128
|
+
const moves = this.#retiredPaths
|
|
1129
|
+
this.#retiredUnchanged = {}
|
|
1055
1130
|
return promiseAllRejectLate(this.diff.children.map(diff => {
|
|
1056
1131
|
// skip if nothing was retired
|
|
1057
1132
|
if (diff.action !== 'CHANGE' && diff.action !== 'REMOVE') {
|
|
@@ -1074,7 +1149,7 @@ module.exports = cls => class Reifier extends cls {
|
|
|
1074
1149
|
}
|
|
1075
1150
|
})
|
|
1076
1151
|
|
|
1077
|
-
this[
|
|
1152
|
+
this.#retiredUnchanged[retireFolder] = []
|
|
1078
1153
|
return promiseAllRejectLate(diff.unchanged.map(node => {
|
|
1079
1154
|
// no need to roll back links, since we'll just delete them anyway
|
|
1080
1155
|
if (node.isLink) {
|
|
@@ -1083,11 +1158,11 @@ module.exports = cls => class Reifier extends cls {
|
|
|
1083
1158
|
}
|
|
1084
1159
|
|
|
1085
1160
|
// will have been moved/unpacked along with bundler
|
|
1086
|
-
if (node.inDepBundle && !this
|
|
1161
|
+
if (node.inDepBundle && !this.#bundleMissing.has(node)) {
|
|
1087
1162
|
return
|
|
1088
1163
|
}
|
|
1089
1164
|
|
|
1090
|
-
this[
|
|
1165
|
+
this.#retiredUnchanged[retireFolder].push(node)
|
|
1091
1166
|
|
|
1092
1167
|
const rel = relative(realFolder, node.path)
|
|
1093
1168
|
const fromPath = resolve(retireFolder, rel)
|
|
@@ -1114,10 +1189,10 @@ module.exports = cls => class Reifier extends cls {
|
|
|
1114
1189
|
}
|
|
1115
1190
|
|
|
1116
1191
|
[_rollbackMoveBackRetiredUnchanged] (er) {
|
|
1117
|
-
const moves = this
|
|
1192
|
+
const moves = this.#retiredPaths
|
|
1118
1193
|
// flip the mapping around to go back
|
|
1119
1194
|
const realFolders = new Map(Object.entries(moves).map(([k, v]) => [v, k]))
|
|
1120
|
-
const promises = Object.entries(this
|
|
1195
|
+
const promises = Object.entries(this.#retiredUnchanged)
|
|
1121
1196
|
.map(([retireFolder, nodes]) => promiseAllRejectLate(nodes.map(node => {
|
|
1122
1197
|
const realFolder = realFolders.get(retireFolder)
|
|
1123
1198
|
const rel = relative(realFolder, node.path)
|
|
@@ -1209,7 +1284,7 @@ module.exports = cls => class Reifier extends cls {
|
|
|
1209
1284
|
const saveIdealTree = !(
|
|
1210
1285
|
(!save && !hasUpdates)
|
|
1211
1286
|
|| this.options.global
|
|
1212
|
-
|| this
|
|
1287
|
+
|| this.options.dryRun
|
|
1213
1288
|
)
|
|
1214
1289
|
|
|
1215
1290
|
if (!saveIdealTree) {
|
|
@@ -1245,7 +1320,7 @@ module.exports = cls => class Reifier extends cls {
|
|
|
1245
1320
|
const isLocalDep = req.type === 'directory' || req.type === 'file'
|
|
1246
1321
|
if (req.registry) {
|
|
1247
1322
|
const version = child.version
|
|
1248
|
-
const prefixRange = version ? this
|
|
1323
|
+
const prefixRange = version ? this.options.savePrefix + version : '*'
|
|
1249
1324
|
// if we installed a range, then we save the range specified
|
|
1250
1325
|
// if it is not a subset of the ^x.y.z. eg, installing a range
|
|
1251
1326
|
// of `1.x <1.2.3` will not be saved as `^1.2.0`, because that
|
|
@@ -1280,7 +1355,7 @@ module.exports = cls => class Reifier extends cls {
|
|
|
1280
1355
|
// using their relative path
|
|
1281
1356
|
if (edge.type === 'workspace') {
|
|
1282
1357
|
const { version } = edge.to.target
|
|
1283
|
-
const prefixRange = version ? this
|
|
1358
|
+
const prefixRange = version ? this.options.savePrefix + version : '*'
|
|
1284
1359
|
newSpec = prefixRange
|
|
1285
1360
|
} else {
|
|
1286
1361
|
// save the relative path in package.json
|
|
@@ -1449,151 +1524,12 @@ module.exports = cls => class Reifier extends cls {
|
|
|
1449
1524
|
|
|
1450
1525
|
// TODO this ignores options.save
|
|
1451
1526
|
await this.idealTree.meta.save({
|
|
1452
|
-
format: (this
|
|
1453
|
-
: this
|
|
1527
|
+
format: (this.options.formatPackageLock && format) ? format
|
|
1528
|
+
: this.options.formatPackageLock,
|
|
1454
1529
|
})
|
|
1455
1530
|
}
|
|
1456
1531
|
|
|
1457
1532
|
timeEnd()
|
|
1458
1533
|
return true
|
|
1459
1534
|
}
|
|
1460
|
-
|
|
1461
|
-
async [_copyIdealToActual] () {
|
|
1462
|
-
// clean up any trash that is still in the tree
|
|
1463
|
-
for (const path of this[_trashList]) {
|
|
1464
|
-
const loc = relpath(this.idealTree.realpath, path)
|
|
1465
|
-
const node = this.idealTree.inventory.get(loc)
|
|
1466
|
-
if (node && node.root === this.idealTree) {
|
|
1467
|
-
node.parent = null
|
|
1468
|
-
}
|
|
1469
|
-
}
|
|
1470
|
-
|
|
1471
|
-
// if we filtered to only certain nodes, then anything ELSE needs
|
|
1472
|
-
// to be untouched in the resulting actual tree, even if it differs
|
|
1473
|
-
// in the idealTree. Copy over anything that was in the actual and
|
|
1474
|
-
// was not changed, delete anything in the ideal and not actual.
|
|
1475
|
-
// Then we move the entire idealTree over to this.actualTree, and
|
|
1476
|
-
// save the hidden lockfile.
|
|
1477
|
-
if (this.diff && this.diff.filterSet.size) {
|
|
1478
|
-
const reroot = new Set()
|
|
1479
|
-
|
|
1480
|
-
const { filterSet } = this.diff
|
|
1481
|
-
const seen = new Set()
|
|
1482
|
-
for (const [loc, ideal] of this.idealTree.inventory.entries()) {
|
|
1483
|
-
seen.add(loc)
|
|
1484
|
-
|
|
1485
|
-
// if it's an ideal node from the filter set, then skip it
|
|
1486
|
-
// because we already made whatever changes were necessary
|
|
1487
|
-
if (filterSet.has(ideal)) {
|
|
1488
|
-
continue
|
|
1489
|
-
}
|
|
1490
|
-
|
|
1491
|
-
// otherwise, if it's not in the actualTree, then it's not a thing
|
|
1492
|
-
// that we actually added. And if it IS in the actualTree, then
|
|
1493
|
-
// it's something that we left untouched, so we need to record
|
|
1494
|
-
// that.
|
|
1495
|
-
const actual = this.actualTree.inventory.get(loc)
|
|
1496
|
-
if (!actual) {
|
|
1497
|
-
ideal.root = null
|
|
1498
|
-
} else {
|
|
1499
|
-
if ([...actual.linksIn].some(link => filterSet.has(link))) {
|
|
1500
|
-
seen.add(actual.location)
|
|
1501
|
-
continue
|
|
1502
|
-
}
|
|
1503
|
-
const { realpath, isLink } = actual
|
|
1504
|
-
if (isLink && ideal.isLink && ideal.realpath === realpath) {
|
|
1505
|
-
continue
|
|
1506
|
-
} else {
|
|
1507
|
-
reroot.add(actual)
|
|
1508
|
-
}
|
|
1509
|
-
}
|
|
1510
|
-
}
|
|
1511
|
-
|
|
1512
|
-
// now find any actual nodes that may not be present in the ideal
|
|
1513
|
-
// tree, but were left behind by virtue of not being in the filter
|
|
1514
|
-
for (const [loc, actual] of this.actualTree.inventory.entries()) {
|
|
1515
|
-
if (seen.has(loc)) {
|
|
1516
|
-
continue
|
|
1517
|
-
}
|
|
1518
|
-
seen.add(loc)
|
|
1519
|
-
|
|
1520
|
-
// we know that this is something that ISN'T in the idealTree,
|
|
1521
|
-
// or else we will have addressed it in the previous loop.
|
|
1522
|
-
// If it's in the filterSet, that means we intentionally removed
|
|
1523
|
-
// it, so nothing to do here.
|
|
1524
|
-
if (filterSet.has(actual)) {
|
|
1525
|
-
continue
|
|
1526
|
-
}
|
|
1527
|
-
|
|
1528
|
-
reroot.add(actual)
|
|
1529
|
-
}
|
|
1530
|
-
|
|
1531
|
-
// go through the rerooted actual nodes, and move them over.
|
|
1532
|
-
for (const actual of reroot) {
|
|
1533
|
-
actual.root = this.idealTree
|
|
1534
|
-
}
|
|
1535
|
-
|
|
1536
|
-
// prune out any tops that lack a linkIn, they are no longer relevant.
|
|
1537
|
-
for (const top of this.idealTree.tops) {
|
|
1538
|
-
if (top.linksIn.size === 0) {
|
|
1539
|
-
top.root = null
|
|
1540
|
-
}
|
|
1541
|
-
}
|
|
1542
|
-
|
|
1543
|
-
// need to calculate dep flags, since nodes may have been marked
|
|
1544
|
-
// as extraneous or otherwise incorrect during transit.
|
|
1545
|
-
calcDepFlags(this.idealTree)
|
|
1546
|
-
}
|
|
1547
|
-
|
|
1548
|
-
// save the ideal's meta as a hidden lockfile after we actualize it
|
|
1549
|
-
this.idealTree.meta.filename =
|
|
1550
|
-
this.idealTree.realpath + '/node_modules/.package-lock.json'
|
|
1551
|
-
this.idealTree.meta.hiddenLockfile = true
|
|
1552
|
-
this.idealTree.meta.lockfileVersion = defaultLockfileVersion
|
|
1553
|
-
|
|
1554
|
-
this.actualTree = this.idealTree
|
|
1555
|
-
this.idealTree = null
|
|
1556
|
-
|
|
1557
|
-
if (!this.options.global) {
|
|
1558
|
-
await this.actualTree.meta.save()
|
|
1559
|
-
const ignoreScripts = !!this.options.ignoreScripts
|
|
1560
|
-
// if we aren't doing a dry run or ignoring scripts and we actually made changes to the dep
|
|
1561
|
-
// tree, then run the dependencies scripts
|
|
1562
|
-
if (!this[_dryRun] && !ignoreScripts && this.diff && this.diff.children.length) {
|
|
1563
|
-
const { path, package: pkg } = this.actualTree.target
|
|
1564
|
-
const stdio = this.options.foregroundScripts ? 'inherit' : 'pipe'
|
|
1565
|
-
const { scripts = {} } = pkg
|
|
1566
|
-
for (const event of ['predependencies', 'dependencies', 'postdependencies']) {
|
|
1567
|
-
if (Object.prototype.hasOwnProperty.call(scripts, event)) {
|
|
1568
|
-
log.info('run', pkg._id, event, scripts[event])
|
|
1569
|
-
await time.start(`reify:run:${event}`, () => runScript({
|
|
1570
|
-
event,
|
|
1571
|
-
path,
|
|
1572
|
-
pkg,
|
|
1573
|
-
stdio,
|
|
1574
|
-
scriptShell: this.options.scriptShell,
|
|
1575
|
-
}))
|
|
1576
|
-
}
|
|
1577
|
-
}
|
|
1578
|
-
}
|
|
1579
|
-
}
|
|
1580
|
-
}
|
|
1581
|
-
|
|
1582
|
-
async dedupe (options = {}) {
|
|
1583
|
-
// allow the user to set options on the ctor as well.
|
|
1584
|
-
// XXX: deprecate separate method options objects.
|
|
1585
|
-
options = { ...this.options, ...options }
|
|
1586
|
-
const tree = await this.loadVirtual().catch(() => this.loadActual())
|
|
1587
|
-
const names = []
|
|
1588
|
-
for (const name of tree.inventory.query('name')) {
|
|
1589
|
-
if (tree.inventory.query('name', name).size > 1) {
|
|
1590
|
-
names.push(name)
|
|
1591
|
-
}
|
|
1592
|
-
}
|
|
1593
|
-
return this.reify({
|
|
1594
|
-
...options,
|
|
1595
|
-
preferDedupe: true,
|
|
1596
|
-
update: { names },
|
|
1597
|
-
})
|
|
1598
|
-
}
|
|
1599
1535
|
}
|