@npmcli/arborist 9.1.5 → 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/README.md +1 -1
- package/bin/index.js +1 -1
- package/lib/arborist/build-ideal-tree.js +43 -54
- package/lib/arborist/index.js +30 -0
- package/lib/arborist/isolated-reifier.js +6 -7
- package/lib/arborist/load-actual.js +1 -14
- package/lib/arborist/load-virtual.js +25 -54
- package/lib/arborist/rebuild.js +13 -18
- package/lib/arborist/reify.js +25 -56
- package/lib/calc-dep-flags.js +86 -128
- package/lib/diff.js +17 -21
- package/lib/edge.js +9 -3
- package/lib/gather-dep-set.js +1 -1
- package/lib/node.js +22 -6
- package/lib/optional-set.js +2 -8
- package/lib/override-set.js +76 -2
- package/lib/packument-cache.js +1 -1
- package/lib/place-dep.js +3 -3
- package/lib/query-selector-all.js +1 -1
- package/lib/reset-dep-flags.js +1 -5
- package/lib/shrinkwrap.js +3 -18
- package/package.json +10 -10
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
|
|
@@ -29,10 +25,8 @@ const optionalSet = node => {
|
|
|
29
25
|
}
|
|
30
26
|
|
|
31
27
|
// 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)
|
|
28
|
+
// the optional section that don't have dependents outside the set.
|
|
29
|
+
return gatherDepSet(set, edge => !set.has(edge.to))
|
|
36
30
|
}
|
|
37
31
|
|
|
38
32
|
module.exports = optionalSet
|
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/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/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/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,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
|
},
|