@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.
@@ -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. that's the set of dependencies that are only
33
- // depended upon by other nodes within the set, or optional dependencies
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
@@ -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, then we consider them to be in conflict.
205
- return (this.findSpecificOverrideSet(first, second) === undefined)
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
 
@@ -30,7 +30,7 @@ class PackumentCache extends LRUCache {
30
30
  maxSize,
31
31
  maxEntrySize,
32
32
  sizeCalculation: (p) => {
33
- // Don't cache if we dont know the size
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 a update scenario, then we MAY have something
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.legaycPeerDeps,
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 in installStategy is nested, we do this amount of deduplication.
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 anscestors
788
+ // follows logical parent for link ancestors
789
789
  if (node.isTop && (node.resolveParent === compareNode)) {
790
790
  return true
791
791
  }
@@ -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.extraneous = true
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) && !data.packages[loc].ideallyInert) {
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, otherwise prefer package-lock
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
- // racey and non-deterministic in a world where deps are deduped
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.5",
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": "^3.0.0",
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": "^3.0.0",
12
- "@npmcli/node-gyp": "^4.0.0",
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": "^3.0.0",
15
+ "@npmcli/redact": "^4.0.0",
16
16
  "@npmcli/run-script": "^10.0.0",
17
- "bin-links": "^5.0.0",
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": "^7.1.0",
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": "^4.0.0",
31
- "proc-log": "^5.0.0",
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": "^12.0.0",
36
+ "ssri": "^13.0.0",
37
37
  "treeverse": "^3.0.0",
38
38
  "walk-up-path": "^4.0.0"
39
39
  },