@npmcli/arborist 7.3.0 → 7.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/arborist/build-ideal-tree.js +98 -62
- package/lib/arborist/index.js +102 -14
- package/lib/arborist/load-actual.js +2 -4
- package/lib/arborist/rebuild.js +55 -84
- package/lib/arborist/reify.js +39 -24
- package/lib/case-insensitive-map.js +20 -20
- package/lib/index.js +0 -2
- package/lib/query-selector-all.js +80 -2
- package/lib/tracker.js +28 -29
- package/lib/version-from-tgz.js +4 -5
- package/lib/vuln.js +28 -31
- package/package.json +3 -3
- package/lib/arborist/audit.js +0 -51
- package/lib/arborist/deduper.js +0 -19
- package/lib/arborist/pruner.js +0 -30
- package/lib/arborist/set-workspaces.js +0 -19
- package/lib/get-workspace-nodes.js +0 -36
package/lib/arborist/rebuild.js
CHANGED
|
@@ -7,7 +7,7 @@ const promiseAllRejectLate = require('promise-all-reject-late')
|
|
|
7
7
|
const rpj = require('read-package-json-fast')
|
|
8
8
|
const binLinks = require('bin-links')
|
|
9
9
|
const runScript = require('@npmcli/run-script')
|
|
10
|
-
const promiseCallLimit = require('promise-call-limit')
|
|
10
|
+
const { callLimit: promiseCallLimit } = require('promise-call-limit')
|
|
11
11
|
const { resolve } = require('path')
|
|
12
12
|
const {
|
|
13
13
|
isNodeGypPackage,
|
|
@@ -19,67 +19,37 @@ const boolEnv = b => b ? '1' : ''
|
|
|
19
19
|
const sortNodes = (a, b) =>
|
|
20
20
|
(a.depth - b.depth) || localeCompare(a.path, b.path)
|
|
21
21
|
|
|
22
|
-
const _workspaces = Symbol.for('workspaces')
|
|
23
|
-
const _build = Symbol('build')
|
|
24
|
-
const _loadDefaultNodes = Symbol('loadDefaultNodes')
|
|
25
|
-
const _retrieveNodesByType = Symbol('retrieveNodesByType')
|
|
26
|
-
const _resetQueues = Symbol('resetQueues')
|
|
27
|
-
const _rebuildBundle = Symbol('rebuildBundle')
|
|
28
|
-
const _ignoreScripts = Symbol('ignoreScripts')
|
|
29
|
-
const _binLinks = Symbol('binLinks')
|
|
30
|
-
const _oldMeta = Symbol('oldMeta')
|
|
31
|
-
const _createBinLinks = Symbol('createBinLinks')
|
|
32
|
-
const _doHandleOptionalFailure = Symbol('doHandleOptionalFailure')
|
|
33
|
-
const _linkAllBins = Symbol('linkAllBins')
|
|
34
|
-
const _runScripts = Symbol('runScripts')
|
|
35
|
-
const _buildQueues = Symbol('buildQueues')
|
|
36
|
-
const _addToBuildSet = Symbol('addToBuildSet')
|
|
37
22
|
const _checkBins = Symbol.for('checkBins')
|
|
38
|
-
const _queues = Symbol('queues')
|
|
39
|
-
const _scriptShell = Symbol('scriptShell')
|
|
40
|
-
const _includeWorkspaceRoot = Symbol.for('includeWorkspaceRoot')
|
|
41
|
-
const _workspacesEnabled = Symbol.for('workspacesEnabled')
|
|
42
|
-
|
|
43
|
-
const _force = Symbol.for('force')
|
|
44
|
-
const _global = Symbol.for('global')
|
|
45
23
|
|
|
46
24
|
// defined by reify mixin
|
|
47
25
|
const _handleOptionalFailure = Symbol.for('handleOptionalFailure')
|
|
48
26
|
const _trashList = Symbol.for('trashList')
|
|
49
27
|
|
|
50
28
|
module.exports = cls => class Builder extends cls {
|
|
29
|
+
#doHandleOptionalFailure
|
|
30
|
+
#oldMeta = null
|
|
31
|
+
#queues
|
|
32
|
+
|
|
51
33
|
constructor (options) {
|
|
52
34
|
super(options)
|
|
53
35
|
|
|
54
|
-
const {
|
|
55
|
-
ignoreScripts = false,
|
|
56
|
-
scriptShell,
|
|
57
|
-
binLinks = true,
|
|
58
|
-
rebuildBundle = true,
|
|
59
|
-
} = options
|
|
60
|
-
|
|
61
36
|
this.scriptsRun = new Set()
|
|
62
|
-
this
|
|
63
|
-
this[_ignoreScripts] = !!ignoreScripts
|
|
64
|
-
this[_scriptShell] = scriptShell
|
|
65
|
-
this[_rebuildBundle] = !!rebuildBundle
|
|
66
|
-
this[_resetQueues]()
|
|
67
|
-
this[_oldMeta] = null
|
|
37
|
+
this.#resetQueues()
|
|
68
38
|
}
|
|
69
39
|
|
|
70
40
|
async rebuild ({ nodes, handleOptionalFailure = false } = {}) {
|
|
71
41
|
// nothing to do if we're not building anything!
|
|
72
|
-
if (this
|
|
42
|
+
if (this.options.ignoreScripts && !this.options.binLinks) {
|
|
73
43
|
return
|
|
74
44
|
}
|
|
75
45
|
|
|
76
46
|
// when building for the first time, as part of reify, we ignore
|
|
77
47
|
// failures in optional nodes, and just delete them. however, when
|
|
78
48
|
// running JUST a rebuild, we treat optional failures as real fails
|
|
79
|
-
this
|
|
49
|
+
this.#doHandleOptionalFailure = handleOptionalFailure
|
|
80
50
|
|
|
81
51
|
if (!nodes) {
|
|
82
|
-
nodes = await this
|
|
52
|
+
nodes = await this.#loadDefaultNodes()
|
|
83
53
|
}
|
|
84
54
|
|
|
85
55
|
// separates links nodes so that it can run
|
|
@@ -89,15 +59,15 @@ module.exports = cls => class Builder extends cls {
|
|
|
89
59
|
const {
|
|
90
60
|
depNodes,
|
|
91
61
|
linkNodes,
|
|
92
|
-
} = this
|
|
62
|
+
} = this.#retrieveNodesByType(nodes)
|
|
93
63
|
|
|
94
64
|
// build regular deps
|
|
95
|
-
await this
|
|
65
|
+
await this.#build(depNodes, {})
|
|
96
66
|
|
|
97
67
|
// build link deps
|
|
98
68
|
if (linkNodes.size) {
|
|
99
|
-
this
|
|
100
|
-
await this
|
|
69
|
+
this.#resetQueues()
|
|
70
|
+
await this.#build(linkNodes, { type: 'links' })
|
|
101
71
|
}
|
|
102
72
|
|
|
103
73
|
process.emit('timeEnd', 'build')
|
|
@@ -105,20 +75,20 @@ module.exports = cls => class Builder extends cls {
|
|
|
105
75
|
|
|
106
76
|
// if we don't have a set of nodes, then just rebuild
|
|
107
77
|
// the actual tree on disk.
|
|
108
|
-
async
|
|
78
|
+
async #loadDefaultNodes () {
|
|
109
79
|
let nodes
|
|
110
80
|
const tree = await this.loadActual()
|
|
111
81
|
let filterSet
|
|
112
|
-
if (!this
|
|
82
|
+
if (!this.options.workspacesEnabled) {
|
|
113
83
|
filterSet = this.excludeWorkspacesDependencySet(tree)
|
|
114
84
|
nodes = tree.inventory.filter(node =>
|
|
115
85
|
filterSet.has(node) || node.isProjectRoot
|
|
116
86
|
)
|
|
117
|
-
} else if (this
|
|
87
|
+
} else if (this.options.workspaces.length) {
|
|
118
88
|
filterSet = this.workspaceDependencySet(
|
|
119
89
|
tree,
|
|
120
|
-
this
|
|
121
|
-
this
|
|
90
|
+
this.options.workspaces,
|
|
91
|
+
this.options.includeWorkspaceRoot
|
|
122
92
|
)
|
|
123
93
|
nodes = tree.inventory.filter(node => filterSet.has(node))
|
|
124
94
|
} else {
|
|
@@ -127,7 +97,7 @@ module.exports = cls => class Builder extends cls {
|
|
|
127
97
|
return nodes
|
|
128
98
|
}
|
|
129
99
|
|
|
130
|
-
|
|
100
|
+
#retrieveNodesByType (nodes) {
|
|
131
101
|
const depNodes = new Set()
|
|
132
102
|
const linkNodes = new Set()
|
|
133
103
|
const storeNodes = new Set()
|
|
@@ -154,7 +124,7 @@ module.exports = cls => class Builder extends cls {
|
|
|
154
124
|
//
|
|
155
125
|
// we avoid doing so if global=true since `bin-links` relies
|
|
156
126
|
// on having the target nodes available in global mode.
|
|
157
|
-
if (!this
|
|
127
|
+
if (!this.options.global) {
|
|
158
128
|
for (const node of linkNodes) {
|
|
159
129
|
depNodes.delete(node.target)
|
|
160
130
|
}
|
|
@@ -166,8 +136,8 @@ module.exports = cls => class Builder extends cls {
|
|
|
166
136
|
}
|
|
167
137
|
}
|
|
168
138
|
|
|
169
|
-
|
|
170
|
-
this
|
|
139
|
+
#resetQueues () {
|
|
140
|
+
this.#queues = {
|
|
171
141
|
preinstall: [],
|
|
172
142
|
install: [],
|
|
173
143
|
postinstall: [],
|
|
@@ -176,46 +146,46 @@ module.exports = cls => class Builder extends cls {
|
|
|
176
146
|
}
|
|
177
147
|
}
|
|
178
148
|
|
|
179
|
-
async
|
|
149
|
+
async #build (nodes, { type = 'deps' }) {
|
|
180
150
|
process.emit('time', `build:${type}`)
|
|
181
151
|
|
|
182
|
-
await this
|
|
152
|
+
await this.#buildQueues(nodes)
|
|
183
153
|
|
|
184
|
-
if (!this
|
|
185
|
-
await this
|
|
154
|
+
if (!this.options.ignoreScripts) {
|
|
155
|
+
await this.#runScripts('preinstall')
|
|
186
156
|
}
|
|
187
157
|
|
|
188
158
|
// links should run prepare scripts and only link bins after that
|
|
189
159
|
if (type === 'links') {
|
|
190
|
-
await this
|
|
160
|
+
await this.#runScripts('prepare')
|
|
191
161
|
}
|
|
192
|
-
if (this
|
|
193
|
-
await this
|
|
162
|
+
if (this.options.binLinks) {
|
|
163
|
+
await this.#linkAllBins()
|
|
194
164
|
}
|
|
195
165
|
|
|
196
|
-
if (!this
|
|
197
|
-
await this
|
|
198
|
-
await this
|
|
166
|
+
if (!this.options.ignoreScripts) {
|
|
167
|
+
await this.#runScripts('install')
|
|
168
|
+
await this.#runScripts('postinstall')
|
|
199
169
|
}
|
|
200
170
|
|
|
201
171
|
process.emit('timeEnd', `build:${type}`)
|
|
202
172
|
}
|
|
203
173
|
|
|
204
|
-
async
|
|
174
|
+
async #buildQueues (nodes) {
|
|
205
175
|
process.emit('time', 'build:queue')
|
|
206
176
|
const set = new Set()
|
|
207
177
|
|
|
208
178
|
const promises = []
|
|
209
179
|
for (const node of nodes) {
|
|
210
|
-
promises.push(this
|
|
180
|
+
promises.push(this.#addToBuildSet(node, set))
|
|
211
181
|
|
|
212
182
|
// if it has bundle deps, add those too, if rebuildBundle
|
|
213
|
-
if (this
|
|
183
|
+
if (this.options.rebuildBundle !== false) {
|
|
214
184
|
const bd = node.package.bundleDependencies
|
|
215
185
|
if (bd && bd.length) {
|
|
216
186
|
dfwalk({
|
|
217
187
|
tree: node,
|
|
218
|
-
leave: node => promises.push(this
|
|
188
|
+
leave: node => promises.push(this.#addToBuildSet(node, set)),
|
|
219
189
|
getChildren: node => [...node.children.values()],
|
|
220
190
|
filter: node => node.inBundle,
|
|
221
191
|
})
|
|
@@ -236,7 +206,7 @@ module.exports = cls => class Builder extends cls {
|
|
|
236
206
|
const tests = { bin, preinstall, install, postinstall, prepare }
|
|
237
207
|
for (const [key, has] of Object.entries(tests)) {
|
|
238
208
|
if (has) {
|
|
239
|
-
this[
|
|
209
|
+
this.#queues[key].push(node)
|
|
240
210
|
}
|
|
241
211
|
}
|
|
242
212
|
}
|
|
@@ -249,21 +219,21 @@ module.exports = cls => class Builder extends cls {
|
|
|
249
219
|
// the node path. Otherwise a package can have a preinstall script
|
|
250
220
|
// that unlinks something, to allow them to silently overwrite system
|
|
251
221
|
// binaries, which is unsafe and insecure.
|
|
252
|
-
if (!node.globalTop || this
|
|
222
|
+
if (!node.globalTop || this.options.force) {
|
|
253
223
|
return
|
|
254
224
|
}
|
|
255
225
|
const { path, package: pkg } = node
|
|
256
226
|
await binLinks.checkBins({ pkg, path, top: true, global: true })
|
|
257
227
|
}
|
|
258
228
|
|
|
259
|
-
async
|
|
229
|
+
async #addToBuildSet (node, set, refreshed = false) {
|
|
260
230
|
if (set.has(node)) {
|
|
261
231
|
return
|
|
262
232
|
}
|
|
263
233
|
|
|
264
|
-
if (this
|
|
234
|
+
if (this.#oldMeta === null) {
|
|
265
235
|
const { root: { meta } } = node
|
|
266
|
-
this
|
|
236
|
+
this.#oldMeta = meta && meta.loadedFromDisk &&
|
|
267
237
|
!(meta.originalLockfileVersion >= 2)
|
|
268
238
|
}
|
|
269
239
|
|
|
@@ -272,7 +242,7 @@ module.exports = cls => class Builder extends cls {
|
|
|
272
242
|
|
|
273
243
|
const { preinstall, install, postinstall, prepare } = scripts
|
|
274
244
|
const anyScript = preinstall || install || postinstall || prepare
|
|
275
|
-
if (!refreshed && !anyScript && (hasInstallScript || this
|
|
245
|
+
if (!refreshed && !anyScript && (hasInstallScript || this.#oldMeta)) {
|
|
276
246
|
// we either have an old metadata (and thus might have scripts)
|
|
277
247
|
// or we have an indication that there's install scripts (but
|
|
278
248
|
// don't yet know what they are) so we have to load the package.json
|
|
@@ -286,7 +256,7 @@ module.exports = cls => class Builder extends cls {
|
|
|
286
256
|
|
|
287
257
|
const { scripts = {} } = pkg
|
|
288
258
|
node.package.scripts = scripts
|
|
289
|
-
return this
|
|
259
|
+
return this.#addToBuildSet(node, set, true)
|
|
290
260
|
}
|
|
291
261
|
|
|
292
262
|
// Rebuild node-gyp dependencies lacking an install or preinstall script
|
|
@@ -309,8 +279,8 @@ module.exports = cls => class Builder extends cls {
|
|
|
309
279
|
}
|
|
310
280
|
}
|
|
311
281
|
|
|
312
|
-
async
|
|
313
|
-
const queue = this[
|
|
282
|
+
async #runScripts (event) {
|
|
283
|
+
const queue = this.#queues[event]
|
|
314
284
|
|
|
315
285
|
if (!queue.length) {
|
|
316
286
|
return
|
|
@@ -358,7 +328,7 @@ module.exports = cls => class Builder extends cls {
|
|
|
358
328
|
pkg,
|
|
359
329
|
stdio,
|
|
360
330
|
env,
|
|
361
|
-
scriptShell: this
|
|
331
|
+
scriptShell: this.options.scriptShell,
|
|
362
332
|
}
|
|
363
333
|
const p = runScript(runOpts).catch(er => {
|
|
364
334
|
const { code, signal } = er
|
|
@@ -382,17 +352,17 @@ module.exports = cls => class Builder extends cls {
|
|
|
382
352
|
log.info('run', pkg._id, event, { code, signal })
|
|
383
353
|
})
|
|
384
354
|
|
|
385
|
-
await (this
|
|
355
|
+
await (this.#doHandleOptionalFailure
|
|
386
356
|
? this[_handleOptionalFailure](node, p)
|
|
387
357
|
: p)
|
|
388
358
|
|
|
389
359
|
process.emit('timeEnd', timer)
|
|
390
|
-
}), limit)
|
|
360
|
+
}), { limit })
|
|
391
361
|
process.emit('timeEnd', `build:run:${event}`)
|
|
392
362
|
}
|
|
393
363
|
|
|
394
|
-
async
|
|
395
|
-
const queue = this
|
|
364
|
+
async #linkAllBins () {
|
|
365
|
+
const queue = this.#queues.bin
|
|
396
366
|
if (!queue.length) {
|
|
397
367
|
return
|
|
398
368
|
}
|
|
@@ -402,14 +372,15 @@ module.exports = cls => class Builder extends cls {
|
|
|
402
372
|
// sort the queue by node path, so that the module-local collision
|
|
403
373
|
// detector in bin-links will always resolve the same way.
|
|
404
374
|
for (const node of queue.sort(sortNodes)) {
|
|
405
|
-
|
|
375
|
+
// TODO these run before they're awaited
|
|
376
|
+
promises.push(this.#createBinLinks(node))
|
|
406
377
|
}
|
|
407
378
|
|
|
408
379
|
await promiseAllRejectLate(promises)
|
|
409
380
|
process.emit('timeEnd', 'build:link')
|
|
410
381
|
}
|
|
411
382
|
|
|
412
|
-
async
|
|
383
|
+
async #createBinLinks (node) {
|
|
413
384
|
if (this[_trashList].has(node.path)) {
|
|
414
385
|
return
|
|
415
386
|
}
|
|
@@ -420,11 +391,11 @@ module.exports = cls => class Builder extends cls {
|
|
|
420
391
|
pkg: node.package,
|
|
421
392
|
path: node.path,
|
|
422
393
|
top: !!(node.isTop || node.globalTop),
|
|
423
|
-
force: this
|
|
394
|
+
force: this.options.force,
|
|
424
395
|
global: !!node.globalTop,
|
|
425
396
|
})
|
|
426
397
|
|
|
427
|
-
await (this
|
|
398
|
+
await (this.#doHandleOptionalFailure
|
|
428
399
|
? this[_handleOptionalFailure](node, p)
|
|
429
400
|
: p)
|
|
430
401
|
|
package/lib/arborist/reify.js
CHANGED
|
@@ -24,13 +24,13 @@ const PackageJson = require('@npmcli/package-json')
|
|
|
24
24
|
const packageContents = require('@npmcli/installed-package-contents')
|
|
25
25
|
const runScript = require('@npmcli/run-script')
|
|
26
26
|
const { checkEngine, checkPlatform } = require('npm-install-checks')
|
|
27
|
-
const _force = Symbol.for('force')
|
|
28
27
|
|
|
29
28
|
const treeCheck = require('../tree-check.js')
|
|
30
29
|
const relpath = require('../relpath.js')
|
|
31
30
|
const Diff = require('../diff.js')
|
|
32
31
|
const retirePath = require('../retire-path.js')
|
|
33
32
|
const promiseAllRejectLate = require('promise-all-reject-late')
|
|
33
|
+
const { callLimit: promiseCallLimit } = require('promise-call-limit')
|
|
34
34
|
const optionalSet = require('../optional-set.js')
|
|
35
35
|
const calcDepFlags = require('../calc-dep-flags.js')
|
|
36
36
|
const { saveTypeMap, hasSubKey } = require('../add-rm-pkg-deps.js')
|
|
@@ -47,8 +47,6 @@ const _retireShallowNodes = Symbol.for('retireShallowNodes')
|
|
|
47
47
|
const _getBundlesByDepth = Symbol('getBundlesByDepth')
|
|
48
48
|
const _registryResolved = Symbol('registryResolved')
|
|
49
49
|
const _addNodeToTrashList = Symbol.for('addNodeToTrashList')
|
|
50
|
-
const _workspaces = Symbol.for('workspaces')
|
|
51
|
-
const _workspacesEnabled = Symbol.for('workspacesEnabled')
|
|
52
50
|
|
|
53
51
|
// shared by rebuild mixin
|
|
54
52
|
const _trashList = Symbol.for('trashList')
|
|
@@ -90,14 +88,11 @@ const _validateNodeModules = Symbol('validateNodeModules')
|
|
|
90
88
|
const _nmValidated = Symbol('nmValidated')
|
|
91
89
|
const _validatePath = Symbol('validatePath')
|
|
92
90
|
const _reifyPackages = Symbol.for('reifyPackages')
|
|
93
|
-
const _includeWorkspaceRoot = Symbol.for('includeWorkspaceRoot')
|
|
94
91
|
|
|
95
92
|
const _omitDev = Symbol('omitDev')
|
|
96
93
|
const _omitOptional = Symbol('omitOptional')
|
|
97
94
|
const _omitPeer = Symbol('omitPeer')
|
|
98
95
|
|
|
99
|
-
const _global = Symbol.for('global')
|
|
100
|
-
|
|
101
96
|
const _pruneBundledMetadeps = Symbol('pruneBundledMetadeps')
|
|
102
97
|
|
|
103
98
|
// defined by Ideal mixin
|
|
@@ -141,7 +136,7 @@ module.exports = cls => class Reifier extends cls {
|
|
|
141
136
|
async reify (options = {}) {
|
|
142
137
|
const linked = (options.installStrategy || this.options.installStrategy) === 'linked'
|
|
143
138
|
|
|
144
|
-
if (this[_packageLockOnly] && this
|
|
139
|
+
if (this[_packageLockOnly] && this.options.global) {
|
|
145
140
|
const er = new Error('cannot generate lockfile for global packages')
|
|
146
141
|
er.code = 'ESHRINKWRAPGLOBAL'
|
|
147
142
|
throw er
|
|
@@ -286,7 +281,7 @@ module.exports = cls => class Reifier extends cls {
|
|
|
286
281
|
.then(() => process.emit('timeEnd', 'reify:loadTrees'))
|
|
287
282
|
}
|
|
288
283
|
|
|
289
|
-
const actualOpt = this
|
|
284
|
+
const actualOpt = this.options.global ? {
|
|
290
285
|
ignoreMissing: true,
|
|
291
286
|
global: true,
|
|
292
287
|
filter: (node, kid) => {
|
|
@@ -313,7 +308,7 @@ module.exports = cls => class Reifier extends cls {
|
|
|
313
308
|
},
|
|
314
309
|
} : { ignoreMissing: true }
|
|
315
310
|
|
|
316
|
-
if (!this
|
|
311
|
+
if (!this.options.global) {
|
|
317
312
|
return Promise.all([
|
|
318
313
|
this.loadActual(actualOpt),
|
|
319
314
|
this.buildIdealTree(bitOpt),
|
|
@@ -340,12 +335,12 @@ module.exports = cls => class Reifier extends cls {
|
|
|
340
335
|
// to just invalidate the parts that changed, but avoid walking the
|
|
341
336
|
// whole tree again.
|
|
342
337
|
|
|
343
|
-
const includeWorkspaces = this
|
|
344
|
-
const includeRootDeps = !
|
|
345
|
-
|| this
|
|
338
|
+
const includeWorkspaces = this.options.workspacesEnabled
|
|
339
|
+
const includeRootDeps = !includeWorkspaces
|
|
340
|
+
|| this.options.includeWorkspaceRoot && this.options.workspaces.length > 0
|
|
346
341
|
|
|
347
342
|
const filterNodes = []
|
|
348
|
-
if (this
|
|
343
|
+
if (this.options.global && this.explicitRequests.size) {
|
|
349
344
|
const idealTree = this.idealTree.target
|
|
350
345
|
const actualTree = this.actualTree.target
|
|
351
346
|
// we ONLY are allowed to make changes in the global top-level
|
|
@@ -363,7 +358,7 @@ module.exports = cls => class Reifier extends cls {
|
|
|
363
358
|
} else {
|
|
364
359
|
if (includeWorkspaces) {
|
|
365
360
|
// add all ws nodes to filterNodes
|
|
366
|
-
for (const ws of this
|
|
361
|
+
for (const ws of this.options.workspaces) {
|
|
367
362
|
const ideal = this.idealTree.children.get(ws)
|
|
368
363
|
if (ideal) {
|
|
369
364
|
filterNodes.push(ideal)
|
|
@@ -655,7 +650,7 @@ module.exports = cls => class Reifier extends cls {
|
|
|
655
650
|
|
|
656
651
|
// do not allow node_modules to be a symlink
|
|
657
652
|
async [_validateNodeModules] (nm) {
|
|
658
|
-
if (this
|
|
653
|
+
if (this.options.force || this[_nmValidated].has(nm)) {
|
|
659
654
|
return
|
|
660
655
|
}
|
|
661
656
|
const st = await lstat(nm).catch(() => null)
|
|
@@ -817,10 +812,12 @@ module.exports = cls => class Reifier extends cls {
|
|
|
817
812
|
}
|
|
818
813
|
|
|
819
814
|
// extract all the nodes with bundles
|
|
820
|
-
return
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
815
|
+
return promiseCallLimit(set.map(node => {
|
|
816
|
+
return () => {
|
|
817
|
+
this[_bundleUnpacked].add(node)
|
|
818
|
+
return this[_reifyNode](node)
|
|
819
|
+
}
|
|
820
|
+
}), { rejectLate: true })
|
|
824
821
|
// then load their unpacked children and move into the ideal tree
|
|
825
822
|
.then(nodes =>
|
|
826
823
|
promiseAllRejectLate(nodes.map(async node => {
|
|
@@ -989,11 +986,11 @@ module.exports = cls => class Reifier extends cls {
|
|
|
989
986
|
const tree = this.idealTree
|
|
990
987
|
|
|
991
988
|
// if we're operating on a workspace, only audit the workspace deps
|
|
992
|
-
if (this
|
|
989
|
+
if (this.options.workspaces.length) {
|
|
993
990
|
options.filterSet = this.workspaceDependencySet(
|
|
994
991
|
tree,
|
|
995
|
-
this
|
|
996
|
-
this
|
|
992
|
+
this.options.workspaces,
|
|
993
|
+
this.options.includeWorkspaceRoot
|
|
997
994
|
)
|
|
998
995
|
}
|
|
999
996
|
|
|
@@ -1217,7 +1214,7 @@ module.exports = cls => class Reifier extends cls {
|
|
|
1217
1214
|
// saveIdealTree to be able to write the lockfile by default.
|
|
1218
1215
|
const saveIdealTree = !(
|
|
1219
1216
|
(!save && !hasUpdates)
|
|
1220
|
-
|| this
|
|
1217
|
+
|| this.options.global
|
|
1221
1218
|
|| this[_dryRun]
|
|
1222
1219
|
)
|
|
1223
1220
|
|
|
@@ -1563,7 +1560,7 @@ module.exports = cls => class Reifier extends cls {
|
|
|
1563
1560
|
this.actualTree = this.idealTree
|
|
1564
1561
|
this.idealTree = null
|
|
1565
1562
|
|
|
1566
|
-
if (!this
|
|
1563
|
+
if (!this.options.global) {
|
|
1567
1564
|
await this.actualTree.meta.save()
|
|
1568
1565
|
const ignoreScripts = !!this.options.ignoreScripts
|
|
1569
1566
|
// if we aren't doing a dry run or ignoring scripts and we actually made changes to the dep
|
|
@@ -1590,4 +1587,22 @@ module.exports = cls => class Reifier extends cls {
|
|
|
1590
1587
|
}
|
|
1591
1588
|
}
|
|
1592
1589
|
}
|
|
1590
|
+
|
|
1591
|
+
async dedupe (options = {}) {
|
|
1592
|
+
// allow the user to set options on the ctor as well.
|
|
1593
|
+
// XXX: deprecate separate method options objects.
|
|
1594
|
+
options = { ...this.options, ...options }
|
|
1595
|
+
const tree = await this.loadVirtual().catch(() => this.loadActual())
|
|
1596
|
+
const names = []
|
|
1597
|
+
for (const name of tree.inventory.query('name')) {
|
|
1598
|
+
if (tree.inventory.query('name', name).size > 1) {
|
|
1599
|
+
names.push(name)
|
|
1600
|
+
}
|
|
1601
|
+
}
|
|
1602
|
+
return this.reify({
|
|
1603
|
+
...options,
|
|
1604
|
+
preferDedupe: true,
|
|
1605
|
+
update: { names },
|
|
1606
|
+
})
|
|
1607
|
+
}
|
|
1593
1608
|
}
|
|
@@ -2,49 +2,49 @@
|
|
|
2
2
|
// are case-insensitive and unicode-normalizing, so we need to treat
|
|
3
3
|
// node.children.get('FOO') and node.children.get('foo') as the same thing.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const OGMap = Map
|
|
9
|
-
module.exports = class Map extends OGMap {
|
|
5
|
+
module.exports = class CIMap extends Map {
|
|
6
|
+
#keys = new Map()
|
|
7
|
+
|
|
10
8
|
constructor (items = []) {
|
|
11
9
|
super()
|
|
12
|
-
this[_keys] = new OGMap()
|
|
13
10
|
for (const [key, val] of items) {
|
|
14
11
|
this.set(key, val)
|
|
15
12
|
}
|
|
16
13
|
}
|
|
17
14
|
|
|
18
|
-
|
|
19
|
-
|
|
15
|
+
#normKey (key) {
|
|
16
|
+
if (typeof key !== 'string') {
|
|
17
|
+
return key
|
|
18
|
+
}
|
|
19
|
+
return key.normalize('NFKD').toLowerCase()
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
get (key) {
|
|
23
|
-
const normKey = this
|
|
24
|
-
return this
|
|
23
|
+
const normKey = this.#normKey(key)
|
|
24
|
+
return this.#keys.has(normKey) ? super.get(this.#keys.get(normKey))
|
|
25
25
|
: undefined
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
set (key, val) {
|
|
29
|
-
const normKey = this
|
|
30
|
-
if (this
|
|
31
|
-
super.delete(this
|
|
29
|
+
const normKey = this.#normKey(key)
|
|
30
|
+
if (this.#keys.has(normKey)) {
|
|
31
|
+
super.delete(this.#keys.get(normKey))
|
|
32
32
|
}
|
|
33
|
-
this
|
|
33
|
+
this.#keys.set(normKey, key)
|
|
34
34
|
return super.set(key, val)
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
delete (key) {
|
|
38
|
-
const normKey = this
|
|
39
|
-
if (this
|
|
40
|
-
const prevKey = this
|
|
41
|
-
this
|
|
38
|
+
const normKey = this.#normKey(key)
|
|
39
|
+
if (this.#keys.has(normKey)) {
|
|
40
|
+
const prevKey = this.#keys.get(normKey)
|
|
41
|
+
this.#keys.delete(normKey)
|
|
42
42
|
return super.delete(prevKey)
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
has (key) {
|
|
47
|
-
const normKey = this
|
|
48
|
-
return this
|
|
47
|
+
const normKey = this.#normKey(key)
|
|
48
|
+
return this.#keys.has(normKey) && super.has(this.#keys.get(normKey))
|
|
49
49
|
}
|
|
50
50
|
}
|
package/lib/index.js
CHANGED