@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 CHANGED
@@ -6,7 +6,7 @@
6
6
 
7
7
  Inspect and manage `node_modules` trees.
8
8
 
9
- ![a tree with the word ARBORIST superimposed on it](https://raw.githubusercontent.com/npm/arborist/main/docs/logo.svg?sanitize=true)
9
+ ![a tree with the word ARBORIST superimposed on it](https://raw.githubusercontent.com/npm/cli/latest/workspaces/arborist/docs/logo.svg?sanitize=true)
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 supppress the printing of package trees
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 rpj = require('read-package-json-fast')
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 rpj(this.path + '/package.json')
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.ideallyInert
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, dont' worry because it
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, otherwise first
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, otherwise the error is fatal.
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
- return rpj(realpath + '/package.json').catch(() => ({})).then(pkg => {
1295
- const link = new Link({ name, parent, realpath, pkg, installLinks, legacyPeerDeps })
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, otherwise we're done
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.ideallyInert = true
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.ideallyInert
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.ideallyInert) {
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.bundledDepenedencies || node.package.bundleDependencies || []).includes(e.to.name))
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.ideallyInert).map(this.externalProxyMemo))
151
- result.externalOptionalDependencies = await Promise.all(optionalDeps.filter(n => !n.ideallyInert).map(this.externalProxyMemo))
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 { relative, dirname, resolve, join, normalize } = require('node:path')
3
+ const { dirname, join, normalize, relative, resolve } = require('node:path')
4
4
 
5
- const rpj = require('read-package-json-fast')
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, otherwise it'd be in a node_modules folder, so take advantage of
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 rpj(join(real, 'package.json'))
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.#loadRoot(s),
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 metadatas, and create Node objects for nodes
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
- const pj = link.realpath + '/package.json'
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,
@@ -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 { resolve } = require('node:path')
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 rpj(node.path + '/package.json').catch(() => ({}))
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
@@ -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 { checkEngine, checkPlatform } = require('npm-install-checks')
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 treeCheck = require('../tree-check.js')
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 && !node.ideallyInert) {
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 rpj(join(node.path, 'package.json'))
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.ideallyInert = true
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.ideallyInert) || node.target.fsTop === tree
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, otherwise shortcut
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
- // rpj will have moved it here if it was in both
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 an installed version 1.0.0,
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 (semver.subset(edge.spec, node.version)) {
1400
+ if (subset(edge.spec, node.version)) {
1478
1401
  return false
1479
1402
  }
1480
1403
  } catch {
@@ -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
- edge.to &&
114
- edge.to[flag] &&
115
- ((flag !== 'peer' && edge.type === 'peer') || edge.type === 'prod')
116
- ) {
117
- children.push(edge.to)
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
  }
@@ -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
@@ -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 semver = require('semver')
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 { normalize } = require('read-package-json-fast')
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
- ideallyInert = false,
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
- // TODO if this came from pacote.manifest we don't have to do this,
125
- // we can be told to skip this step
126
- const pkg = sourceReference ? sourceReference.package
127
- : normalize(options.pkg || {})
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 || pkg.name || realpath) ||
131
- pkg.name ||
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(pkg._resolved)
160
- if (resolved && !(/^file:/.test(resolved) && pkg._where)) {
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 || pkg._integrity || null
165
- this.hasShrinkwrap = hasShrinkwrap || pkg._hasShrinkwrap || false
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.ideallyInert = ideallyInert
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[_package].overrides || {}
218
+ const overrides = this.package.overrides || {}
214
219
  if (Object.keys(overrides).length > 0) {
215
220
  this.overrides = new OverrideSet({
216
- overrides: this[_package].overrides,
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, otherwise if it's
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[_package],
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[_package].version || ''
336
+ return this.package.version || ''
332
337
  }
333
338
 
334
339
  get packageName () {
335
- return this[_package].name || null
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 doens't match, or is missing, since
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 have many children, and (deduped) package
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 propogate the new overrides through.
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) {
@@ -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. 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)
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
@@ -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
  }
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,38 +1,37 @@
1
1
  {
2
2
  "name": "@npmcli/arborist",
3
- "version": "9.1.4",
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": "^4.0.1",
10
- "@npmcli/metavuln-calculator": "^9.0.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": "^6.0.1",
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": "^9.0.1",
16
+ "@npmcli/run-script": "^10.0.0",
17
17
  "bin-links": "^5.0.0",
18
- "cacache": "^19.0.1",
18
+ "cacache": "^20.0.1",
19
19
  "common-ancestor-path": "^1.0.1",
20
- "hosted-git-info": "^8.0.0",
20
+ "hosted-git-info": "^9.0.0",
21
21
  "json-stringify-nice": "^1.1.4",
22
- "lru-cache": "^10.2.2",
23
- "minimatch": "^9.0.4",
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": "^12.0.0",
27
- "npm-pick-manifest": "^10.0.0",
28
- "npm-registry-fetch": "^18.0.1",
29
- "pacote": "^21.0.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.24.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.24.4",
95
+ "version": "4.25.1",
97
96
  "content": "../../scripts/template-oss/index.js"
98
97
  }
99
98
  }