@npmcli/arborist 9.1.6 → 9.1.7
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 +21 -45
- package/lib/arborist/index.js +30 -0
- package/lib/arborist/isolated-reifier.js +2 -3
- package/lib/arborist/load-actual.js +0 -13
- package/lib/arborist/load-virtual.js +25 -53
- package/lib/arborist/rebuild.js +13 -18
- package/lib/arborist/reify.js +14 -10
- package/lib/calc-dep-flags.js +86 -128
- package/lib/edge.js +9 -3
- package/lib/node.js +16 -0
- package/lib/optional-set.js +0 -4
- package/lib/override-set.js +76 -2
- package/lib/reset-dep-flags.js +1 -5
- package/package.json +10 -10
|
@@ -42,7 +42,6 @@ const _flagsSuspect = Symbol.for('flagsSuspect')
|
|
|
42
42
|
const _setWorkspaces = Symbol.for('setWorkspaces')
|
|
43
43
|
const _updateNames = Symbol.for('updateNames')
|
|
44
44
|
const _resolvedAdd = Symbol.for('resolvedAdd')
|
|
45
|
-
const _usePackageLock = Symbol.for('usePackageLock')
|
|
46
45
|
const _rpcache = Symbol.for('realpathCache')
|
|
47
46
|
const _stcache = Symbol.for('statCache')
|
|
48
47
|
|
|
@@ -101,39 +100,28 @@ module.exports = cls => class IdealTreeBuilder extends cls {
|
|
|
101
100
|
constructor (options) {
|
|
102
101
|
super(options)
|
|
103
102
|
|
|
104
|
-
// normalize trailing slash
|
|
105
|
-
const registry = options.registry || 'https://registry.npmjs.org'
|
|
106
|
-
options.registry = this.registry = registry.replace(/\/+$/, '') + '/'
|
|
107
|
-
|
|
108
103
|
const {
|
|
109
104
|
follow = false,
|
|
110
105
|
installStrategy = 'hoisted',
|
|
111
|
-
idealTree = null,
|
|
112
|
-
installLinks = false,
|
|
113
|
-
legacyPeerDeps = false,
|
|
114
|
-
packageLock = true,
|
|
115
106
|
strictPeerDeps = false,
|
|
116
|
-
workspaces,
|
|
117
107
|
global,
|
|
118
108
|
} = options
|
|
119
109
|
|
|
120
110
|
this.#strictPeerDeps = !!strictPeerDeps
|
|
121
111
|
|
|
122
|
-
this.idealTree = idealTree
|
|
123
|
-
this.installLinks = installLinks
|
|
124
|
-
this.legacyPeerDeps = legacyPeerDeps
|
|
125
|
-
|
|
126
|
-
this[_usePackageLock] = packageLock
|
|
127
112
|
this.#installStrategy = global ? 'shallow' : installStrategy
|
|
128
113
|
this.#follow = !!follow
|
|
129
114
|
|
|
130
|
-
if (workspaces?.length && global) {
|
|
131
|
-
throw new Error('Cannot operate on workspaces in global mode')
|
|
132
|
-
}
|
|
133
|
-
|
|
134
115
|
this[_updateAll] = false
|
|
135
116
|
this[_updateNames] = []
|
|
136
117
|
this[_resolvedAdd] = []
|
|
118
|
+
|
|
119
|
+
// caches for cached realpath calls
|
|
120
|
+
const cwd = process.cwd()
|
|
121
|
+
// assume that the cwd is real enough for our purposes
|
|
122
|
+
this[_rpcache] = new Map([[cwd, cwd]])
|
|
123
|
+
this[_stcache] = new Map()
|
|
124
|
+
this[_flagsSuspect] = false
|
|
137
125
|
}
|
|
138
126
|
|
|
139
127
|
get explicitRequests () {
|
|
@@ -298,7 +286,7 @@ module.exports = cls => class IdealTreeBuilder extends cls {
|
|
|
298
286
|
.then(root => {
|
|
299
287
|
if (this.options.global) {
|
|
300
288
|
return root
|
|
301
|
-
} else if (!this
|
|
289
|
+
} else if (!this.options.usePackageLock || this[_updateAll]) {
|
|
302
290
|
return Shrinkwrap.reset({
|
|
303
291
|
path: this.path,
|
|
304
292
|
lockfileVersion: this.options.lockfileVersion,
|
|
@@ -351,7 +339,7 @@ module.exports = cls => class IdealTreeBuilder extends cls {
|
|
|
351
339
|
filter: node => node,
|
|
352
340
|
visit: node => {
|
|
353
341
|
for (const edge of node.edgesOut.values()) {
|
|
354
|
-
if (!edge.to || !edge.valid) {
|
|
342
|
+
if ((!edge.to && edge.type !== 'peerOptional') || !edge.valid) {
|
|
355
343
|
this.#depsQueue.push(node)
|
|
356
344
|
break // no need to continue the loop after the first hit
|
|
357
345
|
}
|
|
@@ -754,6 +742,7 @@ This is a one-time fix-up, please be patient...
|
|
|
754
742
|
|
|
755
743
|
// have to re-calc dep flags, because the nodes don't have edges
|
|
756
744
|
// until their packages get assigned, so everything looks extraneous
|
|
745
|
+
resetDepFlags(this.idealTree)
|
|
757
746
|
calcDepFlags(this.idealTree)
|
|
758
747
|
|
|
759
748
|
// yes, yes, this isn't the "original" version, but now that it's been
|
|
@@ -1230,7 +1219,7 @@ This is a one-time fix-up, please be patient...
|
|
|
1230
1219
|
}
|
|
1231
1220
|
}
|
|
1232
1221
|
|
|
1233
|
-
#nodeFromSpec (name, spec, parent, edge) {
|
|
1222
|
+
async #nodeFromSpec (name, spec, parent, edge) {
|
|
1234
1223
|
// pacote will slap integrity on its options, so we have to clone
|
|
1235
1224
|
// the object so it doesn't get mutated.
|
|
1236
1225
|
// Don't bother to load the manifest for link deps, because the target
|
|
@@ -1259,7 +1248,13 @@ This is a one-time fix-up, please be patient...
|
|
|
1259
1248
|
// Decide whether to link or copy the dependency
|
|
1260
1249
|
const shouldLink = (isWorkspace || isProjectInternalFileSpec || !installLinks) && !isTransitiveFileDep
|
|
1261
1250
|
if (spec.type === 'directory' && shouldLink) {
|
|
1262
|
-
|
|
1251
|
+
const realpath = spec.fetchSpec
|
|
1252
|
+
const { content: pkg } = await PackageJson.normalize(realpath).catch(() => {
|
|
1253
|
+
return { content: {} }
|
|
1254
|
+
})
|
|
1255
|
+
const link = new Link({ name, parent, realpath, pkg, installLinks, legacyPeerDeps })
|
|
1256
|
+
this.#linkNodes.add(link)
|
|
1257
|
+
return link
|
|
1263
1258
|
}
|
|
1264
1259
|
|
|
1265
1260
|
// if the spec matches a workspace name, then see if the workspace node will satisfy the edge. if it does, we return the workspace node to make sure it takes priority.
|
|
@@ -1300,17 +1295,6 @@ This is a one-time fix-up, please be patient...
|
|
|
1300
1295
|
})
|
|
1301
1296
|
}
|
|
1302
1297
|
|
|
1303
|
-
async #linkFromSpec (name, spec, parent) {
|
|
1304
|
-
const realpath = spec.fetchSpec
|
|
1305
|
-
const { installLinks, legacyPeerDeps } = this
|
|
1306
|
-
const { content: pkg } = await PackageJson.normalize(realpath).catch(() => {
|
|
1307
|
-
return { content: {} }
|
|
1308
|
-
})
|
|
1309
|
-
const link = new Link({ name, parent, realpath, pkg, installLinks, legacyPeerDeps })
|
|
1310
|
-
this.#linkNodes.add(link)
|
|
1311
|
-
return link
|
|
1312
|
-
}
|
|
1313
|
-
|
|
1314
1298
|
// load all peer deps and meta-peer deps into the node's parent
|
|
1315
1299
|
// At the end of this, the node's peer-type outward edges are all
|
|
1316
1300
|
// resolved, and so are all of theirs, but other dep types are not.
|
|
@@ -1445,6 +1429,7 @@ This is a one-time fix-up, please be patient...
|
|
|
1445
1429
|
// and add it to the _depsQueue
|
|
1446
1430
|
//
|
|
1447
1431
|
// call buildDepStep if anything was added to the queue; otherwise, we're done
|
|
1432
|
+
// XXX load-virtual also has a #resolveLinks, is there overlap?
|
|
1448
1433
|
#resolveLinks () {
|
|
1449
1434
|
for (const link of this.#linkNodes) {
|
|
1450
1435
|
this.#linkNodes.delete(link)
|
|
@@ -1508,11 +1493,7 @@ This is a one-time fix-up, please be patient...
|
|
|
1508
1493
|
} else {
|
|
1509
1494
|
// otherwise just unset all the flags on the root node
|
|
1510
1495
|
// since they will sometimes have the default value
|
|
1511
|
-
this.idealTree.
|
|
1512
|
-
this.idealTree.dev = false
|
|
1513
|
-
this.idealTree.optional = false
|
|
1514
|
-
this.idealTree.devOptional = false
|
|
1515
|
-
this.idealTree.peer = false
|
|
1496
|
+
this.idealTree.unsetDepFlags()
|
|
1516
1497
|
}
|
|
1517
1498
|
|
|
1518
1499
|
// at this point, any node marked as extraneous should be pruned.
|
|
@@ -1555,12 +1536,7 @@ This is a one-time fix-up, please be patient...
|
|
|
1555
1536
|
|
|
1556
1537
|
#idealTreePrune () {
|
|
1557
1538
|
for (const node of this.idealTree.inventory.values()) {
|
|
1558
|
-
|
|
1559
|
-
// through an explicit required dependency (most commonly in the
|
|
1560
|
-
// root package.json), at which point they won't be optional so
|
|
1561
|
-
// any dependencies still marked as both optional and peer at
|
|
1562
|
-
// this point can be pruned as a special kind of extraneous
|
|
1563
|
-
if (node.extraneous || (node.peer && node.optional)) {
|
|
1539
|
+
if (node.extraneous) {
|
|
1564
1540
|
node.parent = null
|
|
1565
1541
|
}
|
|
1566
1542
|
}
|
package/lib/arborist/index.js
CHANGED
|
@@ -68,6 +68,34 @@ class Arborist extends Base {
|
|
|
68
68
|
constructor (options = {}) {
|
|
69
69
|
const timeEnd = time.start('arborist:ctor')
|
|
70
70
|
super(options)
|
|
71
|
+
|
|
72
|
+
// normalize trailing slash
|
|
73
|
+
const registry = options.registry || 'https://registry.npmjs.org'
|
|
74
|
+
options.registry = this.registry = registry.replace(/(?<!\/)\/+$/, '') + '/'
|
|
75
|
+
|
|
76
|
+
// TODO as we consolidate constructors it's more apparent that we are not parsing options and using this.options consistently
|
|
77
|
+
const {
|
|
78
|
+
actualTree,
|
|
79
|
+
global,
|
|
80
|
+
idealTree = null,
|
|
81
|
+
installLinks = false,
|
|
82
|
+
legacyPeerDeps = false,
|
|
83
|
+
virtualTree,
|
|
84
|
+
workspaces,
|
|
85
|
+
} = options
|
|
86
|
+
|
|
87
|
+
if (workspaces?.length && global) {
|
|
88
|
+
throw new Error('Cannot operate on workspaces in global mode')
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// the tree of nodes on disk
|
|
92
|
+
this.actualTree = actualTree
|
|
93
|
+
this.idealTree = idealTree
|
|
94
|
+
this.installLinks = installLinks
|
|
95
|
+
this.legacyPeerDeps = legacyPeerDeps
|
|
96
|
+
// the virtual tree we load from a shrinkwrap
|
|
97
|
+
this.virtualTree = virtualTree
|
|
98
|
+
|
|
71
99
|
this.options = {
|
|
72
100
|
nodeVersion: process.version,
|
|
73
101
|
...options,
|
|
@@ -88,6 +116,7 @@ class Arborist extends Base {
|
|
|
88
116
|
replaceRegistryHost: options.replaceRegistryHost,
|
|
89
117
|
savePrefix: 'savePrefix' in options ? options.savePrefix : '^',
|
|
90
118
|
scriptShell: options.scriptShell,
|
|
119
|
+
usePackageLock: 'packageLock' in options ? options.packageLock : true,
|
|
91
120
|
workspaces: options.workspaces || [],
|
|
92
121
|
workspacesEnabled: options.workspacesEnabled !== false,
|
|
93
122
|
}
|
|
@@ -104,6 +133,7 @@ class Arborist extends Base {
|
|
|
104
133
|
this.cache = resolve(this.options.cache)
|
|
105
134
|
this.diff = null
|
|
106
135
|
this.path = resolve(this.options.path)
|
|
136
|
+
this.scriptsRun = new Set()
|
|
107
137
|
timeEnd()
|
|
108
138
|
}
|
|
109
139
|
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
const _makeIdealGraph = Symbol('makeIdealGraph')
|
|
2
2
|
const _createIsolatedTree = Symbol.for('createIsolatedTree')
|
|
3
|
-
const _createBundledTree = Symbol('createBundledTree')
|
|
4
3
|
const { mkdirSync } = require('node:fs')
|
|
5
4
|
const pacote = require('pacote')
|
|
6
5
|
const { join } = require('node:path')
|
|
@@ -162,7 +161,7 @@ module.exports = cls => class IsolatedReifier extends cls {
|
|
|
162
161
|
result.hasInstallScript = node.hasInstallScript
|
|
163
162
|
}
|
|
164
163
|
|
|
165
|
-
async
|
|
164
|
+
async #createBundledTree () {
|
|
166
165
|
// TODO: make sure that idealTree object exists
|
|
167
166
|
const idealTree = this.idealTree
|
|
168
167
|
// TODO: test workspaces having bundled deps
|
|
@@ -217,7 +216,7 @@ module.exports = cls => class IsolatedReifier extends cls {
|
|
|
217
216
|
|
|
218
217
|
const proxiedIdealTree = this.idealGraph
|
|
219
218
|
|
|
220
|
-
const bundledTree = await this
|
|
219
|
+
const bundledTree = await this.#createBundledTree()
|
|
221
220
|
|
|
222
221
|
const treeHash = (startNode) => {
|
|
223
222
|
// generate short hash based on the dependency tree
|
|
@@ -41,19 +41,6 @@ module.exports = cls => class ActualLoader extends cls {
|
|
|
41
41
|
#topNodes = new Set()
|
|
42
42
|
#transplantFilter
|
|
43
43
|
|
|
44
|
-
constructor (options) {
|
|
45
|
-
super(options)
|
|
46
|
-
|
|
47
|
-
// the tree of nodes on disk
|
|
48
|
-
this.actualTree = options.actualTree
|
|
49
|
-
|
|
50
|
-
// caches for cached realpath calls
|
|
51
|
-
const cwd = process.cwd()
|
|
52
|
-
// assume that the cwd is real enough for our purposes
|
|
53
|
-
this[_rpcache] = new Map([[cwd, cwd]])
|
|
54
|
-
this[_stcache] = new Map()
|
|
55
|
-
}
|
|
56
|
-
|
|
57
44
|
// public method
|
|
58
45
|
// TODO remove options param in next semver major
|
|
59
46
|
async loadActual (options = {}) {
|
|
@@ -18,14 +18,6 @@ const setWorkspaces = Symbol.for('setWorkspaces')
|
|
|
18
18
|
module.exports = cls => class VirtualLoader extends cls {
|
|
19
19
|
#rootOptionProvided
|
|
20
20
|
|
|
21
|
-
constructor (options) {
|
|
22
|
-
super(options)
|
|
23
|
-
|
|
24
|
-
// the virtual tree we load from a shrinkwrap
|
|
25
|
-
this.virtualTree = options.virtualTree
|
|
26
|
-
this[flagsSuspect] = false
|
|
27
|
-
}
|
|
28
|
-
|
|
29
21
|
// public method
|
|
30
22
|
async loadVirtual (options = {}) {
|
|
31
23
|
if (this.virtualTree) {
|
|
@@ -69,11 +61,7 @@ module.exports = cls => class VirtualLoader extends cls {
|
|
|
69
61
|
if (!this.#rootOptionProvided) {
|
|
70
62
|
// root is never any of these things, but might be a brand new
|
|
71
63
|
// baby Node object that never had its dep flags calculated.
|
|
72
|
-
root.
|
|
73
|
-
root.dev = false
|
|
74
|
-
root.optional = false
|
|
75
|
-
root.devOptional = false
|
|
76
|
-
root.peer = false
|
|
64
|
+
root.unsetDepFlags()
|
|
77
65
|
} else {
|
|
78
66
|
this[flagsSuspect] = true
|
|
79
67
|
}
|
|
@@ -81,7 +69,21 @@ module.exports = cls => class VirtualLoader extends cls {
|
|
|
81
69
|
this.#checkRootEdges(s, root)
|
|
82
70
|
root.meta = s
|
|
83
71
|
this.virtualTree = root
|
|
84
|
-
|
|
72
|
+
// separate out link metadata, and create Node objects for nodes
|
|
73
|
+
const links = new Map()
|
|
74
|
+
const nodes = new Map([['', root]])
|
|
75
|
+
for (const [location, meta] of Object.entries(s.data.packages)) {
|
|
76
|
+
// skip the root because we already got it
|
|
77
|
+
if (!location) {
|
|
78
|
+
continue
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (meta.link) {
|
|
82
|
+
links.set(location, meta)
|
|
83
|
+
} else {
|
|
84
|
+
nodes.set(location, this.#loadNode(location, meta))
|
|
85
|
+
}
|
|
86
|
+
}
|
|
85
87
|
await this.#resolveLinks(links, nodes)
|
|
86
88
|
if (!(s.originalLockfileVersion >= 2)) {
|
|
87
89
|
this.#assignBundles(nodes)
|
|
@@ -93,11 +95,7 @@ module.exports = cls => class VirtualLoader extends cls {
|
|
|
93
95
|
if (node.isRoot || node === this.#rootOptionProvided) {
|
|
94
96
|
continue
|
|
95
97
|
}
|
|
96
|
-
node.
|
|
97
|
-
node.dev = true
|
|
98
|
-
node.optional = true
|
|
99
|
-
node.devOptional = true
|
|
100
|
-
node.peer = true
|
|
98
|
+
node.resetDepFlags()
|
|
101
99
|
}
|
|
102
100
|
calcDepFlags(this.virtualTree, !this.#rootOptionProvided)
|
|
103
101
|
}
|
|
@@ -168,27 +166,9 @@ module.exports = cls => class VirtualLoader extends cls {
|
|
|
168
166
|
}
|
|
169
167
|
}
|
|
170
168
|
|
|
171
|
-
// separate out link metadata, and create Node objects for nodes
|
|
172
|
-
#resolveNodes (s, root) {
|
|
173
|
-
const links = new Map()
|
|
174
|
-
const nodes = new Map([['', root]])
|
|
175
|
-
for (const [location, meta] of Object.entries(s.data.packages)) {
|
|
176
|
-
// skip the root because we already got it
|
|
177
|
-
if (!location) {
|
|
178
|
-
continue
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
if (meta.link) {
|
|
182
|
-
links.set(location, meta)
|
|
183
|
-
} else {
|
|
184
|
-
nodes.set(location, this.#loadNode(location, meta))
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
return { links, nodes }
|
|
188
|
-
}
|
|
189
|
-
|
|
190
169
|
// links is the set of metadata, and nodes is the map of non-Link nodes
|
|
191
170
|
// Set the targets to nodes in the set, if we have them (we might not)
|
|
171
|
+
// XXX build-ideal-tree also has a #resolveLinks, is there overlap?
|
|
192
172
|
async #resolveLinks (links, nodes) {
|
|
193
173
|
for (const [location, meta] of links.entries()) {
|
|
194
174
|
const targetPath = resolve(this.path, meta.resolved)
|
|
@@ -255,11 +235,6 @@ To fix:
|
|
|
255
235
|
sw.name = nameFromFolder(path)
|
|
256
236
|
}
|
|
257
237
|
|
|
258
|
-
const dev = sw.dev
|
|
259
|
-
const optional = sw.optional
|
|
260
|
-
const devOptional = dev || optional || sw.devOptional
|
|
261
|
-
const peer = sw.peer
|
|
262
|
-
|
|
263
238
|
const node = new Node({
|
|
264
239
|
installLinks: this.installLinks,
|
|
265
240
|
legacyPeerDeps: this.legacyPeerDeps,
|
|
@@ -270,18 +245,15 @@ To fix:
|
|
|
270
245
|
resolved: consistentResolve(sw.resolved, this.path, path),
|
|
271
246
|
pkg: sw,
|
|
272
247
|
hasShrinkwrap: sw.hasShrinkwrap,
|
|
273
|
-
dev,
|
|
274
|
-
optional,
|
|
275
|
-
devOptional,
|
|
276
|
-
peer,
|
|
277
248
|
loadOverrides,
|
|
249
|
+
// cast to boolean because they're undefined in the lock file when false
|
|
250
|
+
extraneous: !!sw.extraneous,
|
|
251
|
+
devOptional: !!(sw.devOptional || sw.dev || sw.optional),
|
|
252
|
+
peer: !!sw.peer,
|
|
253
|
+
optional: !!sw.optional,
|
|
254
|
+
dev: !!sw.dev,
|
|
278
255
|
})
|
|
279
|
-
|
|
280
|
-
node.extraneous = !!sw.extraneous
|
|
281
|
-
node.devOptional = !!(sw.devOptional || sw.dev || sw.optional)
|
|
282
|
-
node.peer = !!sw.peer
|
|
283
|
-
node.optional = !!sw.optional
|
|
284
|
-
node.dev = !!sw.dev
|
|
256
|
+
|
|
285
257
|
return node
|
|
286
258
|
}
|
|
287
259
|
|
package/lib/arborist/rebuild.js
CHANGED
|
@@ -24,13 +24,12 @@ const _trashList = Symbol.for('trashList')
|
|
|
24
24
|
module.exports = cls => class Builder extends cls {
|
|
25
25
|
#doHandleOptionalFailure
|
|
26
26
|
#oldMeta = null
|
|
27
|
-
#queues
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
this.#resetQueues()
|
|
27
|
+
#queues = {
|
|
28
|
+
preinstall: [],
|
|
29
|
+
install: [],
|
|
30
|
+
postinstall: [],
|
|
31
|
+
prepare: [],
|
|
32
|
+
bin: [],
|
|
34
33
|
}
|
|
35
34
|
|
|
36
35
|
async rebuild ({ nodes, handleOptionalFailure = false } = {}) {
|
|
@@ -62,7 +61,13 @@ module.exports = cls => class Builder extends cls {
|
|
|
62
61
|
|
|
63
62
|
// build link deps
|
|
64
63
|
if (linkNodes.size) {
|
|
65
|
-
this.#
|
|
64
|
+
this.#queues = {
|
|
65
|
+
preinstall: [],
|
|
66
|
+
install: [],
|
|
67
|
+
postinstall: [],
|
|
68
|
+
prepare: [],
|
|
69
|
+
bin: [],
|
|
70
|
+
}
|
|
66
71
|
await this.#build(linkNodes, { type: 'links' })
|
|
67
72
|
}
|
|
68
73
|
|
|
@@ -132,16 +137,6 @@ module.exports = cls => class Builder extends cls {
|
|
|
132
137
|
}
|
|
133
138
|
}
|
|
134
139
|
|
|
135
|
-
#resetQueues () {
|
|
136
|
-
this.#queues = {
|
|
137
|
-
preinstall: [],
|
|
138
|
-
install: [],
|
|
139
|
-
postinstall: [],
|
|
140
|
-
prepare: [],
|
|
141
|
-
bin: [],
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
|
|
145
140
|
async #build (nodes, { type = 'deps' }) {
|
|
146
141
|
const timeEnd = time.start(`build:${type}`)
|
|
147
142
|
|
package/lib/arborist/reify.js
CHANGED
|
@@ -57,11 +57,9 @@ const _rollbackRetireShallowNodes = Symbol.for('rollbackRetireShallowNodes')
|
|
|
57
57
|
const _rollbackCreateSparseTree = Symbol.for('rollbackCreateSparseTree')
|
|
58
58
|
const _rollbackMoveBackRetiredUnchanged = Symbol.for('rollbackMoveBackRetiredUnchanged')
|
|
59
59
|
const _saveIdealTree = Symbol.for('saveIdealTree')
|
|
60
|
-
const _reifyPackages = Symbol.for('reifyPackages')
|
|
61
60
|
|
|
62
61
|
// defined by build-ideal-tree mixin
|
|
63
62
|
const _resolvedAdd = Symbol.for('resolvedAdd')
|
|
64
|
-
const _usePackageLock = Symbol.for('usePackageLock')
|
|
65
63
|
// used by build-ideal-tree mixin
|
|
66
64
|
const _addNodeToTrashList = Symbol.for('addNodeToTrashList')
|
|
67
65
|
|
|
@@ -70,12 +68,10 @@ const _createIsolatedTree = Symbol.for('createIsolatedTree')
|
|
|
70
68
|
module.exports = cls => class Reifier extends cls {
|
|
71
69
|
#bundleMissing = new Set() // child nodes we'd EXPECT to be included in a bundle, but aren't
|
|
72
70
|
#bundleUnpacked = new Set() // the nodes we unpack to read their bundles
|
|
73
|
-
#dryRun
|
|
74
71
|
#nmValidated = new Set()
|
|
75
72
|
#omit
|
|
76
73
|
#retiredPaths = {}
|
|
77
74
|
#retiredUnchanged = {}
|
|
78
|
-
#savePrefix
|
|
79
75
|
#shrinkwrapInflated = new Set()
|
|
80
76
|
#sparseTreeDirs = new Set()
|
|
81
77
|
#sparseTreeRoots = new Set()
|
|
@@ -122,7 +118,7 @@ module.exports = cls => class Reifier extends cls {
|
|
|
122
118
|
this.idealTree = await this[_createIsolatedTree]()
|
|
123
119
|
}
|
|
124
120
|
await this[_diffTrees]()
|
|
125
|
-
await this
|
|
121
|
+
await this.#reifyPackages()
|
|
126
122
|
if (linked) {
|
|
127
123
|
// swap back in the idealTree
|
|
128
124
|
// so that the lockfile is preserved
|
|
@@ -261,7 +257,7 @@ module.exports = cls => class Reifier extends cls {
|
|
|
261
257
|
return treeCheck(this.actualTree)
|
|
262
258
|
}
|
|
263
259
|
|
|
264
|
-
async
|
|
260
|
+
async #reifyPackages () {
|
|
265
261
|
// we don't submit the audit report or write to disk on dry runs
|
|
266
262
|
if (this.options.dryRun) {
|
|
267
263
|
return
|
|
@@ -817,9 +813,17 @@ module.exports = cls => class Reifier extends cls {
|
|
|
817
813
|
// Make sure we don't double-include the path if it's already there
|
|
818
814
|
const registryPath = registryURL.pathname.replace(/\/$/, '')
|
|
819
815
|
|
|
820
|
-
if (registryPath && registryPath !== '/'
|
|
821
|
-
//
|
|
822
|
-
|
|
816
|
+
if (registryPath && registryPath !== '/') {
|
|
817
|
+
// Check if the resolved pathname already starts with the registry path
|
|
818
|
+
// We need to ensure it's a proper path prefix, not just a string prefix
|
|
819
|
+
// e.g., registry path '/npm' should not match '/npm-run-path'
|
|
820
|
+
const hasRegistryPath = resolvedURL.pathname === registryPath ||
|
|
821
|
+
resolvedURL.pathname.startsWith(registryPath + '/')
|
|
822
|
+
|
|
823
|
+
if (!hasRegistryPath) {
|
|
824
|
+
// Since hostname is changed, we need to ensure the registry path is included
|
|
825
|
+
resolvedURL.pathname = registryPath + resolvedURL.pathname
|
|
826
|
+
}
|
|
823
827
|
}
|
|
824
828
|
|
|
825
829
|
return resolvedURL.toString()
|
|
@@ -1496,7 +1500,7 @@ module.exports = cls => class Reifier extends cls {
|
|
|
1496
1500
|
|
|
1497
1501
|
// before now edge specs could be changing, affecting the `requires` field
|
|
1498
1502
|
// in the package lock, so we hold off saving to the very last action
|
|
1499
|
-
if (this
|
|
1503
|
+
if (this.options.usePackageLock) {
|
|
1500
1504
|
// preserve indentation, if possible
|
|
1501
1505
|
let format = this.idealTree.package[Symbol.for('indent')]
|
|
1502
1506
|
if (format === undefined) {
|
package/lib/calc-dep-flags.js
CHANGED
|
@@ -1,144 +1,102 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
// Dep flag (dev, peer, etc.) calculation requires default or reset flags.
|
|
2
|
+
// Flags are true by default and are unset to false as we walk deps.
|
|
3
|
+
// We iterate outward edges looking for dep flags that can
|
|
4
|
+
// be unset based on the current nodes flags and edge type.
|
|
5
|
+
// Examples:
|
|
6
|
+
// - a non-optional node with a non-optional edge out, the edge node should not be optional
|
|
7
|
+
// - a non-peer node with a non-peer edge out, the edge node should not be peer
|
|
8
|
+
// If a node is changed, we add to the queue and continue until no more changes.
|
|
9
|
+
// Flags that remain after all this unsetting should be valid.
|
|
10
|
+
// Examples:
|
|
11
|
+
// - a node still flagged optional must only be reachable via optional edges
|
|
12
|
+
// - a node still flagged peer must only be reachable via peer edges
|
|
3
13
|
const calcDepFlags = (tree, resetRoot = true) => {
|
|
4
14
|
if (resetRoot) {
|
|
5
|
-
tree.
|
|
6
|
-
tree.optional = false
|
|
7
|
-
tree.devOptional = false
|
|
8
|
-
tree.peer = false
|
|
15
|
+
tree.unsetDepFlags()
|
|
9
16
|
}
|
|
10
|
-
const ret = depth({
|
|
11
|
-
tree,
|
|
12
|
-
visit: node => calcDepFlagsStep(node),
|
|
13
|
-
filter: node => node,
|
|
14
|
-
getChildren: (node, tree) =>
|
|
15
|
-
[...tree.edgesOut.values()].map(edge => edge.to),
|
|
16
|
-
})
|
|
17
|
-
return ret
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const calcDepFlagsStep = (node) => {
|
|
21
|
-
// This rewalk is necessary to handle cases where devDep and optional
|
|
22
|
-
// or normal dependency graphs overlap deep in the dep graph.
|
|
23
|
-
// Since we're only walking through deps that are not already flagged
|
|
24
|
-
// as non-dev/non-optional, it's typically a very shallow traversal
|
|
25
|
-
|
|
26
|
-
node.extraneous = false
|
|
27
|
-
resetParents(node, 'extraneous')
|
|
28
|
-
resetParents(node, 'dev')
|
|
29
|
-
resetParents(node, 'peer')
|
|
30
|
-
resetParents(node, 'devOptional')
|
|
31
|
-
resetParents(node, 'optional')
|
|
32
|
-
|
|
33
|
-
// for links, map their hierarchy appropriately
|
|
34
|
-
if (node.isLink) {
|
|
35
|
-
// node.target can be null, we check to ensure it's not null before proceeding
|
|
36
|
-
if (node.target == null) {
|
|
37
|
-
return node
|
|
38
|
-
}
|
|
39
|
-
node.target.dev = node.dev
|
|
40
|
-
node.target.optional = node.optional
|
|
41
|
-
node.target.devOptional = node.devOptional
|
|
42
|
-
node.target.peer = node.peer
|
|
43
|
-
return calcDepFlagsStep(node.target)
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
node.edgesOut.forEach(({ peer, optional, dev, to }) => {
|
|
47
|
-
// if the dep is missing, then its flags are already maximally unset
|
|
48
|
-
if (!to) {
|
|
49
|
-
return
|
|
50
|
-
}
|
|
51
|
-
// everything with any kind of edge into it is not extraneous
|
|
52
|
-
to.extraneous = false
|
|
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
17
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
// it set when we are in *either* tree, and then omit it from the
|
|
64
|
-
// package-lock if either dev or optional are set.
|
|
65
|
-
const unsetDevOpt = !node.devOptional && !node.dev && !node.optional && !dev && !optional
|
|
18
|
+
const seen = new Set()
|
|
19
|
+
const queue = [tree]
|
|
66
20
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
const unsetOpt = unsetDevOpt || !node.optional && !optional
|
|
21
|
+
let node
|
|
22
|
+
while (node = queue.pop()) {
|
|
23
|
+
seen.add(node)
|
|
71
24
|
|
|
72
|
-
|
|
73
|
-
|
|
25
|
+
// Unset extraneous from all parents to avoid removal of children.
|
|
26
|
+
if (!node.extraneous) {
|
|
27
|
+
for (let n = node.resolveParent; n?.extraneous; n = n.resolveParent) {
|
|
28
|
+
n.extraneous = false
|
|
29
|
+
}
|
|
74
30
|
}
|
|
75
31
|
|
|
76
|
-
|
|
77
|
-
|
|
32
|
+
// for links, map their hierarchy appropriately
|
|
33
|
+
if (node.isLink) {
|
|
34
|
+
// node.target can be null, we check to ensure it's not null before proceeding
|
|
35
|
+
if (node.target == null) {
|
|
36
|
+
continue
|
|
37
|
+
}
|
|
38
|
+
node.target.dev = node.dev
|
|
39
|
+
node.target.optional = node.optional
|
|
40
|
+
node.target.devOptional = node.devOptional
|
|
41
|
+
node.target.peer = node.peer
|
|
42
|
+
node.target.extraneous = node.extraneous
|
|
43
|
+
queue.push(node.target)
|
|
44
|
+
continue
|
|
78
45
|
}
|
|
79
46
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
47
|
+
for (const { peer, optional, dev, to } of node.edgesOut.values()) {
|
|
48
|
+
// if the dep is missing, then its flags are already maximally unset
|
|
49
|
+
if (!to) {
|
|
50
|
+
continue
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
let changed = false
|
|
54
|
+
|
|
55
|
+
// only optional peer dependencies should stay extraneous
|
|
56
|
+
if (to.extraneous && !node.extraneous && !(peer && optional)) {
|
|
57
|
+
to.extraneous = false
|
|
58
|
+
changed = true
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (to.dev && !node.dev && !dev) {
|
|
62
|
+
to.dev = false
|
|
63
|
+
changed = true
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (to.optional && !node.optional && !optional) {
|
|
67
|
+
to.optional = false
|
|
68
|
+
changed = true
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// devOptional is the *overlap* of the dev and optional tree.
|
|
72
|
+
// A node may be depended on by separate dev and optional nodes.
|
|
73
|
+
// It SHOULD NOT be removed when pruning dev OR optional.
|
|
74
|
+
// It SHOULD be removed when pruning dev AND optional.
|
|
75
|
+
// We only unset here if a node is not dev AND not optional because
|
|
76
|
+
// if we did unset, it would prevent any overlap deeper in the tree.
|
|
77
|
+
// We correct this later by removing if dev OR optional is set.
|
|
78
|
+
if (to.devOptional && !node.devOptional && !node.dev && !node.optional && !dev && !optional) {
|
|
79
|
+
to.devOptional = false
|
|
80
|
+
changed = true
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (to.peer && !node.peer && !peer) {
|
|
84
|
+
to.peer = false
|
|
85
|
+
changed = true
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (changed) {
|
|
89
|
+
queue.push(to)
|
|
90
|
+
}
|
|
93
91
|
}
|
|
94
92
|
}
|
|
95
|
-
return false
|
|
96
|
-
}
|
|
97
93
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
p[flag] = false
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// typically a short walk, since it only traverses deps that have the flag set.
|
|
109
|
-
const unsetFlag = (node, flag) => {
|
|
110
|
-
if (node[flag]) {
|
|
111
|
-
node[flag] = false
|
|
112
|
-
depth({
|
|
113
|
-
tree: node,
|
|
114
|
-
visit: node => {
|
|
115
|
-
node.extraneous = node[flag] = false
|
|
116
|
-
if (node.isLink && node.target) {
|
|
117
|
-
node.target.extraneous = node.target[flag] = false
|
|
118
|
-
}
|
|
119
|
-
},
|
|
120
|
-
getChildren: node => {
|
|
121
|
-
const children = []
|
|
122
|
-
const targetNode = node.isLink && node.target ? node.target : node
|
|
123
|
-
for (const edge of targetNode.edgesOut.values()) {
|
|
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
|
-
}
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
return children
|
|
140
|
-
},
|
|
141
|
-
})
|
|
94
|
+
// Remove incorrect devOptional flags now that we have walked all deps.
|
|
95
|
+
seen.delete(tree)
|
|
96
|
+
for (const node of seen.values()) {
|
|
97
|
+
if (node.devOptional && (node.dev || node.optional)) {
|
|
98
|
+
node.devOptional = false
|
|
99
|
+
}
|
|
142
100
|
}
|
|
143
101
|
}
|
|
144
102
|
|
package/lib/edge.js
CHANGED
|
@@ -276,9 +276,15 @@ class Edge {
|
|
|
276
276
|
} else if (!this.satisfiedBy(this.#to)) {
|
|
277
277
|
this.#error = 'INVALID'
|
|
278
278
|
} else if (this.overrides && this.#to.edgesOut.size && OverrideSet.doOverrideSetsConflict(this.overrides, this.#to.overrides)) {
|
|
279
|
-
//
|
|
280
|
-
//
|
|
281
|
-
//
|
|
279
|
+
// Check for conflicts between the edge's override set and the target node's override set.
|
|
280
|
+
// This catches cases where different parts of the tree have genuinely incompatible
|
|
281
|
+
// version requirements for the same package.
|
|
282
|
+
// The improved conflict detection uses semantic comparison (checking for incompatible
|
|
283
|
+
// version ranges) rather than pure structural equality, avoiding false positives from:
|
|
284
|
+
// - Reference overrides ($syntax) that resolve to compatible versions
|
|
285
|
+
// - Peer dependencies with different but compatible override contexts
|
|
286
|
+
// Note: We only check if the target has dependencies (edgesOut.size > 0), since
|
|
287
|
+
// override conflicts are only relevant if the target has its own dependencies.
|
|
282
288
|
this.#error = 'INVALID'
|
|
283
289
|
} else {
|
|
284
290
|
this.#error = 'OK'
|
package/lib/node.js
CHANGED
|
@@ -1613,6 +1613,22 @@ class Node {
|
|
|
1613
1613
|
[util.inspect.custom] () {
|
|
1614
1614
|
return this.toJSON()
|
|
1615
1615
|
}
|
|
1616
|
+
|
|
1617
|
+
resetDepFlags () {
|
|
1618
|
+
this.extraneous = true
|
|
1619
|
+
this.dev = true
|
|
1620
|
+
this.optional = true
|
|
1621
|
+
this.devOptional = true
|
|
1622
|
+
this.peer = true
|
|
1623
|
+
}
|
|
1624
|
+
|
|
1625
|
+
unsetDepFlags () {
|
|
1626
|
+
this.extraneous = false
|
|
1627
|
+
this.dev = false
|
|
1628
|
+
this.optional = false
|
|
1629
|
+
this.devOptional = false
|
|
1630
|
+
this.peer = false
|
|
1631
|
+
}
|
|
1616
1632
|
}
|
|
1617
1633
|
|
|
1618
1634
|
module.exports = Node
|
package/lib/optional-set.js
CHANGED
|
@@ -10,10 +10,6 @@
|
|
|
10
10
|
|
|
11
11
|
const gatherDepSet = require('./gather-dep-set.js')
|
|
12
12
|
const optionalSet = node => {
|
|
13
|
-
if (!node.optional) {
|
|
14
|
-
return new Set()
|
|
15
|
-
}
|
|
16
|
-
|
|
17
13
|
// start with the node, then walk up the dependency graph until we
|
|
18
14
|
// get to the boundaries that define the optional set. since the
|
|
19
15
|
// node is optional, we know that all paths INTO this area of the
|
package/lib/override-set.js
CHANGED
|
@@ -201,8 +201,82 @@ class OverrideSet {
|
|
|
201
201
|
|
|
202
202
|
static doOverrideSetsConflict (first, second) {
|
|
203
203
|
// If override sets contain one another then we can try to use the more specific one.
|
|
204
|
-
// If neither one is more specific,
|
|
205
|
-
|
|
204
|
+
// If neither one is more specific, check for semantic conflicts.
|
|
205
|
+
const specificSet = this.findSpecificOverrideSet(first, second)
|
|
206
|
+
if (specificSet !== undefined) {
|
|
207
|
+
// One contains the other, so no conflict
|
|
208
|
+
return false
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// The override sets are structurally incomparable, but this doesn't necessarily
|
|
212
|
+
// mean they conflict. We need to check if they have conflicting version requirements
|
|
213
|
+
// for any package that appears in both rulesets.
|
|
214
|
+
return this.haveConflictingRules(first, second)
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
static haveConflictingRules (first, second) {
|
|
218
|
+
// Get all rules from both override sets
|
|
219
|
+
const firstRules = first.ruleset
|
|
220
|
+
const secondRules = second.ruleset
|
|
221
|
+
|
|
222
|
+
// Check each package that appears in both rulesets
|
|
223
|
+
for (const [key, firstRule] of firstRules) {
|
|
224
|
+
const secondRule = secondRules.get(key)
|
|
225
|
+
if (!secondRule) {
|
|
226
|
+
// Package only appears in one ruleset, no conflict
|
|
227
|
+
continue
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Same rule object means no conflict
|
|
231
|
+
if (firstRule === secondRule || firstRule.isEqual(secondRule)) {
|
|
232
|
+
continue
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// Both rulesets have rules for this package with different values.
|
|
236
|
+
// Check if the version requirements are actually incompatible.
|
|
237
|
+
const firstValue = firstRule.value
|
|
238
|
+
const secondValue = secondRule.value
|
|
239
|
+
|
|
240
|
+
// If either value is a reference (starts with $), we can't determine
|
|
241
|
+
// compatibility here - the reference might resolve to compatible versions.
|
|
242
|
+
// We defer to runtime resolution rather than failing early.
|
|
243
|
+
if (firstValue.startsWith('$') || secondValue.startsWith('$')) {
|
|
244
|
+
continue
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// Check if the version ranges are compatible using semver
|
|
248
|
+
// If both specify version ranges, they conflict only if they have no overlap
|
|
249
|
+
try {
|
|
250
|
+
const firstSpec = npa(`${firstRule.name}@${firstValue}`)
|
|
251
|
+
const secondSpec = npa(`${secondRule.name}@${secondValue}`)
|
|
252
|
+
|
|
253
|
+
// For range/version types, check if they intersect
|
|
254
|
+
if ((firstSpec.type === 'range' || firstSpec.type === 'version') &&
|
|
255
|
+
(secondSpec.type === 'range' || secondSpec.type === 'version')) {
|
|
256
|
+
// Check if the ranges intersect
|
|
257
|
+
const firstRange = firstSpec.fetchSpec
|
|
258
|
+
const secondRange = secondSpec.fetchSpec
|
|
259
|
+
|
|
260
|
+
// If the ranges don't intersect, we have a real conflict
|
|
261
|
+
if (!semver.intersects(firstRange, secondRange)) {
|
|
262
|
+
log.silly('Found conflicting override rules', {
|
|
263
|
+
package: firstRule.name,
|
|
264
|
+
first: firstValue,
|
|
265
|
+
second: secondValue,
|
|
266
|
+
})
|
|
267
|
+
return true
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
// For other types (git, file, directory, tag), we can't easily determine
|
|
271
|
+
// compatibility, so we conservatively assume no conflict
|
|
272
|
+
} catch {
|
|
273
|
+
// If we can't parse the specs, conservatively assume no conflict
|
|
274
|
+
// Real conflicts will be caught during dependency resolution
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// No conflicting rules found
|
|
279
|
+
return false
|
|
206
280
|
}
|
|
207
281
|
}
|
|
208
282
|
|
package/lib/reset-dep-flags.js
CHANGED
|
@@ -6,10 +6,6 @@
|
|
|
6
6
|
// we can find the set that is actually extraneous.
|
|
7
7
|
module.exports = tree => {
|
|
8
8
|
for (const node of tree.inventory.values()) {
|
|
9
|
-
node.
|
|
10
|
-
node.dev = true
|
|
11
|
-
node.devOptional = true
|
|
12
|
-
node.peer = true
|
|
13
|
-
node.optional = true
|
|
9
|
+
node.resetDepFlags()
|
|
14
10
|
}
|
|
15
11
|
}
|
package/package.json
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@npmcli/arborist",
|
|
3
|
-
"version": "9.1.
|
|
3
|
+
"version": "9.1.7",
|
|
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
|
-
"@npmcli/installed-package-contents": "^
|
|
8
|
+
"@npmcli/installed-package-contents": "^4.0.0",
|
|
9
9
|
"@npmcli/map-workspaces": "^5.0.0",
|
|
10
10
|
"@npmcli/metavuln-calculator": "^9.0.2",
|
|
11
|
-
"@npmcli/name-from-folder": "^
|
|
12
|
-
"@npmcli/node-gyp": "^
|
|
11
|
+
"@npmcli/name-from-folder": "^4.0.0",
|
|
12
|
+
"@npmcli/node-gyp": "^5.0.0",
|
|
13
13
|
"@npmcli/package-json": "^7.0.0",
|
|
14
14
|
"@npmcli/query": "^4.0.0",
|
|
15
|
-
"@npmcli/redact": "^
|
|
15
|
+
"@npmcli/redact": "^4.0.0",
|
|
16
16
|
"@npmcli/run-script": "^10.0.0",
|
|
17
|
-
"bin-links": "^
|
|
17
|
+
"bin-links": "^6.0.0",
|
|
18
18
|
"cacache": "^20.0.1",
|
|
19
19
|
"common-ancestor-path": "^1.0.1",
|
|
20
20
|
"hosted-git-info": "^9.0.0",
|
|
@@ -22,18 +22,18 @@
|
|
|
22
22
|
"lru-cache": "^11.2.1",
|
|
23
23
|
"minimatch": "^10.0.3",
|
|
24
24
|
"nopt": "^8.0.0",
|
|
25
|
-
"npm-install-checks": "^
|
|
25
|
+
"npm-install-checks": "^8.0.0",
|
|
26
26
|
"npm-package-arg": "^13.0.0",
|
|
27
27
|
"npm-pick-manifest": "^11.0.1",
|
|
28
28
|
"npm-registry-fetch": "^19.0.0",
|
|
29
29
|
"pacote": "^21.0.2",
|
|
30
|
-
"parse-conflict-json": "^
|
|
31
|
-
"proc-log": "^
|
|
30
|
+
"parse-conflict-json": "^5.0.1",
|
|
31
|
+
"proc-log": "^6.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
35
|
"semver": "^7.3.7",
|
|
36
|
-
"ssri": "^
|
|
36
|
+
"ssri": "^13.0.0",
|
|
37
37
|
"treeverse": "^3.0.0",
|
|
38
38
|
"walk-up-path": "^4.0.0"
|
|
39
39
|
},
|