@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.
@@ -7,7 +7,6 @@ const pacote = require('pacote')
7
7
  const promiseAllRejectLate = require('promise-all-reject-late')
8
8
  const runScript = require('@npmcli/run-script')
9
9
  const { callLimit: promiseCallLimit } = require('promise-call-limit')
10
- const { checkEngine, checkPlatform } = require('npm-install-checks')
11
10
  const { depth: dfwalk } = require('treeverse')
12
11
  const { dirname, resolve, relative, join } = require('node:path')
13
12
  const { log, time } = require('proc-log')
@@ -58,11 +57,9 @@ const _rollbackRetireShallowNodes = Symbol.for('rollbackRetireShallowNodes')
58
57
  const _rollbackCreateSparseTree = Symbol.for('rollbackCreateSparseTree')
59
58
  const _rollbackMoveBackRetiredUnchanged = Symbol.for('rollbackMoveBackRetiredUnchanged')
60
59
  const _saveIdealTree = Symbol.for('saveIdealTree')
61
- const _reifyPackages = Symbol.for('reifyPackages')
62
60
 
63
61
  // defined by build-ideal-tree mixin
64
62
  const _resolvedAdd = Symbol.for('resolvedAdd')
65
- const _usePackageLock = Symbol.for('usePackageLock')
66
63
  // used by build-ideal-tree mixin
67
64
  const _addNodeToTrashList = Symbol.for('addNodeToTrashList')
68
65
 
@@ -71,13 +68,10 @@ const _createIsolatedTree = Symbol.for('createIsolatedTree')
71
68
  module.exports = cls => class Reifier extends cls {
72
69
  #bundleMissing = new Set() // child nodes we'd EXPECT to be included in a bundle, but aren't
73
70
  #bundleUnpacked = new Set() // the nodes we unpack to read their bundles
74
- #dryRun
75
71
  #nmValidated = new Set()
76
72
  #omit
77
- #omitted
78
73
  #retiredPaths = {}
79
74
  #retiredUnchanged = {}
80
- #savePrefix
81
75
  #shrinkwrapInflated = new Set()
82
76
  #sparseTreeDirs = new Set()
83
77
  #sparseTreeRoots = new Set()
@@ -99,7 +93,6 @@ module.exports = cls => class Reifier extends cls {
99
93
  }
100
94
 
101
95
  this.#omit = new Set(options.omit)
102
- this.#omitted = new Set()
103
96
 
104
97
  // start tracker block
105
98
  this.addTracker('reify')
@@ -125,22 +118,24 @@ module.exports = cls => class Reifier extends cls {
125
118
  this.idealTree = await this[_createIsolatedTree]()
126
119
  }
127
120
  await this[_diffTrees]()
128
- await this[_reifyPackages]()
121
+ await this.#reifyPackages()
129
122
  if (linked) {
130
123
  // swap back in the idealTree
131
124
  // so that the lockfile is preserved
132
125
  this.idealTree = oldTree
133
126
  }
134
127
  await this[_saveIdealTree](options)
135
- // clean omitted
136
- for (const node of this.#omitted) {
137
- node.parent = null
128
+ // clean inert
129
+ for (const node of this.idealTree.inventory.values()) {
130
+ if (node.inert) {
131
+ node.parent = null
132
+ }
138
133
  }
139
134
  // clean up any trash that is still in the tree
140
135
  for (const path of this[_trashList]) {
141
136
  const loc = relpath(this.idealTree.realpath, path)
142
137
  const node = this.idealTree.inventory.get(loc)
143
- if (node && node.root === this.idealTree && !node.ideallyInert) {
138
+ if (node && node.root === this.idealTree) {
144
139
  node.parent = null
145
140
  }
146
141
  }
@@ -228,18 +223,6 @@ module.exports = cls => class Reifier extends cls {
228
223
  this.idealTree.meta.hiddenLockfile = true
229
224
  this.idealTree.meta.lockfileVersion = defaultLockfileVersion
230
225
 
231
- // Preserve inertness for failed stuff.
232
- if (this.actualTree) {
233
- for (const [loc, actual] of this.actualTree.inventory.entries()) {
234
- if (actual.ideallyInert) {
235
- const ideal = this.idealTree.inventory.get(loc)
236
- if (ideal) {
237
- ideal.ideallyInert = true
238
- }
239
- }
240
- }
241
- }
242
-
243
226
  this.actualTree = this.idealTree
244
227
  this.idealTree = null
245
228
 
@@ -274,7 +257,7 @@ module.exports = cls => class Reifier extends cls {
274
257
  return treeCheck(this.actualTree)
275
258
  }
276
259
 
277
- async [_reifyPackages] () {
260
+ async #reifyPackages () {
278
261
  // we don't submit the audit report or write to disk on dry runs
279
262
  if (this.options.dryRun) {
280
263
  return
@@ -465,7 +448,6 @@ module.exports = cls => class Reifier extends cls {
465
448
  // and ideal trees.
466
449
  this.diff = Diff.calculate({
467
450
  omit: this.#omit,
468
- omitted: this.#omitted,
469
451
  shrinkwrapInflated: this.#shrinkwrapInflated,
470
452
  filterNodes,
471
453
  actual: this.actualTree,
@@ -566,9 +548,6 @@ module.exports = cls => class Reifier extends cls {
566
548
  // retire the same path at the same time.
567
549
  const dirsChecked = new Set()
568
550
  return promiseAllRejectLate(leaves.map(async node => {
569
- if (node.ideallyInert) {
570
- return
571
- }
572
551
  for (const d of walkUp(node.path)) {
573
552
  if (d === node.top.path) {
574
553
  break
@@ -662,18 +641,7 @@ module.exports = cls => class Reifier extends cls {
662
641
  const timeEnd = time.start(`reifyNode:${node.location}`)
663
642
  this.addTracker('reify', node.name, node.location)
664
643
 
665
- const { npmVersion, nodeVersion, cpu, os, libc } = this.options
666
644
  const p = Promise.resolve().then(async () => {
667
- // when we reify an optional node, check the engine and platform
668
- // first. be sure to ignore the --force and --engine-strict flags,
669
- // since we always want to skip any optional packages we can't install.
670
- // these checks throwing will result in a rollback and removal
671
- // of the mismatches
672
- // eslint-disable-next-line promise/always-return
673
- if (node.optional) {
674
- checkEngine(node.package, npmVersion, nodeVersion, false)
675
- checkPlatform(node.package, false, { cpu, os, libc })
676
- }
677
645
  await this[_checkBins](node)
678
646
  await this.#extractOrLink(node)
679
647
  const { _id, deprecated } = node.package
@@ -707,10 +675,6 @@ module.exports = cls => class Reifier extends cls {
707
675
  }
708
676
 
709
677
  async #extractOrLink (node) {
710
- if (node.ideallyInert) {
711
- return
712
- }
713
-
714
678
  const nm = resolve(node.parent.path, 'node_modules')
715
679
  await this.#validateNodeModules(nm)
716
680
 
@@ -791,9 +755,9 @@ module.exports = cls => class Reifier extends cls {
791
755
  [_handleOptionalFailure] (node, p) {
792
756
  return (node.optional ? p.catch(() => {
793
757
  const set = optionalSet(node)
794
- for (node of set) {
758
+ for (const node of set) {
795
759
  log.verbose('reify', 'failed optional dependency', node.path)
796
- node.ideallyInert = true
760
+ node.inert = true
797
761
  this[_addNodeToTrashList](node)
798
762
  }
799
763
  }) : p).then(() => node)
@@ -849,9 +813,17 @@ module.exports = cls => class Reifier extends cls {
849
813
  // Make sure we don't double-include the path if it's already there
850
814
  const registryPath = registryURL.pathname.replace(/\/$/, '')
851
815
 
852
- if (registryPath && registryPath !== '/' && !resolvedURL.pathname.startsWith(registryPath)) {
853
- // Since hostname is changed, we need to ensure the registry path is included
854
- resolvedURL.pathname = registryPath + resolvedURL.pathname
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
+ }
855
827
  }
856
828
 
857
829
  return resolvedURL.toString()
@@ -1165,9 +1137,6 @@ module.exports = cls => class Reifier extends cls {
1165
1137
 
1166
1138
  this.#retiredUnchanged[retireFolder] = []
1167
1139
  return promiseAllRejectLate(diff.unchanged.map(node => {
1168
- if (node.ideallyInert) {
1169
- return
1170
- }
1171
1140
  // no need to roll back links, since we'll just delete them anyway
1172
1141
  if (node.isLink) {
1173
1142
  return mkdir(dirname(node.path), { recursive: true, force: true })
@@ -1247,7 +1216,7 @@ module.exports = cls => class Reifier extends cls {
1247
1216
  // skip links that only live within node_modules as they are most
1248
1217
  // likely managed by packages we installed, we only want to rebuild
1249
1218
  // unchanged links we directly manage
1250
- const linkedFromRoot = (node.parent === tree && !node.ideallyInert) || node.target.fsTop === tree
1219
+ const linkedFromRoot = (node.parent === tree && !node.inert) || node.target.fsTop === tree
1251
1220
  if (node.isLink && linkedFromRoot) {
1252
1221
  nodes.push(node)
1253
1222
  }
@@ -1358,7 +1327,7 @@ module.exports = cls => class Reifier extends cls {
1358
1327
  const alias = name !== pname
1359
1328
  newSpec = alias ? `npm:${pname}@${range}` : range
1360
1329
  } else if (req.hosted) {
1361
- // save the git+https url if it has auth, otherwise shortcut
1330
+ // save the git+https url if it has auth; otherwise, shortcut
1362
1331
  const h = req.hosted
1363
1332
  const opt = { noCommittish: false }
1364
1333
  if (h.https && h.auth) {
@@ -1427,7 +1396,7 @@ module.exports = cls => class Reifier extends cls {
1427
1396
 
1428
1397
  // Returns true if any of the edges from this node has a semver
1429
1398
  // range definition that is an exact match to the version installed
1430
- // e.g: should return true if for a given an installed version 1.0.0,
1399
+ // e.g: should return true if for a given and installed version 1.0.0,
1431
1400
  // range is either =1.0.0 or 1.0.0
1432
1401
  const exactVersion = node => {
1433
1402
  for (const edge of node.edgesIn) {
@@ -1531,7 +1500,7 @@ module.exports = cls => class Reifier extends cls {
1531
1500
 
1532
1501
  // before now edge specs could be changing, affecting the `requires` field
1533
1502
  // in the package lock, so we hold off saving to the very last action
1534
- if (this[_usePackageLock]) {
1503
+ if (this.options.usePackageLock) {
1535
1504
  // preserve indentation, if possible
1536
1505
  let format = this.idealTree.package[Symbol.for('indent')]
1537
1506
  if (format === undefined) {
@@ -1,144 +1,102 @@
1
- const { depth } = require('treeverse')
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.dev = false
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
- // devOptional is the *overlap* of the dev and optional tree.
62
- // however, for convenience and to save an extra rewalk, we leave
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
- // if we are not in the devOpt tree, then we're also not in
68
- // either the dev or opt trees
69
- const unsetDev = unsetDevOpt || !node.dev && !dev
70
- const unsetOpt = unsetDevOpt || !node.optional && !optional
21
+ let node
22
+ while (node = queue.pop()) {
23
+ seen.add(node)
71
24
 
72
- if (unsetDevOpt) {
73
- unsetFlag(to, 'devOptional')
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
- if (unsetDev) {
77
- unsetFlag(to, 'dev')
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
- if (unsetOpt) {
81
- unsetFlag(to, 'optional')
82
- }
83
- })
84
-
85
- return node
86
- }
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
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
- const resetParents = (node, flag) => {
99
- if (node[flag]) {
100
- return
101
- }
102
-
103
- for (let p = node; p && (p === node || p[flag]); p = p.resolveParent) {
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/diff.js CHANGED
@@ -11,9 +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, omit, omitted }) {
14
+ constructor ({ actual, ideal, filterSet, shrinkwrapInflated, omit }) {
15
15
  this.omit = omit
16
- this.omitted = omitted
17
16
  this.filterSet = filterSet
18
17
  this.shrinkwrapInflated = shrinkwrapInflated
19
18
  this.children = []
@@ -39,7 +38,6 @@ class Diff {
39
38
  filterNodes = [],
40
39
  shrinkwrapInflated = new Set(),
41
40
  omit = new Set(),
42
- omitted = new Set(),
43
41
  }) {
44
42
  // if there's a filterNode, then:
45
43
  // - get the path from the root to the filterNode. The root or
@@ -98,28 +96,18 @@ class Diff {
98
96
  }
99
97
 
100
98
  return depth({
101
- tree: new Diff({ actual, ideal, filterSet, shrinkwrapInflated, omit, omitted }),
99
+ tree: new Diff({ actual, ideal, filterSet, shrinkwrapInflated, omit }),
102
100
  getChildren,
103
101
  leave,
104
102
  })
105
103
  }
106
104
  }
107
105
 
108
- const getAction = ({ actual, ideal, omit, omitted }) => {
106
+ const getAction = ({ actual, ideal }) => {
109
107
  if (!ideal) {
110
108
  return 'REMOVE'
111
109
  }
112
110
 
113
- if (ideal.shouldOmit?.(omit)) {
114
- omitted.add(ideal)
115
-
116
- if (actual) {
117
- return 'REMOVE'
118
- }
119
-
120
- return null
121
- }
122
-
123
111
  // bundled meta-deps are copied over to the ideal tree when we visit it,
124
112
  // so they'll appear to be missing here. There's no need to handle them
125
113
  // in the diff, though, because they'll be replaced at reify time anyway
@@ -199,7 +187,6 @@ const getChildren = diff => {
199
187
  filterSet,
200
188
  shrinkwrapInflated,
201
189
  omit,
202
- omitted,
203
190
  } = diff
204
191
 
205
192
  // Note: we DON'T diff fsChildren themselves, because they are either
@@ -231,7 +218,6 @@ const getChildren = diff => {
231
218
  filterSet,
232
219
  shrinkwrapInflated,
233
220
  omit,
234
- omitted,
235
221
  })
236
222
  }
237
223
 
@@ -251,13 +237,24 @@ const diffNode = ({
251
237
  filterSet,
252
238
  shrinkwrapInflated,
253
239
  omit,
254
- omitted,
255
240
  }) => {
256
241
  if (filterSet.size && !(filterSet.has(ideal) || filterSet.has(actual))) {
257
242
  return
258
243
  }
259
244
 
260
- const action = getAction({ actual, ideal, omit, omitted })
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
+
257
+ const action = getAction({ actual, ideal })
261
258
 
262
259
  // if it's a match, then get its children
263
260
  // otherwise, this is the child diff node
@@ -265,7 +262,7 @@ const diffNode = ({
265
262
  if (action === 'REMOVE') {
266
263
  removed.push(actual)
267
264
  }
268
- children.push(new Diff({ actual, ideal, filterSet, shrinkwrapInflated, omit, omitted }))
265
+ children.push(new Diff({ actual, ideal, filterSet, shrinkwrapInflated, omit }))
269
266
  } else {
270
267
  unchanged.push(ideal)
271
268
  // !*! Weird dirty hack warning !*!
@@ -306,7 +303,6 @@ const diffNode = ({
306
303
  filterSet,
307
304
  shrinkwrapInflated,
308
305
  omit,
309
- omitted,
310
306
  }))
311
307
  }
312
308
  }
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
- // Any inconsistency between the edge's override set and the target's override set is potentially problematic.
280
- // But we only say the edge is in error if the override sets are plainly conflicting.
281
- // Note that if the target doesn't have any dependencies of their own, then this inconsistency is irrelevant.
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'
@@ -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 dependant outside the set
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
@@ -101,7 +101,7 @@ class Node {
101
101
  global = false,
102
102
  dummy = false,
103
103
  sourceReference = null,
104
- ideallyInert = false,
104
+ inert = false,
105
105
  } = options
106
106
  // this object gives querySelectorAll somewhere to stash context about a node
107
107
  // while processing a query
@@ -207,7 +207,7 @@ class Node {
207
207
  this.extraneous = false
208
208
  }
209
209
 
210
- this.ideallyInert = ideallyInert
210
+ this.inert = inert
211
211
 
212
212
  this.edgesIn = new Set()
213
213
  this.edgesOut = new CaseInsensitiveMap()
@@ -248,7 +248,7 @@ class Node {
248
248
  this.fsParent = fsParent || null
249
249
 
250
250
  // see parent/root setters below.
251
- // root is set to parent's root if we have a parent, otherwise if it's
251
+ // root is set to parent's root if we have a parent; otherwise, if it's
252
252
  // null, then it's set to the node itself.
253
253
  if (!parent && !fsParent) {
254
254
  this.root = root || null
@@ -832,7 +832,7 @@ class Node {
832
832
  edge.reload()
833
833
  }
834
834
  }
835
- // reload all edgesOut where root doens't match, or is missing, since
835
+ // reload all edgesOut where root doesn't match, or is missing, since
836
836
  // it might not be missing in the new tree
837
837
  for (const edge of this.edgesOut.values()) {
838
838
  if (!edge.to || edge.to.root !== root) {
@@ -1268,7 +1268,7 @@ class Node {
1268
1268
  // with another by the same name (eg, to update or dedupe).
1269
1269
  // This does a couple of walks out on the node_modules tree, recursing
1270
1270
  // into child nodes. However, as setting the parent is typically done
1271
- // with nodes that don't have have many children, and (deduped) package
1271
+ // with nodes that don't have many children, and (deduped) package
1272
1272
  // trees tend to be broad rather than deep, it's not that bad.
1273
1273
  // The only walk that starts from the parent rather than this node is
1274
1274
  // limited by edge name.
@@ -1412,7 +1412,7 @@ class Node {
1412
1412
  }
1413
1413
 
1414
1414
  recalculateOutEdgesOverrides () {
1415
- // For each edge out propogate the new overrides through.
1415
+ // For each edge out propagate the new overrides through.
1416
1416
  for (const edge of this.edgesOut.values()) {
1417
1417
  edge.reload(true)
1418
1418
  if (edge.to) {
@@ -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