@npmcli/arborist 9.1.4 → 9.1.6
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/README.md +1 -1
- package/bin/index.js +1 -1
- package/lib/arborist/build-ideal-tree.js +31 -18
- package/lib/arborist/isolated-reifier.js +4 -4
- package/lib/arborist/load-actual.js +8 -4
- package/lib/arborist/load-virtual.js +8 -19
- package/lib/arborist/rebuild.js +8 -7
- package/lib/arborist/reify.js +36 -113
- package/lib/calc-dep-flags.js +31 -12
- package/lib/diff.js +21 -3
- package/lib/gather-dep-set.js +1 -1
- package/lib/node.js +58 -41
- package/lib/optional-set.js +2 -4
- package/lib/packument-cache.js +1 -1
- package/lib/place-dep.js +3 -3
- package/lib/query-selector-all.js +1 -1
- package/lib/shrinkwrap.js +3 -18
- package/package.json +15 -16
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
Inspect and manage `node_modules` trees.
|
|
8
8
|
|
|
9
|
-

|
|
10
10
|
|
|
11
11
|
There's more documentation [in the docs
|
|
12
12
|
folder](https://github.com/npm/cli/tree/latest/workspaces/arborist/docs).
|
package/bin/index.js
CHANGED
|
@@ -37,7 +37,7 @@ ${message && '\n' + message + '\n'}
|
|
|
37
37
|
|
|
38
38
|
Additionally:
|
|
39
39
|
|
|
40
|
-
* --loglevel=warn|--quiet will
|
|
40
|
+
* --loglevel=warn|--quiet will suppress the printing of package trees
|
|
41
41
|
* --logfile <file|bool> will output logs to a file
|
|
42
42
|
* --timing will show timing information
|
|
43
43
|
* Instead of 'npm install <pkg>', use 'arborist reify --add=<pkg>'.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// mixin implementing the buildIdealTree method
|
|
2
2
|
const localeCompare = require('@isaacs/string-locale-compare')('en')
|
|
3
|
-
const
|
|
3
|
+
const PackageJson = require('@npmcli/package-json')
|
|
4
4
|
const npa = require('npm-package-arg')
|
|
5
5
|
const pacote = require('pacote')
|
|
6
6
|
const cacache = require('cacache')
|
|
@@ -192,7 +192,7 @@ module.exports = cls => class IdealTreeBuilder extends cls {
|
|
|
192
192
|
}
|
|
193
193
|
|
|
194
194
|
async #checkEngineAndPlatform () {
|
|
195
|
-
const { engineStrict, npmVersion, nodeVersion, omit = [] } = this.options
|
|
195
|
+
const { engineStrict, npmVersion, nodeVersion, omit = [], cpu, os, libc } = this.options
|
|
196
196
|
const omitSet = new Set(omit)
|
|
197
197
|
|
|
198
198
|
for (const node of this.idealTree.inventory.values()) {
|
|
@@ -214,6 +214,19 @@ module.exports = cls => class IdealTreeBuilder extends cls {
|
|
|
214
214
|
}
|
|
215
215
|
checkPlatform(node.package, this.options.force)
|
|
216
216
|
}
|
|
217
|
+
if (node.optional && !node.inert) {
|
|
218
|
+
// Mark any optional packages we can't install as inert.
|
|
219
|
+
// We ignore the --force and --engine-strict flags.
|
|
220
|
+
try {
|
|
221
|
+
checkEngine(node.package, npmVersion, nodeVersion, false)
|
|
222
|
+
checkPlatform(node.package, false, { cpu, os, libc })
|
|
223
|
+
} catch (error) {
|
|
224
|
+
const set = optionalSet(node)
|
|
225
|
+
for (const node of set) {
|
|
226
|
+
node.inert = true
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
217
230
|
}
|
|
218
231
|
}
|
|
219
232
|
|
|
@@ -268,7 +281,7 @@ module.exports = cls => class IdealTreeBuilder extends cls {
|
|
|
268
281
|
root = await this.#globalRootNode()
|
|
269
282
|
} else {
|
|
270
283
|
try {
|
|
271
|
-
const pkg = await
|
|
284
|
+
const { content: pkg } = await PackageJson.normalize(this.path)
|
|
272
285
|
root = await this.#rootNodeFromPackage(pkg)
|
|
273
286
|
} catch (err) {
|
|
274
287
|
if (err.code === 'EJSONPARSE') {
|
|
@@ -324,7 +337,7 @@ module.exports = cls => class IdealTreeBuilder extends cls {
|
|
|
324
337
|
})
|
|
325
338
|
|
|
326
339
|
.then(tree => {
|
|
327
|
-
// search the virtual tree for invalid edges, if any are found add their source to
|
|
340
|
+
// search the virtual tree for missing/invalid edges, if any are found add their source to
|
|
328
341
|
// the depsQueue so that we'll fix it later
|
|
329
342
|
depth({
|
|
330
343
|
tree,
|
|
@@ -338,7 +351,7 @@ module.exports = cls => class IdealTreeBuilder extends cls {
|
|
|
338
351
|
filter: node => node,
|
|
339
352
|
visit: node => {
|
|
340
353
|
for (const edge of node.edgesOut.values()) {
|
|
341
|
-
if (!edge.valid) {
|
|
354
|
+
if (!edge.to || !edge.valid) {
|
|
342
355
|
this.#depsQueue.push(node)
|
|
343
356
|
break // no need to continue the loop after the first hit
|
|
344
357
|
}
|
|
@@ -448,7 +461,6 @@ module.exports = cls => class IdealTreeBuilder extends cls {
|
|
|
448
461
|
const paths = await readdirScoped(nm).catch(() => [])
|
|
449
462
|
for (const p of paths) {
|
|
450
463
|
const name = p.replace(/\\/g, '/')
|
|
451
|
-
tree.package.dependencies = tree.package.dependencies || {}
|
|
452
464
|
const updateName = this[_updateNames].includes(name)
|
|
453
465
|
if (this[_updateAll] || updateName) {
|
|
454
466
|
if (updateName) {
|
|
@@ -812,7 +824,7 @@ This is a one-time fix-up, please be patient...
|
|
|
812
824
|
node !== this.idealTree &&
|
|
813
825
|
node.resolved &&
|
|
814
826
|
(hasBundle || hasShrinkwrap) &&
|
|
815
|
-
!node.
|
|
827
|
+
!node.inert
|
|
816
828
|
if (crackOpen) {
|
|
817
829
|
const Arborist = this.constructor
|
|
818
830
|
const opt = { ...this.options }
|
|
@@ -1012,7 +1024,7 @@ This is a one-time fix-up, please be patient...
|
|
|
1012
1024
|
}
|
|
1013
1025
|
|
|
1014
1026
|
// pre-fetch any problem edges, since we'll need these soon
|
|
1015
|
-
// if it fails at this point, though,
|
|
1027
|
+
// if it fails at this point, though, don't worry because it
|
|
1016
1028
|
// may well be an optional dep that has gone missing. it'll
|
|
1017
1029
|
// fail later anyway.
|
|
1018
1030
|
for (const e of this.#problemEdges(placed)) {
|
|
@@ -1068,7 +1080,7 @@ This is a one-time fix-up, please be patient...
|
|
|
1068
1080
|
? await this.#nodeFromSpec(edge.name, spec2, parent, secondEdge)
|
|
1069
1081
|
: null
|
|
1070
1082
|
|
|
1071
|
-
// pick the second one if they're both happy with that
|
|
1083
|
+
// pick the second one if they're both happy with that; otherwise, first
|
|
1072
1084
|
const node = second && edge.valid ? second : first
|
|
1073
1085
|
// ensure the one we want is the one that's placed
|
|
1074
1086
|
node.parent = parent
|
|
@@ -1275,7 +1287,7 @@ This is a one-time fix-up, please be patient...
|
|
|
1275
1287
|
|
|
1276
1288
|
// failed to load the spec, either because of enotarget or
|
|
1277
1289
|
// fetch failure of some other sort. save it so we can verify
|
|
1278
|
-
// later that it's optional
|
|
1290
|
+
// later that it's optional; otherwise, the error is fatal.
|
|
1279
1291
|
const n = new Node({
|
|
1280
1292
|
name,
|
|
1281
1293
|
parent,
|
|
@@ -1288,14 +1300,15 @@ This is a one-time fix-up, please be patient...
|
|
|
1288
1300
|
})
|
|
1289
1301
|
}
|
|
1290
1302
|
|
|
1291
|
-
#linkFromSpec (name, spec, parent) {
|
|
1303
|
+
async #linkFromSpec (name, spec, parent) {
|
|
1292
1304
|
const realpath = spec.fetchSpec
|
|
1293
1305
|
const { installLinks, legacyPeerDeps } = this
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
this.#linkNodes.add(link)
|
|
1297
|
-
return link
|
|
1306
|
+
const { content: pkg } = await PackageJson.normalize(realpath).catch(() => {
|
|
1307
|
+
return { content: {} }
|
|
1298
1308
|
})
|
|
1309
|
+
const link = new Link({ name, parent, realpath, pkg, installLinks, legacyPeerDeps })
|
|
1310
|
+
this.#linkNodes.add(link)
|
|
1311
|
+
return link
|
|
1299
1312
|
}
|
|
1300
1313
|
|
|
1301
1314
|
// load all peer deps and meta-peer deps into the node's parent
|
|
@@ -1431,7 +1444,7 @@ This is a one-time fix-up, please be patient...
|
|
|
1431
1444
|
// - if a path under an existing node, then assign that as the fsParent,
|
|
1432
1445
|
// and add it to the _depsQueue
|
|
1433
1446
|
//
|
|
1434
|
-
// call buildDepStep if anything was added to the queue
|
|
1447
|
+
// call buildDepStep if anything was added to the queue; otherwise, we're done
|
|
1435
1448
|
#resolveLinks () {
|
|
1436
1449
|
for (const link of this.#linkNodes) {
|
|
1437
1450
|
this.#linkNodes.delete(link)
|
|
@@ -1561,7 +1574,7 @@ This is a one-time fix-up, please be patient...
|
|
|
1561
1574
|
|
|
1562
1575
|
const set = optionalSet(node)
|
|
1563
1576
|
for (const node of set) {
|
|
1564
|
-
node.
|
|
1577
|
+
node.inert = true
|
|
1565
1578
|
}
|
|
1566
1579
|
}
|
|
1567
1580
|
}
|
|
@@ -1582,7 +1595,7 @@ This is a one-time fix-up, please be patient...
|
|
|
1582
1595
|
node.parent !== null
|
|
1583
1596
|
&& !node.isProjectRoot
|
|
1584
1597
|
&& !excludeNodes.has(node)
|
|
1585
|
-
&& !node.
|
|
1598
|
+
&& !node.inert
|
|
1586
1599
|
) {
|
|
1587
1600
|
this[_addNodeToTrashList](node)
|
|
1588
1601
|
}
|
|
@@ -81,7 +81,7 @@ module.exports = cls => class IsolatedReifier extends cls {
|
|
|
81
81
|
}
|
|
82
82
|
queue.push(e.to)
|
|
83
83
|
})
|
|
84
|
-
if (!next.isProjectRoot && !next.isWorkspace && !next.
|
|
84
|
+
if (!next.isProjectRoot && !next.isWorkspace && !next.inert) {
|
|
85
85
|
root.external.push(await this.externalProxyMemo(next))
|
|
86
86
|
}
|
|
87
87
|
}
|
|
@@ -140,15 +140,15 @@ module.exports = cls => class IsolatedReifier extends cls {
|
|
|
140
140
|
|
|
141
141
|
async assignCommonProperties (node, result) {
|
|
142
142
|
function validEdgesOut (node) {
|
|
143
|
-
return [...node.edgesOut.values()].filter(e => e.to && e.to.target && !(node.package.
|
|
143
|
+
return [...node.edgesOut.values()].filter(e => e.to && e.to.target && !(node.package.bundledDependencies || node.package.bundleDependencies || []).includes(e.to.name))
|
|
144
144
|
}
|
|
145
145
|
const edges = validEdgesOut(node)
|
|
146
146
|
const optionalDeps = edges.filter(e => e.optional).map(e => e.to.target)
|
|
147
147
|
const nonOptionalDeps = edges.filter(e => !e.optional).map(e => e.to.target)
|
|
148
148
|
|
|
149
149
|
result.localDependencies = await Promise.all(nonOptionalDeps.filter(n => n.isWorkspace).map(this.workspaceProxyMemo))
|
|
150
|
-
result.externalDependencies = await Promise.all(nonOptionalDeps.filter(n => !n.isWorkspace && !n.
|
|
151
|
-
result.externalOptionalDependencies = await Promise.all(optionalDeps.filter(n => !n.
|
|
150
|
+
result.externalDependencies = await Promise.all(nonOptionalDeps.filter(n => !n.isWorkspace && !n.inert).map(this.externalProxyMemo))
|
|
151
|
+
result.externalOptionalDependencies = await Promise.all(optionalDeps.filter(n => !n.inert).map(this.externalProxyMemo))
|
|
152
152
|
result.dependencies = [
|
|
153
153
|
...result.externalDependencies,
|
|
154
154
|
...result.localDependencies,
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// mix-in implementing the loadActual method
|
|
2
2
|
|
|
3
|
-
const {
|
|
3
|
+
const { dirname, join, normalize, relative, resolve } = require('node:path')
|
|
4
4
|
|
|
5
|
-
const
|
|
5
|
+
const PackageJson = require('@npmcli/package-json')
|
|
6
6
|
const { readdirScoped } = require('@npmcli/fs')
|
|
7
7
|
const { walkUp } = require('walk-up-path')
|
|
8
8
|
const ancestorPath = require('common-ancestor-path')
|
|
@@ -36,7 +36,7 @@ module.exports = cls => class ActualLoader extends cls {
|
|
|
36
36
|
// We don't do fsParent as a magic getter/setter, because it'd be too costly
|
|
37
37
|
// to keep up to date along the walk.
|
|
38
38
|
// And, we know that it can ONLY be relevant when the node is a target of a
|
|
39
|
-
// link
|
|
39
|
+
// link; otherwise, it'd be in a node_modules folder, so take advantage of
|
|
40
40
|
// that to limit the scans later.
|
|
41
41
|
#topNodes = new Set()
|
|
42
42
|
#transplantFilter
|
|
@@ -279,12 +279,16 @@ module.exports = cls => class ActualLoader extends cls {
|
|
|
279
279
|
}
|
|
280
280
|
|
|
281
281
|
try {
|
|
282
|
-
const pkg = await
|
|
282
|
+
const { content: pkg } = await PackageJson.normalize(real)
|
|
283
283
|
params.pkg = pkg
|
|
284
284
|
if (useRootOverrides && root.overrides) {
|
|
285
285
|
params.overrides = root.overrides.getNodeRule({ name: pkg.name, version: pkg.version })
|
|
286
286
|
}
|
|
287
287
|
} catch (err) {
|
|
288
|
+
if (err.code === 'EJSONPARSE') {
|
|
289
|
+
// TODO @npmcli/package-json should be doing this
|
|
290
|
+
err.path = join(real, 'package.json')
|
|
291
|
+
}
|
|
288
292
|
params.error = err
|
|
289
293
|
}
|
|
290
294
|
|
|
@@ -1,16 +1,15 @@
|
|
|
1
|
+
const { resolve } = require('node:path')
|
|
1
2
|
// mixin providing the loadVirtual method
|
|
2
3
|
const mapWorkspaces = require('@npmcli/map-workspaces')
|
|
3
|
-
|
|
4
|
-
const { resolve } = require('node:path')
|
|
5
|
-
|
|
4
|
+
const PackageJson = require('@npmcli/package-json')
|
|
6
5
|
const nameFromFolder = require('@npmcli/name-from-folder')
|
|
6
|
+
|
|
7
7
|
const consistentResolve = require('../consistent-resolve.js')
|
|
8
8
|
const Shrinkwrap = require('../shrinkwrap.js')
|
|
9
9
|
const Node = require('../node.js')
|
|
10
10
|
const Link = require('../link.js')
|
|
11
11
|
const relpath = require('../relpath.js')
|
|
12
12
|
const calcDepFlags = require('../calc-dep-flags.js')
|
|
13
|
-
const rpj = require('read-package-json-fast')
|
|
14
13
|
const treeCheck = require('../tree-check.js')
|
|
15
14
|
|
|
16
15
|
const flagsSuspect = Symbol.for('flagsSuspect')
|
|
@@ -54,10 +53,11 @@ module.exports = cls => class VirtualLoader extends cls {
|
|
|
54
53
|
|
|
55
54
|
// when building the ideal tree, we pass in a root node to this function
|
|
56
55
|
// otherwise, load it from the root package json or the lockfile
|
|
56
|
+
const pkg = await PackageJson.normalize(this.path).then(p => p.content).catch(() => s.data.packages[''] || {})
|
|
57
|
+
// TODO clean this up
|
|
57
58
|
const {
|
|
58
|
-
root = await this.#
|
|
59
|
+
root = await this[setWorkspaces](this.#loadNode('', pkg, true)),
|
|
59
60
|
} = options
|
|
60
|
-
|
|
61
61
|
this.#rootOptionProvided = options.root
|
|
62
62
|
|
|
63
63
|
await this.#loadFromShrinkwrap(s, root)
|
|
@@ -65,12 +65,6 @@ module.exports = cls => class VirtualLoader extends cls {
|
|
|
65
65
|
return treeCheck(this.virtualTree)
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
async #loadRoot (s) {
|
|
69
|
-
const pj = this.path + '/package.json'
|
|
70
|
-
const pkg = await rpj(pj).catch(() => s.data.packages['']) || {}
|
|
71
|
-
return this[setWorkspaces](this.#loadNode('', pkg, true))
|
|
72
|
-
}
|
|
73
|
-
|
|
74
68
|
async #loadFromShrinkwrap (s, root) {
|
|
75
69
|
if (!this.#rootOptionProvided) {
|
|
76
70
|
// root is never any of these things, but might be a brand new
|
|
@@ -174,7 +168,7 @@ module.exports = cls => class VirtualLoader extends cls {
|
|
|
174
168
|
}
|
|
175
169
|
}
|
|
176
170
|
|
|
177
|
-
// separate out link
|
|
171
|
+
// separate out link metadata, and create Node objects for nodes
|
|
178
172
|
#resolveNodes (s, root) {
|
|
179
173
|
const links = new Map()
|
|
180
174
|
const nodes = new Map([['', root]])
|
|
@@ -219,11 +213,7 @@ To fix:
|
|
|
219
213
|
// we always need to read the package.json for link targets
|
|
220
214
|
// outside node_modules because they can be changed by the local user
|
|
221
215
|
if (!link.target.parent) {
|
|
222
|
-
|
|
223
|
-
const pkg = await rpj(pj).catch(() => null)
|
|
224
|
-
if (pkg) {
|
|
225
|
-
link.target.package = pkg
|
|
226
|
-
}
|
|
216
|
+
await PackageJson.normalize(link.realpath).then(p => link.target.package = p.content).catch(() => null)
|
|
227
217
|
}
|
|
228
218
|
}
|
|
229
219
|
}
|
|
@@ -279,7 +269,6 @@ To fix:
|
|
|
279
269
|
integrity: sw.integrity,
|
|
280
270
|
resolved: consistentResolve(sw.resolved, this.path, path),
|
|
281
271
|
pkg: sw,
|
|
282
|
-
ideallyInert: sw.ideallyInert,
|
|
283
272
|
hasShrinkwrap: sw.hasShrinkwrap,
|
|
284
273
|
dev,
|
|
285
274
|
optional,
|
package/lib/arborist/rebuild.js
CHANGED
|
@@ -1,20 +1,19 @@
|
|
|
1
1
|
// Arborist.rebuild({path = this.path}) will do all the binlinks and
|
|
2
2
|
// bundle building needed. Called by reify, and by `npm rebuild`.
|
|
3
3
|
|
|
4
|
+
const PackageJson = require('@npmcli/package-json')
|
|
5
|
+
const binLinks = require('bin-links')
|
|
4
6
|
const localeCompare = require('@isaacs/string-locale-compare')('en')
|
|
5
|
-
const { depth: dfwalk } = require('treeverse')
|
|
6
7
|
const promiseAllRejectLate = require('promise-all-reject-late')
|
|
7
|
-
const rpj = require('read-package-json-fast')
|
|
8
|
-
const binLinks = require('bin-links')
|
|
9
8
|
const runScript = require('@npmcli/run-script')
|
|
10
9
|
const { callLimit: promiseCallLimit } = require('promise-call-limit')
|
|
11
|
-
const {
|
|
10
|
+
const { depth: dfwalk } = require('treeverse')
|
|
12
11
|
const { isNodeGypPackage, defaultGypInstallScript } = require('@npmcli/node-gyp')
|
|
13
12
|
const { log, time } = require('proc-log')
|
|
13
|
+
const { resolve } = require('node:path')
|
|
14
14
|
|
|
15
15
|
const boolEnv = b => b ? '1' : ''
|
|
16
|
-
const sortNodes = (a, b) =>
|
|
17
|
-
(a.depth - b.depth) || localeCompare(a.path, b.path)
|
|
16
|
+
const sortNodes = (a, b) => (a.depth - b.depth) || localeCompare(a.path, b.path)
|
|
18
17
|
|
|
19
18
|
const _checkBins = Symbol.for('checkBins')
|
|
20
19
|
|
|
@@ -250,7 +249,9 @@ module.exports = cls => class Builder extends cls {
|
|
|
250
249
|
// add to the set then remove while we're reading the pj, so we
|
|
251
250
|
// don't accidentally hit it multiple times.
|
|
252
251
|
set.add(node)
|
|
253
|
-
const pkg = await
|
|
252
|
+
const { content: pkg } = await PackageJson.normalize(node.path).catch(() => {
|
|
253
|
+
return { content: {} }
|
|
254
|
+
})
|
|
254
255
|
set.delete(node)
|
|
255
256
|
|
|
256
257
|
const { scripts = {} } = pkg
|
package/lib/arborist/reify.js
CHANGED
|
@@ -1,48 +1,36 @@
|
|
|
1
1
|
// mixin implementing the reify method
|
|
2
|
-
const onExit = require('../signal-handling.js')
|
|
3
|
-
const pacote = require('pacote')
|
|
4
|
-
const AuditReport = require('../audit-report.js')
|
|
5
|
-
const { subset, intersects } = require('semver')
|
|
6
|
-
const npa = require('npm-package-arg')
|
|
7
|
-
const semver = require('semver')
|
|
8
|
-
const debug = require('../debug.js')
|
|
9
|
-
const { walkUp } = require('walk-up-path')
|
|
10
|
-
const { log, time } = require('proc-log')
|
|
11
|
-
const rpj = require('read-package-json-fast')
|
|
12
|
-
const hgi = require('hosted-git-info')
|
|
13
|
-
|
|
14
|
-
const { dirname, resolve, relative, join } = require('node:path')
|
|
15
|
-
const { depth: dfwalk } = require('treeverse')
|
|
16
|
-
const {
|
|
17
|
-
lstat,
|
|
18
|
-
mkdir,
|
|
19
|
-
rm,
|
|
20
|
-
symlink,
|
|
21
|
-
} = require('node:fs/promises')
|
|
22
|
-
const { moveFile } = require('@npmcli/fs')
|
|
23
2
|
const PackageJson = require('@npmcli/package-json')
|
|
3
|
+
const hgi = require('hosted-git-info')
|
|
4
|
+
const npa = require('npm-package-arg')
|
|
24
5
|
const packageContents = require('@npmcli/installed-package-contents')
|
|
6
|
+
const pacote = require('pacote')
|
|
7
|
+
const promiseAllRejectLate = require('promise-all-reject-late')
|
|
25
8
|
const runScript = require('@npmcli/run-script')
|
|
26
|
-
const {
|
|
9
|
+
const { callLimit: promiseCallLimit } = require('promise-call-limit')
|
|
10
|
+
const { depth: dfwalk } = require('treeverse')
|
|
11
|
+
const { dirname, resolve, relative, join } = require('node:path')
|
|
12
|
+
const { log, time } = require('proc-log')
|
|
13
|
+
const { lstat, mkdir, rm, symlink } = require('node:fs/promises')
|
|
14
|
+
const { moveFile } = require('@npmcli/fs')
|
|
15
|
+
const { subset, intersects } = require('semver')
|
|
16
|
+
const { walkUp } = require('walk-up-path')
|
|
27
17
|
|
|
28
|
-
const
|
|
29
|
-
const relpath = require('../relpath.js')
|
|
18
|
+
const AuditReport = require('../audit-report.js')
|
|
30
19
|
const Diff = require('../diff.js')
|
|
31
|
-
const retirePath = require('../retire-path.js')
|
|
32
|
-
const promiseAllRejectLate = require('promise-all-reject-late')
|
|
33
|
-
const { callLimit: promiseCallLimit } = require('promise-call-limit')
|
|
34
|
-
const optionalSet = require('../optional-set.js')
|
|
35
20
|
const calcDepFlags = require('../calc-dep-flags.js')
|
|
21
|
+
const debug = require('../debug.js')
|
|
22
|
+
const onExit = require('../signal-handling.js')
|
|
23
|
+
const optionalSet = require('../optional-set.js')
|
|
24
|
+
const relpath = require('../relpath.js')
|
|
25
|
+
const retirePath = require('../retire-path.js')
|
|
26
|
+
const treeCheck = require('../tree-check.js')
|
|
27
|
+
const { defaultLockfileVersion } = require('../shrinkwrap.js')
|
|
36
28
|
const { saveTypeMap, hasSubKey } = require('../add-rm-pkg-deps.js')
|
|
37
29
|
|
|
38
|
-
const Shrinkwrap = require('../shrinkwrap.js')
|
|
39
|
-
const { defaultLockfileVersion } = Shrinkwrap
|
|
40
|
-
|
|
41
30
|
// Part of steps (steps need refactoring before we can do anything about these)
|
|
42
31
|
const _retireShallowNodes = Symbol.for('retireShallowNodes')
|
|
43
32
|
const _loadBundlesAndUpdateTrees = Symbol.for('loadBundlesAndUpdateTrees')
|
|
44
33
|
const _submitQuickAudit = Symbol('submitQuickAudit')
|
|
45
|
-
const _addOmitsToTrashList = Symbol('addOmitsToTrashList')
|
|
46
34
|
const _unpackNewModules = Symbol.for('unpackNewModules')
|
|
47
35
|
const _build = Symbol.for('build')
|
|
48
36
|
|
|
@@ -141,11 +129,17 @@ module.exports = cls => class Reifier extends cls {
|
|
|
141
129
|
this.idealTree = oldTree
|
|
142
130
|
}
|
|
143
131
|
await this[_saveIdealTree](options)
|
|
132
|
+
// clean inert
|
|
133
|
+
for (const node of this.idealTree.inventory.values()) {
|
|
134
|
+
if (node.inert) {
|
|
135
|
+
node.parent = null
|
|
136
|
+
}
|
|
137
|
+
}
|
|
144
138
|
// clean up any trash that is still in the tree
|
|
145
139
|
for (const path of this[_trashList]) {
|
|
146
140
|
const loc = relpath(this.idealTree.realpath, path)
|
|
147
141
|
const node = this.idealTree.inventory.get(loc)
|
|
148
|
-
if (node && node.root === this.idealTree
|
|
142
|
+
if (node && node.root === this.idealTree) {
|
|
149
143
|
node.parent = null
|
|
150
144
|
}
|
|
151
145
|
}
|
|
@@ -233,18 +227,6 @@ module.exports = cls => class Reifier extends cls {
|
|
|
233
227
|
this.idealTree.meta.hiddenLockfile = true
|
|
234
228
|
this.idealTree.meta.lockfileVersion = defaultLockfileVersion
|
|
235
229
|
|
|
236
|
-
// Preserve inertness for failed stuff.
|
|
237
|
-
if (this.actualTree) {
|
|
238
|
-
for (const [loc, actual] of this.actualTree.inventory.entries()) {
|
|
239
|
-
if (actual.ideallyInert) {
|
|
240
|
-
const ideal = this.idealTree.inventory.get(loc)
|
|
241
|
-
if (ideal) {
|
|
242
|
-
ideal.ideallyInert = true
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
|
|
248
230
|
this.actualTree = this.idealTree
|
|
249
231
|
this.idealTree = null
|
|
250
232
|
|
|
@@ -315,7 +297,6 @@ module.exports = cls => class Reifier extends cls {
|
|
|
315
297
|
]],
|
|
316
298
|
[_rollbackCreateSparseTree, [
|
|
317
299
|
_createSparseTree,
|
|
318
|
-
_addOmitsToTrashList,
|
|
319
300
|
_loadShrinkwrapsAndUpdateTrees,
|
|
320
301
|
_loadBundlesAndUpdateTrees,
|
|
321
302
|
_submitQuickAudit,
|
|
@@ -470,6 +451,7 @@ module.exports = cls => class Reifier extends cls {
|
|
|
470
451
|
// find all the nodes that need to change between the actual
|
|
471
452
|
// and ideal trees.
|
|
472
453
|
this.diff = Diff.calculate({
|
|
454
|
+
omit: this.#omit,
|
|
473
455
|
shrinkwrapInflated: this.#shrinkwrapInflated,
|
|
474
456
|
filterNodes,
|
|
475
457
|
actual: this.actualTree,
|
|
@@ -554,37 +536,6 @@ module.exports = cls => class Reifier extends cls {
|
|
|
554
536
|
})
|
|
555
537
|
}
|
|
556
538
|
|
|
557
|
-
// adding to the trash list will skip reifying, and delete them
|
|
558
|
-
// if they are currently in the tree and otherwise untouched.
|
|
559
|
-
[_addOmitsToTrashList] () {
|
|
560
|
-
if (!this.#omit.size) {
|
|
561
|
-
return
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
const timeEnd = time.start('reify:trashOmits')
|
|
565
|
-
for (const node of this.idealTree.inventory.values()) {
|
|
566
|
-
const { top } = node
|
|
567
|
-
|
|
568
|
-
// if the top is not the root or workspace then we do not want to omit it
|
|
569
|
-
if (!top.isProjectRoot && !top.isWorkspace) {
|
|
570
|
-
continue
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
// if a diff filter has been created, then we do not omit the node if the
|
|
574
|
-
// top node is not in that set
|
|
575
|
-
if (this.diff?.filterSet?.size && !this.diff.filterSet.has(top)) {
|
|
576
|
-
continue
|
|
577
|
-
}
|
|
578
|
-
|
|
579
|
-
// omit node if the dep type matches any omit flags that were set
|
|
580
|
-
if (node.shouldOmit(this.#omit)) {
|
|
581
|
-
this[_addNodeToTrashList](node)
|
|
582
|
-
}
|
|
583
|
-
}
|
|
584
|
-
|
|
585
|
-
timeEnd()
|
|
586
|
-
}
|
|
587
|
-
|
|
588
539
|
[_createSparseTree] () {
|
|
589
540
|
const timeEnd = time.start('reify:createSparse')
|
|
590
541
|
// if we call this fn again, we look for the previous list
|
|
@@ -601,9 +552,6 @@ module.exports = cls => class Reifier extends cls {
|
|
|
601
552
|
// retire the same path at the same time.
|
|
602
553
|
const dirsChecked = new Set()
|
|
603
554
|
return promiseAllRejectLate(leaves.map(async node => {
|
|
604
|
-
if (node.ideallyInert) {
|
|
605
|
-
return
|
|
606
|
-
}
|
|
607
555
|
for (const d of walkUp(node.path)) {
|
|
608
556
|
if (d === node.top.path) {
|
|
609
557
|
break
|
|
@@ -683,7 +631,6 @@ module.exports = cls => class Reifier extends cls {
|
|
|
683
631
|
// reload the diff and sparse tree because the ideal tree changed
|
|
684
632
|
.then(() => this[_diffTrees]())
|
|
685
633
|
.then(() => this[_createSparseTree]())
|
|
686
|
-
.then(() => this[_addOmitsToTrashList]())
|
|
687
634
|
.then(() => this[_loadShrinkwrapsAndUpdateTrees]())
|
|
688
635
|
.then(timeEnd)
|
|
689
636
|
}
|
|
@@ -691,30 +638,14 @@ module.exports = cls => class Reifier extends cls {
|
|
|
691
638
|
// create a symlink for Links, extract for Nodes
|
|
692
639
|
// return the node object, since we usually want that
|
|
693
640
|
// handle optional dep failures here
|
|
694
|
-
// If node is in trash list, skip it
|
|
695
641
|
// If reifying fails, and the node is optional, add it and its optionalSet
|
|
696
642
|
// to the trash list
|
|
697
643
|
// Always return the node.
|
|
698
644
|
[_reifyNode] (node) {
|
|
699
|
-
if (this[_trashList].has(node.path)) {
|
|
700
|
-
return node
|
|
701
|
-
}
|
|
702
|
-
|
|
703
645
|
const timeEnd = time.start(`reifyNode:${node.location}`)
|
|
704
646
|
this.addTracker('reify', node.name, node.location)
|
|
705
647
|
|
|
706
|
-
const { npmVersion, nodeVersion, cpu, os, libc } = this.options
|
|
707
648
|
const p = Promise.resolve().then(async () => {
|
|
708
|
-
// when we reify an optional node, check the engine and platform
|
|
709
|
-
// first. be sure to ignore the --force and --engine-strict flags,
|
|
710
|
-
// since we always want to skip any optional packages we can't install.
|
|
711
|
-
// these checks throwing will result in a rollback and removal
|
|
712
|
-
// of the mismatches
|
|
713
|
-
// eslint-disable-next-line promise/always-return
|
|
714
|
-
if (node.optional) {
|
|
715
|
-
checkEngine(node.package, npmVersion, nodeVersion, false)
|
|
716
|
-
checkPlatform(node.package, false, { cpu, os, libc })
|
|
717
|
-
}
|
|
718
649
|
await this[_checkBins](node)
|
|
719
650
|
await this.#extractOrLink(node)
|
|
720
651
|
const { _id, deprecated } = node.package
|
|
@@ -748,10 +679,6 @@ module.exports = cls => class Reifier extends cls {
|
|
|
748
679
|
}
|
|
749
680
|
|
|
750
681
|
async #extractOrLink (node) {
|
|
751
|
-
if (node.ideallyInert) {
|
|
752
|
-
return
|
|
753
|
-
}
|
|
754
|
-
|
|
755
682
|
const nm = resolve(node.parent.path, 'node_modules')
|
|
756
683
|
await this.#validateNodeModules(nm)
|
|
757
684
|
|
|
@@ -803,7 +730,7 @@ module.exports = cls => class Reifier extends cls {
|
|
|
803
730
|
})
|
|
804
731
|
// store nodes don't use Node class so node.package doesn't get updated
|
|
805
732
|
if (node.isInStore) {
|
|
806
|
-
const pkg = await
|
|
733
|
+
const { content: pkg } = await PackageJson.normalize(node.path)
|
|
807
734
|
node.package.scripts = pkg.scripts
|
|
808
735
|
}
|
|
809
736
|
return
|
|
@@ -832,9 +759,9 @@ module.exports = cls => class Reifier extends cls {
|
|
|
832
759
|
[_handleOptionalFailure] (node, p) {
|
|
833
760
|
return (node.optional ? p.catch(() => {
|
|
834
761
|
const set = optionalSet(node)
|
|
835
|
-
for (node of set) {
|
|
762
|
+
for (const node of set) {
|
|
836
763
|
log.verbose('reify', 'failed optional dependency', node.path)
|
|
837
|
-
node.
|
|
764
|
+
node.inert = true
|
|
838
765
|
this[_addNodeToTrashList](node)
|
|
839
766
|
}
|
|
840
767
|
}) : p).then(() => node)
|
|
@@ -1206,9 +1133,6 @@ module.exports = cls => class Reifier extends cls {
|
|
|
1206
1133
|
|
|
1207
1134
|
this.#retiredUnchanged[retireFolder] = []
|
|
1208
1135
|
return promiseAllRejectLate(diff.unchanged.map(node => {
|
|
1209
|
-
if (node.ideallyInert) {
|
|
1210
|
-
return
|
|
1211
|
-
}
|
|
1212
1136
|
// no need to roll back links, since we'll just delete them anyway
|
|
1213
1137
|
if (node.isLink) {
|
|
1214
1138
|
return mkdir(dirname(node.path), { recursive: true, force: true })
|
|
@@ -1288,7 +1212,7 @@ module.exports = cls => class Reifier extends cls {
|
|
|
1288
1212
|
// skip links that only live within node_modules as they are most
|
|
1289
1213
|
// likely managed by packages we installed, we only want to rebuild
|
|
1290
1214
|
// unchanged links we directly manage
|
|
1291
|
-
const linkedFromRoot = (node.parent === tree && !node.
|
|
1215
|
+
const linkedFromRoot = (node.parent === tree && !node.inert) || node.target.fsTop === tree
|
|
1292
1216
|
if (node.isLink && linkedFromRoot) {
|
|
1293
1217
|
nodes.push(node)
|
|
1294
1218
|
}
|
|
@@ -1399,7 +1323,7 @@ module.exports = cls => class Reifier extends cls {
|
|
|
1399
1323
|
const alias = name !== pname
|
|
1400
1324
|
newSpec = alias ? `npm:${pname}@${range}` : range
|
|
1401
1325
|
} else if (req.hosted) {
|
|
1402
|
-
// save the git+https url if it has auth
|
|
1326
|
+
// save the git+https url if it has auth; otherwise, shortcut
|
|
1403
1327
|
const h = req.hosted
|
|
1404
1328
|
const opt = { noCommittish: false }
|
|
1405
1329
|
if (h.https && h.auth) {
|
|
@@ -1432,8 +1356,7 @@ module.exports = cls => class Reifier extends cls {
|
|
|
1432
1356
|
if (options.saveType) {
|
|
1433
1357
|
const depType = saveTypeMap.get(options.saveType)
|
|
1434
1358
|
pkg[depType][name] = newSpec
|
|
1435
|
-
//
|
|
1436
|
-
// if it is empty it will be deleted later
|
|
1359
|
+
// PackageJson.normalize will have moved it here if it was in both, if it is empty it will be deleted later
|
|
1437
1360
|
if (options.saveType === 'prod' && pkg.optionalDependencies) {
|
|
1438
1361
|
delete pkg.optionalDependencies[name]
|
|
1439
1362
|
}
|
|
@@ -1469,12 +1392,12 @@ module.exports = cls => class Reifier extends cls {
|
|
|
1469
1392
|
|
|
1470
1393
|
// Returns true if any of the edges from this node has a semver
|
|
1471
1394
|
// range definition that is an exact match to the version installed
|
|
1472
|
-
// e.g: should return true if for a given
|
|
1395
|
+
// e.g: should return true if for a given and installed version 1.0.0,
|
|
1473
1396
|
// range is either =1.0.0 or 1.0.0
|
|
1474
1397
|
const exactVersion = node => {
|
|
1475
1398
|
for (const edge of node.edgesIn) {
|
|
1476
1399
|
try {
|
|
1477
|
-
if (
|
|
1400
|
+
if (subset(edge.spec, node.version)) {
|
|
1478
1401
|
return false
|
|
1479
1402
|
}
|
|
1480
1403
|
} catch {
|
package/lib/calc-dep-flags.js
CHANGED
|
@@ -22,6 +22,7 @@ const calcDepFlagsStep = (node) => {
|
|
|
22
22
|
// or normal dependency graphs overlap deep in the dep graph.
|
|
23
23
|
// Since we're only walking through deps that are not already flagged
|
|
24
24
|
// as non-dev/non-optional, it's typically a very shallow traversal
|
|
25
|
+
|
|
25
26
|
node.extraneous = false
|
|
26
27
|
resetParents(node, 'extraneous')
|
|
27
28
|
resetParents(node, 'dev')
|
|
@@ -47,10 +48,16 @@ const calcDepFlagsStep = (node) => {
|
|
|
47
48
|
if (!to) {
|
|
48
49
|
return
|
|
49
50
|
}
|
|
50
|
-
|
|
51
51
|
// everything with any kind of edge into it is not extraneous
|
|
52
52
|
to.extraneous = false
|
|
53
53
|
|
|
54
|
+
// If this is a peer edge, mark the target as peer
|
|
55
|
+
if (peer) {
|
|
56
|
+
to.peer = true
|
|
57
|
+
} else if (to.peer && !hasIncomingPeerEdge(to)) {
|
|
58
|
+
unsetFlag(to, 'peer')
|
|
59
|
+
}
|
|
60
|
+
|
|
54
61
|
// devOptional is the *overlap* of the dev and optional tree.
|
|
55
62
|
// however, for convenience and to save an extra rewalk, we leave
|
|
56
63
|
// it set when we are in *either* tree, and then omit it from the
|
|
@@ -61,11 +68,6 @@ const calcDepFlagsStep = (node) => {
|
|
|
61
68
|
// either the dev or opt trees
|
|
62
69
|
const unsetDev = unsetDevOpt || !node.dev && !dev
|
|
63
70
|
const unsetOpt = unsetDevOpt || !node.optional && !optional
|
|
64
|
-
const unsetPeer = !node.peer && !peer
|
|
65
|
-
|
|
66
|
-
if (unsetPeer) {
|
|
67
|
-
unsetFlag(to, 'peer')
|
|
68
|
-
}
|
|
69
71
|
|
|
70
72
|
if (unsetDevOpt) {
|
|
71
73
|
unsetFlag(to, 'devOptional')
|
|
@@ -83,6 +85,16 @@ const calcDepFlagsStep = (node) => {
|
|
|
83
85
|
return node
|
|
84
86
|
}
|
|
85
87
|
|
|
88
|
+
const hasIncomingPeerEdge = (node) => {
|
|
89
|
+
const target = node.isLink && node.target ? node.target : node
|
|
90
|
+
for (const edge of target.edgesIn) {
|
|
91
|
+
if (edge.type === 'peer') {
|
|
92
|
+
return true
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return false
|
|
96
|
+
}
|
|
97
|
+
|
|
86
98
|
const resetParents = (node, flag) => {
|
|
87
99
|
if (node[flag]) {
|
|
88
100
|
return
|
|
@@ -109,12 +121,19 @@ const unsetFlag = (node, flag) => {
|
|
|
109
121
|
const children = []
|
|
110
122
|
const targetNode = node.isLink && node.target ? node.target : node
|
|
111
123
|
for (const edge of targetNode.edgesOut.values()) {
|
|
112
|
-
if (
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
(
|
|
116
|
-
|
|
117
|
-
|
|
124
|
+
if (edge.to?.[flag]) {
|
|
125
|
+
// For the peer flag, only follow peer edges to unset the flag
|
|
126
|
+
// Don't propagate peer flag through prod/dev/optional edges
|
|
127
|
+
if (flag === 'peer') {
|
|
128
|
+
if (edge.type === 'peer') {
|
|
129
|
+
children.push(edge.to)
|
|
130
|
+
}
|
|
131
|
+
} else {
|
|
132
|
+
// For other flags, follow prod edges (and peer edges for non-peer flags)
|
|
133
|
+
if (edge.type === 'prod' || edge.type === 'peer') {
|
|
134
|
+
children.push(edge.to)
|
|
135
|
+
}
|
|
136
|
+
}
|
|
118
137
|
}
|
|
119
138
|
}
|
|
120
139
|
return children
|
package/lib/diff.js
CHANGED
|
@@ -11,7 +11,8 @@ const { existsSync } = require('node:fs')
|
|
|
11
11
|
const ssri = require('ssri')
|
|
12
12
|
|
|
13
13
|
class Diff {
|
|
14
|
-
constructor ({ actual, ideal, filterSet, shrinkwrapInflated }) {
|
|
14
|
+
constructor ({ actual, ideal, filterSet, shrinkwrapInflated, omit }) {
|
|
15
|
+
this.omit = omit
|
|
15
16
|
this.filterSet = filterSet
|
|
16
17
|
this.shrinkwrapInflated = shrinkwrapInflated
|
|
17
18
|
this.children = []
|
|
@@ -36,6 +37,7 @@ class Diff {
|
|
|
36
37
|
ideal,
|
|
37
38
|
filterNodes = [],
|
|
38
39
|
shrinkwrapInflated = new Set(),
|
|
40
|
+
omit = new Set(),
|
|
39
41
|
}) {
|
|
40
42
|
// if there's a filterNode, then:
|
|
41
43
|
// - get the path from the root to the filterNode. The root or
|
|
@@ -94,7 +96,7 @@ class Diff {
|
|
|
94
96
|
}
|
|
95
97
|
|
|
96
98
|
return depth({
|
|
97
|
-
tree: new Diff({ actual, ideal, filterSet, shrinkwrapInflated }),
|
|
99
|
+
tree: new Diff({ actual, ideal, filterSet, shrinkwrapInflated, omit }),
|
|
98
100
|
getChildren,
|
|
99
101
|
leave,
|
|
100
102
|
})
|
|
@@ -184,6 +186,7 @@ const getChildren = diff => {
|
|
|
184
186
|
removed,
|
|
185
187
|
filterSet,
|
|
186
188
|
shrinkwrapInflated,
|
|
189
|
+
omit,
|
|
187
190
|
} = diff
|
|
188
191
|
|
|
189
192
|
// Note: we DON'T diff fsChildren themselves, because they are either
|
|
@@ -214,6 +217,7 @@ const getChildren = diff => {
|
|
|
214
217
|
removed,
|
|
215
218
|
filterSet,
|
|
216
219
|
shrinkwrapInflated,
|
|
220
|
+
omit,
|
|
217
221
|
})
|
|
218
222
|
}
|
|
219
223
|
|
|
@@ -232,11 +236,24 @@ const diffNode = ({
|
|
|
232
236
|
removed,
|
|
233
237
|
filterSet,
|
|
234
238
|
shrinkwrapInflated,
|
|
239
|
+
omit,
|
|
235
240
|
}) => {
|
|
236
241
|
if (filterSet.size && !(filterSet.has(ideal) || filterSet.has(actual))) {
|
|
237
242
|
return
|
|
238
243
|
}
|
|
239
244
|
|
|
245
|
+
if (ideal?.shouldOmit?.(omit)) {
|
|
246
|
+
ideal.inert = true
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Treat inert nodes as undefined for the purposes of diffing.
|
|
250
|
+
if (ideal?.inert) {
|
|
251
|
+
ideal = undefined
|
|
252
|
+
}
|
|
253
|
+
if (!actual && !ideal) {
|
|
254
|
+
return
|
|
255
|
+
}
|
|
256
|
+
|
|
240
257
|
const action = getAction({ actual, ideal })
|
|
241
258
|
|
|
242
259
|
// if it's a match, then get its children
|
|
@@ -245,7 +262,7 @@ const diffNode = ({
|
|
|
245
262
|
if (action === 'REMOVE') {
|
|
246
263
|
removed.push(actual)
|
|
247
264
|
}
|
|
248
|
-
children.push(new Diff({ actual, ideal, filterSet, shrinkwrapInflated }))
|
|
265
|
+
children.push(new Diff({ actual, ideal, filterSet, shrinkwrapInflated, omit }))
|
|
249
266
|
} else {
|
|
250
267
|
unchanged.push(ideal)
|
|
251
268
|
// !*! Weird dirty hack warning !*!
|
|
@@ -285,6 +302,7 @@ const diffNode = ({
|
|
|
285
302
|
removed,
|
|
286
303
|
filterSet,
|
|
287
304
|
shrinkwrapInflated,
|
|
305
|
+
omit,
|
|
288
306
|
}))
|
|
289
307
|
}
|
|
290
308
|
}
|
package/lib/gather-dep-set.js
CHANGED
|
@@ -20,7 +20,7 @@ const gatherDepSet = (set, edgeFilter) => {
|
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
// now remove all nodes in the set that have a
|
|
23
|
+
// now remove all nodes in the set that have a dependent outside the set
|
|
24
24
|
// if any change is made, then re-check
|
|
25
25
|
// continue until no changes made, or deps set evaporates fully.
|
|
26
26
|
let changed = true
|
package/lib/node.js
CHANGED
|
@@ -28,22 +28,28 @@
|
|
|
28
28
|
// where we need to quickly find all instances of a given package name within a
|
|
29
29
|
// tree.
|
|
30
30
|
|
|
31
|
-
const
|
|
31
|
+
const PackageJson = require('@npmcli/package-json')
|
|
32
32
|
const nameFromFolder = require('@npmcli/name-from-folder')
|
|
33
|
+
const npa = require('npm-package-arg')
|
|
34
|
+
const semver = require('semver')
|
|
35
|
+
const util = require('node:util')
|
|
36
|
+
const { getPaths: getBinPaths } = require('bin-links')
|
|
37
|
+
const { log } = require('proc-log')
|
|
38
|
+
const { resolve, relative, dirname, basename } = require('node:path')
|
|
39
|
+
const { walkUp } = require('walk-up-path')
|
|
40
|
+
|
|
41
|
+
const CaseInsensitiveMap = require('./case-insensitive-map.js')
|
|
33
42
|
const Edge = require('./edge.js')
|
|
34
43
|
const Inventory = require('./inventory.js')
|
|
35
44
|
const OverrideSet = require('./override-set.js')
|
|
36
|
-
const
|
|
37
|
-
const { getPaths: getBinPaths } = require('bin-links')
|
|
38
|
-
const npa = require('npm-package-arg')
|
|
45
|
+
const consistentResolve = require('./consistent-resolve.js')
|
|
39
46
|
const debug = require('./debug.js')
|
|
40
47
|
const gatherDepSet = require('./gather-dep-set.js')
|
|
48
|
+
const printableTree = require('./printable.js')
|
|
49
|
+
const querySelectorAll = require('./query-selector-all.js')
|
|
50
|
+
const relpath = require('./relpath.js')
|
|
41
51
|
const treeCheck = require('./tree-check.js')
|
|
42
|
-
const { walkUp } = require('walk-up-path')
|
|
43
|
-
const { log } = require('proc-log')
|
|
44
52
|
|
|
45
|
-
const { resolve, relative, dirname, basename } = require('node:path')
|
|
46
|
-
const util = require('node:util')
|
|
47
53
|
const _package = Symbol('_package')
|
|
48
54
|
const _parent = Symbol('_parent')
|
|
49
55
|
const _target = Symbol.for('_target')
|
|
@@ -58,14 +64,6 @@ const _delistFromMeta = Symbol.for('_delistFromMeta')
|
|
|
58
64
|
const _explain = Symbol('_explain')
|
|
59
65
|
const _explanation = Symbol('_explanation')
|
|
60
66
|
|
|
61
|
-
const relpath = require('./relpath.js')
|
|
62
|
-
const consistentResolve = require('./consistent-resolve.js')
|
|
63
|
-
|
|
64
|
-
const printableTree = require('./printable.js')
|
|
65
|
-
const CaseInsensitiveMap = require('./case-insensitive-map.js')
|
|
66
|
-
|
|
67
|
-
const querySelectorAll = require('./query-selector-all.js')
|
|
68
|
-
|
|
69
67
|
class Node {
|
|
70
68
|
#global
|
|
71
69
|
#meta
|
|
@@ -103,7 +101,7 @@ class Node {
|
|
|
103
101
|
global = false,
|
|
104
102
|
dummy = false,
|
|
105
103
|
sourceReference = null,
|
|
106
|
-
|
|
104
|
+
inert = false,
|
|
107
105
|
} = options
|
|
108
106
|
// this object gives querySelectorAll somewhere to stash context about a node
|
|
109
107
|
// while processing a query
|
|
@@ -121,14 +119,25 @@ class Node {
|
|
|
121
119
|
// package's dependencies in a virtual root.
|
|
122
120
|
this.sourceReference = sourceReference
|
|
123
121
|
|
|
124
|
-
//
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
122
|
+
// have to set the internal package ref before assigning the parent, because this.package is read when adding to inventory
|
|
123
|
+
if (sourceReference) {
|
|
124
|
+
this[_package] = sourceReference.package
|
|
125
|
+
} else {
|
|
126
|
+
// TODO if this came from pacote.manifest we don't have to do this, we can be told to skip this step
|
|
127
|
+
const pkg = new PackageJson()
|
|
128
|
+
let content = {}
|
|
129
|
+
// TODO this is overly guarded. If pkg is not an object we should not allow it at all.
|
|
130
|
+
if (options.pkg && typeof options.pkg === 'object') {
|
|
131
|
+
content = options.pkg
|
|
132
|
+
}
|
|
133
|
+
pkg.fromContent(content)
|
|
134
|
+
pkg.syncNormalize()
|
|
135
|
+
this[_package] = pkg.content
|
|
136
|
+
}
|
|
128
137
|
|
|
129
138
|
this.name = name ||
|
|
130
|
-
nameFromFolder(path ||
|
|
131
|
-
|
|
139
|
+
nameFromFolder(path || this.package.name || realpath) ||
|
|
140
|
+
this.package.name ||
|
|
132
141
|
null
|
|
133
142
|
|
|
134
143
|
// should be equal if not a link
|
|
@@ -156,13 +165,13 @@ class Node {
|
|
|
156
165
|
// probably what we're getting from pacote, which IS trustworthy.
|
|
157
166
|
//
|
|
158
167
|
// Otherwise, hopefully a shrinkwrap will help us out.
|
|
159
|
-
const resolved = consistentResolve(
|
|
160
|
-
if (resolved && !(/^file:/.test(resolved) &&
|
|
168
|
+
const resolved = consistentResolve(this.package._resolved)
|
|
169
|
+
if (resolved && !(/^file:/.test(resolved) && this.package._where)) {
|
|
161
170
|
this.resolved = resolved
|
|
162
171
|
}
|
|
163
172
|
}
|
|
164
|
-
this.integrity = integrity ||
|
|
165
|
-
this.hasShrinkwrap = hasShrinkwrap ||
|
|
173
|
+
this.integrity = integrity || this.package._integrity || null
|
|
174
|
+
this.hasShrinkwrap = hasShrinkwrap || this.package._hasShrinkwrap || false
|
|
166
175
|
this.installLinks = installLinks
|
|
167
176
|
this.legacyPeerDeps = legacyPeerDeps
|
|
168
177
|
|
|
@@ -198,22 +207,18 @@ class Node {
|
|
|
198
207
|
this.extraneous = false
|
|
199
208
|
}
|
|
200
209
|
|
|
201
|
-
this.
|
|
210
|
+
this.inert = inert
|
|
202
211
|
|
|
203
212
|
this.edgesIn = new Set()
|
|
204
213
|
this.edgesOut = new CaseInsensitiveMap()
|
|
205
214
|
|
|
206
|
-
// have to set the internal package ref before assigning the parent,
|
|
207
|
-
// because this.package is read when adding to inventory
|
|
208
|
-
this[_package] = pkg && typeof pkg === 'object' ? pkg : {}
|
|
209
|
-
|
|
210
215
|
if (overrides) {
|
|
211
216
|
this.overrides = overrides
|
|
212
217
|
} else if (loadOverrides) {
|
|
213
|
-
const overrides = this
|
|
218
|
+
const overrides = this.package.overrides || {}
|
|
214
219
|
if (Object.keys(overrides).length > 0) {
|
|
215
220
|
this.overrides = new OverrideSet({
|
|
216
|
-
overrides: this
|
|
221
|
+
overrides: this.package.overrides,
|
|
217
222
|
})
|
|
218
223
|
}
|
|
219
224
|
}
|
|
@@ -243,7 +248,7 @@ class Node {
|
|
|
243
248
|
this.fsParent = fsParent || null
|
|
244
249
|
|
|
245
250
|
// see parent/root setters below.
|
|
246
|
-
// root is set to parent's root if we have a parent
|
|
251
|
+
// root is set to parent's root if we have a parent; otherwise, if it's
|
|
247
252
|
// null, then it's set to the node itself.
|
|
248
253
|
if (!parent && !fsParent) {
|
|
249
254
|
this.root = root || null
|
|
@@ -314,7 +319,7 @@ class Node {
|
|
|
314
319
|
}
|
|
315
320
|
|
|
316
321
|
return getBinPaths({
|
|
317
|
-
pkg: this
|
|
322
|
+
pkg: this.package,
|
|
318
323
|
path: this.path,
|
|
319
324
|
global: this.global,
|
|
320
325
|
top: this.globalTop,
|
|
@@ -328,11 +333,11 @@ class Node {
|
|
|
328
333
|
}
|
|
329
334
|
|
|
330
335
|
get version () {
|
|
331
|
-
return this
|
|
336
|
+
return this.package.version || ''
|
|
332
337
|
}
|
|
333
338
|
|
|
334
339
|
get packageName () {
|
|
335
|
-
return this
|
|
340
|
+
return this.package.name || null
|
|
336
341
|
}
|
|
337
342
|
|
|
338
343
|
get pkgid () {
|
|
@@ -490,6 +495,18 @@ class Node {
|
|
|
490
495
|
}
|
|
491
496
|
|
|
492
497
|
shouldOmit (omitSet) {
|
|
498
|
+
if (!omitSet.size) {
|
|
499
|
+
return false
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
const { top } = this
|
|
503
|
+
|
|
504
|
+
// if the top is not the root or workspace then we do not want to omit it
|
|
505
|
+
if (!top.isProjectRoot && !top.isWorkspace) {
|
|
506
|
+
return false
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
// omit node if the dep type matches any omit flags that were set
|
|
493
510
|
return (
|
|
494
511
|
this.peer && omitSet.has('peer') ||
|
|
495
512
|
this.dev && omitSet.has('dev') ||
|
|
@@ -815,7 +832,7 @@ class Node {
|
|
|
815
832
|
edge.reload()
|
|
816
833
|
}
|
|
817
834
|
}
|
|
818
|
-
// reload all edgesOut where root
|
|
835
|
+
// reload all edgesOut where root doesn't match, or is missing, since
|
|
819
836
|
// it might not be missing in the new tree
|
|
820
837
|
for (const edge of this.edgesOut.values()) {
|
|
821
838
|
if (!edge.to || edge.to.root !== root) {
|
|
@@ -1251,7 +1268,7 @@ class Node {
|
|
|
1251
1268
|
// with another by the same name (eg, to update or dedupe).
|
|
1252
1269
|
// This does a couple of walks out on the node_modules tree, recursing
|
|
1253
1270
|
// into child nodes. However, as setting the parent is typically done
|
|
1254
|
-
// with nodes that don't have
|
|
1271
|
+
// with nodes that don't have many children, and (deduped) package
|
|
1255
1272
|
// trees tend to be broad rather than deep, it's not that bad.
|
|
1256
1273
|
// The only walk that starts from the parent rather than this node is
|
|
1257
1274
|
// limited by edge name.
|
|
@@ -1395,7 +1412,7 @@ class Node {
|
|
|
1395
1412
|
}
|
|
1396
1413
|
|
|
1397
1414
|
recalculateOutEdgesOverrides () {
|
|
1398
|
-
// For each edge out
|
|
1415
|
+
// For each edge out propagate the new overrides through.
|
|
1399
1416
|
for (const edge of this.edgesOut.values()) {
|
|
1400
1417
|
edge.reload(true)
|
|
1401
1418
|
if (edge.to) {
|
package/lib/optional-set.js
CHANGED
|
@@ -29,10 +29,8 @@ const optionalSet = node => {
|
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
// now that we've hit the boundary, gather the rest of the nodes in
|
|
32
|
-
// the optional section
|
|
33
|
-
|
|
34
|
-
// from outside the set.
|
|
35
|
-
return gatherDepSet(set, edge => !edge.optional)
|
|
32
|
+
// the optional section that don't have dependents outside the set.
|
|
33
|
+
return gatherDepSet(set, edge => !set.has(edge.to))
|
|
36
34
|
}
|
|
37
35
|
|
|
38
36
|
module.exports = optionalSet
|
package/lib/packument-cache.js
CHANGED
|
@@ -30,7 +30,7 @@ class PackumentCache extends LRUCache {
|
|
|
30
30
|
maxSize,
|
|
31
31
|
maxEntrySize,
|
|
32
32
|
sizeCalculation: (p) => {
|
|
33
|
-
// Don't cache if we
|
|
33
|
+
// Don't cache if we don't know the size
|
|
34
34
|
// Some versions of pacote set this to `0`, newer versions set it to `null`
|
|
35
35
|
if (!p[sizeKey]) {
|
|
36
36
|
return maxEntrySize + 1
|
package/lib/place-dep.js
CHANGED
|
@@ -203,7 +203,7 @@ class PlaceDep {
|
|
|
203
203
|
this.warnPeerConflict()
|
|
204
204
|
}
|
|
205
205
|
|
|
206
|
-
// if we get a KEEP in
|
|
206
|
+
// if we get a KEEP in an update scenario, then we MAY have something
|
|
207
207
|
// already duplicating this unnecessarily! For example:
|
|
208
208
|
// ```
|
|
209
209
|
// root (dep: y@1)
|
|
@@ -317,7 +317,7 @@ class PlaceDep {
|
|
|
317
317
|
force: this.force,
|
|
318
318
|
installLinks: this.installLinks,
|
|
319
319
|
installStrategy: this.installStrategy,
|
|
320
|
-
legacyPeerDeps: this.
|
|
320
|
+
legacyPeerDeps: this.legacyPeerDeps,
|
|
321
321
|
preferDedupe: this.preferDedupe,
|
|
322
322
|
strictPeerDeps: this.strictPeerDeps,
|
|
323
323
|
updateNames: this.updateName,
|
|
@@ -421,7 +421,7 @@ class PlaceDep {
|
|
|
421
421
|
// prune all the nodes in a branch of the tree that can be safely removed
|
|
422
422
|
// This is only the most basic duplication detection; it finds if there
|
|
423
423
|
// is another satisfying node further up the tree, and if so, dedupes.
|
|
424
|
-
// Even
|
|
424
|
+
// Even if installStrategy is nested, we do this amount of deduplication.
|
|
425
425
|
pruneDedupable (node, descend = true) {
|
|
426
426
|
if (node.canDedupe(this.preferDedupe, this.explicitRequest)) {
|
|
427
427
|
// gather up all deps that have no valid edges in from outside
|
|
@@ -785,7 +785,7 @@ const hasParent = (node, compareNodes) => {
|
|
|
785
785
|
compareNode = compareNode.target
|
|
786
786
|
}
|
|
787
787
|
|
|
788
|
-
// follows logical parent for link
|
|
788
|
+
// follows logical parent for link ancestors
|
|
789
789
|
if (node.isTop && (node.resolveParent === compareNode)) {
|
|
790
790
|
return true
|
|
791
791
|
}
|
package/lib/shrinkwrap.js
CHANGED
|
@@ -109,7 +109,6 @@ const nodeMetaKeys = [
|
|
|
109
109
|
'inBundle',
|
|
110
110
|
'hasShrinkwrap',
|
|
111
111
|
'hasInstallScript',
|
|
112
|
-
'ideallyInert',
|
|
113
112
|
]
|
|
114
113
|
|
|
115
114
|
const metaFieldFromPkg = (pkg, key) => {
|
|
@@ -136,10 +135,6 @@ const assertNoNewer = async (path, data, lockTime, dir, seen) => {
|
|
|
136
135
|
|
|
137
136
|
const parent = isParent ? dir : resolve(dir, 'node_modules')
|
|
138
137
|
const rel = relpath(path, dir)
|
|
139
|
-
const inert = data.packages[rel]?.ideallyInert
|
|
140
|
-
if (inert) {
|
|
141
|
-
return
|
|
142
|
-
}
|
|
143
138
|
seen.add(rel)
|
|
144
139
|
let entries
|
|
145
140
|
if (dir === path) {
|
|
@@ -178,7 +173,7 @@ const assertNoNewer = async (path, data, lockTime, dir, seen) => {
|
|
|
178
173
|
|
|
179
174
|
// assert that all the entries in the lockfile were seen
|
|
180
175
|
for (const loc in data.packages) {
|
|
181
|
-
if (!seen.has(loc)
|
|
176
|
+
if (!seen.has(loc)) {
|
|
182
177
|
throw new Error(`missing from node_modules: ${loc}`)
|
|
183
178
|
}
|
|
184
179
|
}
|
|
@@ -436,7 +431,7 @@ class Shrinkwrap {
|
|
|
436
431
|
const [sw, lock, yarn] = await this.loadFiles
|
|
437
432
|
data = sw || lock || '{}'
|
|
438
433
|
|
|
439
|
-
// use shrinkwrap only for deps
|
|
434
|
+
// use shrinkwrap only for deps; otherwise, prefer package-lock
|
|
440
435
|
// and ignore npm-shrinkwrap if both are present.
|
|
441
436
|
// TODO: emit a warning here or something if both are present.
|
|
442
437
|
if (this.hiddenLockfile) {
|
|
@@ -788,10 +783,6 @@ class Shrinkwrap {
|
|
|
788
783
|
// ok, I did my best! good luck!
|
|
789
784
|
}
|
|
790
785
|
|
|
791
|
-
if (lock.ideallyInert) {
|
|
792
|
-
meta.ideallyInert = true
|
|
793
|
-
}
|
|
794
|
-
|
|
795
786
|
if (lock.bundled) {
|
|
796
787
|
meta.inBundle = true
|
|
797
788
|
}
|
|
@@ -962,12 +953,6 @@ class Shrinkwrap {
|
|
|
962
953
|
this.#buildLegacyLockfile(this.tree, this.data)
|
|
963
954
|
}
|
|
964
955
|
|
|
965
|
-
if (!this.hiddenLockfile) {
|
|
966
|
-
for (const node of Object.values(this.data.packages)) {
|
|
967
|
-
delete node.ideallyInert
|
|
968
|
-
}
|
|
969
|
-
}
|
|
970
|
-
|
|
971
956
|
// lf version 1 = dependencies only
|
|
972
957
|
// lf version 2 = dependencies and packages
|
|
973
958
|
// lf version 3 = packages only
|
|
@@ -993,7 +978,7 @@ class Shrinkwrap {
|
|
|
993
978
|
|
|
994
979
|
// npm v6 and before tracked 'from', meaning "the request that led
|
|
995
980
|
// to this package being installed". However, that's inherently
|
|
996
|
-
//
|
|
981
|
+
// racy and non-deterministic in a world where deps are deduped
|
|
997
982
|
// ahead of fetch time. In order to maintain backwards compatibility
|
|
998
983
|
// with v6 in the lockfile, we do this trick where we pick a valid
|
|
999
984
|
// dep link out of the edgesIn set. Choose the edge with the fewest
|
package/package.json
CHANGED
|
@@ -1,38 +1,37 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@npmcli/arborist",
|
|
3
|
-
"version": "9.1.
|
|
3
|
+
"version": "9.1.6",
|
|
4
4
|
"description": "Manage node_modules trees",
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"@isaacs/string-locale-compare": "^1.1.0",
|
|
7
7
|
"@npmcli/fs": "^4.0.0",
|
|
8
8
|
"@npmcli/installed-package-contents": "^3.0.0",
|
|
9
|
-
"@npmcli/map-workspaces": "^
|
|
10
|
-
"@npmcli/metavuln-calculator": "^9.0.
|
|
9
|
+
"@npmcli/map-workspaces": "^5.0.0",
|
|
10
|
+
"@npmcli/metavuln-calculator": "^9.0.2",
|
|
11
11
|
"@npmcli/name-from-folder": "^3.0.0",
|
|
12
12
|
"@npmcli/node-gyp": "^4.0.0",
|
|
13
|
-
"@npmcli/package-json": "^
|
|
13
|
+
"@npmcli/package-json": "^7.0.0",
|
|
14
14
|
"@npmcli/query": "^4.0.0",
|
|
15
15
|
"@npmcli/redact": "^3.0.0",
|
|
16
|
-
"@npmcli/run-script": "^
|
|
16
|
+
"@npmcli/run-script": "^10.0.0",
|
|
17
17
|
"bin-links": "^5.0.0",
|
|
18
|
-
"cacache": "^
|
|
18
|
+
"cacache": "^20.0.1",
|
|
19
19
|
"common-ancestor-path": "^1.0.1",
|
|
20
|
-
"hosted-git-info": "^
|
|
20
|
+
"hosted-git-info": "^9.0.0",
|
|
21
21
|
"json-stringify-nice": "^1.1.4",
|
|
22
|
-
"lru-cache": "^
|
|
23
|
-
"minimatch": "^
|
|
22
|
+
"lru-cache": "^11.2.1",
|
|
23
|
+
"minimatch": "^10.0.3",
|
|
24
24
|
"nopt": "^8.0.0",
|
|
25
25
|
"npm-install-checks": "^7.1.0",
|
|
26
|
-
"npm-package-arg": "^
|
|
27
|
-
"npm-pick-manifest": "^
|
|
28
|
-
"npm-registry-fetch": "^
|
|
29
|
-
"pacote": "^21.0.
|
|
26
|
+
"npm-package-arg": "^13.0.0",
|
|
27
|
+
"npm-pick-manifest": "^11.0.1",
|
|
28
|
+
"npm-registry-fetch": "^19.0.0",
|
|
29
|
+
"pacote": "^21.0.2",
|
|
30
30
|
"parse-conflict-json": "^4.0.0",
|
|
31
31
|
"proc-log": "^5.0.0",
|
|
32
32
|
"proggy": "^3.0.0",
|
|
33
33
|
"promise-all-reject-late": "^1.0.0",
|
|
34
34
|
"promise-call-limit": "^3.0.1",
|
|
35
|
-
"read-package-json-fast": "^4.0.0",
|
|
36
35
|
"semver": "^7.3.7",
|
|
37
36
|
"ssri": "^12.0.0",
|
|
38
37
|
"treeverse": "^3.0.0",
|
|
@@ -41,7 +40,7 @@
|
|
|
41
40
|
"devDependencies": {
|
|
42
41
|
"@npmcli/eslint-config": "^5.0.1",
|
|
43
42
|
"@npmcli/mock-registry": "^1.0.0",
|
|
44
|
-
"@npmcli/template-oss": "4.
|
|
43
|
+
"@npmcli/template-oss": "4.25.1",
|
|
45
44
|
"benchmark": "^2.1.4",
|
|
46
45
|
"minify-registry-metadata": "^4.0.0",
|
|
47
46
|
"nock": "^13.3.3",
|
|
@@ -93,7 +92,7 @@
|
|
|
93
92
|
},
|
|
94
93
|
"templateOSS": {
|
|
95
94
|
"//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.",
|
|
96
|
-
"version": "4.
|
|
95
|
+
"version": "4.25.1",
|
|
97
96
|
"content": "../../scripts/template-oss/index.js"
|
|
98
97
|
}
|
|
99
98
|
}
|