@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 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>'.
@@ -42,7 +42,6 @@ const _flagsSuspect = Symbol.for('flagsSuspect')
42
42
  const _setWorkspaces = Symbol.for('setWorkspaces')
43
43
  const _updateNames = Symbol.for('updateNames')
44
44
  const _resolvedAdd = Symbol.for('resolvedAdd')
45
- const _usePackageLock = Symbol.for('usePackageLock')
46
45
  const _rpcache = Symbol.for('realpathCache')
47
46
  const _stcache = Symbol.for('statCache')
48
47
 
@@ -101,39 +100,28 @@ module.exports = cls => class IdealTreeBuilder extends cls {
101
100
  constructor (options) {
102
101
  super(options)
103
102
 
104
- // normalize trailing slash
105
- const registry = options.registry || 'https://registry.npmjs.org'
106
- options.registry = this.registry = registry.replace(/\/+$/, '') + '/'
107
-
108
103
  const {
109
104
  follow = false,
110
105
  installStrategy = 'hoisted',
111
- idealTree = null,
112
- installLinks = false,
113
- legacyPeerDeps = false,
114
- packageLock = true,
115
106
  strictPeerDeps = false,
116
- workspaces,
117
107
  global,
118
108
  } = options
119
109
 
120
110
  this.#strictPeerDeps = !!strictPeerDeps
121
111
 
122
- this.idealTree = idealTree
123
- this.installLinks = installLinks
124
- this.legacyPeerDeps = legacyPeerDeps
125
-
126
- this[_usePackageLock] = packageLock
127
112
  this.#installStrategy = global ? 'shallow' : installStrategy
128
113
  this.#follow = !!follow
129
114
 
130
- if (workspaces?.length && global) {
131
- throw new Error('Cannot operate on workspaces in global mode')
132
- }
133
-
134
115
  this[_updateAll] = false
135
116
  this[_updateNames] = []
136
117
  this[_resolvedAdd] = []
118
+
119
+ // caches for cached realpath calls
120
+ const cwd = process.cwd()
121
+ // assume that the cwd is real enough for our purposes
122
+ this[_rpcache] = new Map([[cwd, cwd]])
123
+ this[_stcache] = new Map()
124
+ this[_flagsSuspect] = false
137
125
  }
138
126
 
139
127
  get explicitRequests () {
@@ -192,7 +180,7 @@ module.exports = cls => class IdealTreeBuilder extends cls {
192
180
  }
193
181
 
194
182
  async #checkEngineAndPlatform () {
195
- const { engineStrict, npmVersion, nodeVersion, omit = [] } = this.options
183
+ const { engineStrict, npmVersion, nodeVersion, omit = [], cpu, os, libc } = this.options
196
184
  const omitSet = new Set(omit)
197
185
 
198
186
  for (const node of this.idealTree.inventory.values()) {
@@ -214,6 +202,19 @@ module.exports = cls => class IdealTreeBuilder extends cls {
214
202
  }
215
203
  checkPlatform(node.package, this.options.force)
216
204
  }
205
+ if (node.optional && !node.inert) {
206
+ // Mark any optional packages we can't install as inert.
207
+ // We ignore the --force and --engine-strict flags.
208
+ try {
209
+ checkEngine(node.package, npmVersion, nodeVersion, false)
210
+ checkPlatform(node.package, false, { cpu, os, libc })
211
+ } catch (error) {
212
+ const set = optionalSet(node)
213
+ for (const node of set) {
214
+ node.inert = true
215
+ }
216
+ }
217
+ }
217
218
  }
218
219
  }
219
220
 
@@ -285,7 +286,7 @@ module.exports = cls => class IdealTreeBuilder extends cls {
285
286
  .then(root => {
286
287
  if (this.options.global) {
287
288
  return root
288
- } else if (!this[_usePackageLock] || this[_updateAll]) {
289
+ } else if (!this.options.usePackageLock || this[_updateAll]) {
289
290
  return Shrinkwrap.reset({
290
291
  path: this.path,
291
292
  lockfileVersion: this.options.lockfileVersion,
@@ -324,7 +325,7 @@ module.exports = cls => class IdealTreeBuilder extends cls {
324
325
  })
325
326
 
326
327
  .then(tree => {
327
- // search the virtual tree for invalid edges, if any are found add their source to
328
+ // search the virtual tree for missing/invalid edges, if any are found add their source to
328
329
  // the depsQueue so that we'll fix it later
329
330
  depth({
330
331
  tree,
@@ -338,7 +339,7 @@ module.exports = cls => class IdealTreeBuilder extends cls {
338
339
  filter: node => node,
339
340
  visit: node => {
340
341
  for (const edge of node.edgesOut.values()) {
341
- if (!edge.valid) {
342
+ if ((!edge.to && edge.type !== 'peerOptional') || !edge.valid) {
342
343
  this.#depsQueue.push(node)
343
344
  break // no need to continue the loop after the first hit
344
345
  }
@@ -741,6 +742,7 @@ This is a one-time fix-up, please be patient...
741
742
 
742
743
  // have to re-calc dep flags, because the nodes don't have edges
743
744
  // until their packages get assigned, so everything looks extraneous
745
+ resetDepFlags(this.idealTree)
744
746
  calcDepFlags(this.idealTree)
745
747
 
746
748
  // yes, yes, this isn't the "original" version, but now that it's been
@@ -811,7 +813,7 @@ This is a one-time fix-up, please be patient...
811
813
  node !== this.idealTree &&
812
814
  node.resolved &&
813
815
  (hasBundle || hasShrinkwrap) &&
814
- !node.ideallyInert
816
+ !node.inert
815
817
  if (crackOpen) {
816
818
  const Arborist = this.constructor
817
819
  const opt = { ...this.options }
@@ -1011,7 +1013,7 @@ This is a one-time fix-up, please be patient...
1011
1013
  }
1012
1014
 
1013
1015
  // pre-fetch any problem edges, since we'll need these soon
1014
- // if it fails at this point, though, dont' worry because it
1016
+ // if it fails at this point, though, don't worry because it
1015
1017
  // may well be an optional dep that has gone missing. it'll
1016
1018
  // fail later anyway.
1017
1019
  for (const e of this.#problemEdges(placed)) {
@@ -1067,7 +1069,7 @@ This is a one-time fix-up, please be patient...
1067
1069
  ? await this.#nodeFromSpec(edge.name, spec2, parent, secondEdge)
1068
1070
  : null
1069
1071
 
1070
- // pick the second one if they're both happy with that, otherwise first
1072
+ // pick the second one if they're both happy with that; otherwise, first
1071
1073
  const node = second && edge.valid ? second : first
1072
1074
  // ensure the one we want is the one that's placed
1073
1075
  node.parent = parent
@@ -1217,7 +1219,7 @@ This is a one-time fix-up, please be patient...
1217
1219
  }
1218
1220
  }
1219
1221
 
1220
- #nodeFromSpec (name, spec, parent, edge) {
1222
+ async #nodeFromSpec (name, spec, parent, edge) {
1221
1223
  // pacote will slap integrity on its options, so we have to clone
1222
1224
  // the object so it doesn't get mutated.
1223
1225
  // Don't bother to load the manifest for link deps, because the target
@@ -1246,7 +1248,13 @@ This is a one-time fix-up, please be patient...
1246
1248
  // Decide whether to link or copy the dependency
1247
1249
  const shouldLink = (isWorkspace || isProjectInternalFileSpec || !installLinks) && !isTransitiveFileDep
1248
1250
  if (spec.type === 'directory' && shouldLink) {
1249
- return this.#linkFromSpec(name, spec, parent, edge)
1251
+ const realpath = spec.fetchSpec
1252
+ const { content: pkg } = await PackageJson.normalize(realpath).catch(() => {
1253
+ return { content: {} }
1254
+ })
1255
+ const link = new Link({ name, parent, realpath, pkg, installLinks, legacyPeerDeps })
1256
+ this.#linkNodes.add(link)
1257
+ return link
1250
1258
  }
1251
1259
 
1252
1260
  // if the spec matches a workspace name, then see if the workspace node will satisfy the edge. if it does, we return the workspace node to make sure it takes priority.
@@ -1274,7 +1282,7 @@ This is a one-time fix-up, please be patient...
1274
1282
 
1275
1283
  // failed to load the spec, either because of enotarget or
1276
1284
  // fetch failure of some other sort. save it so we can verify
1277
- // later that it's optional, otherwise the error is fatal.
1285
+ // later that it's optional; otherwise, the error is fatal.
1278
1286
  const n = new Node({
1279
1287
  name,
1280
1288
  parent,
@@ -1287,17 +1295,6 @@ This is a one-time fix-up, please be patient...
1287
1295
  })
1288
1296
  }
1289
1297
 
1290
- async #linkFromSpec (name, spec, parent) {
1291
- const realpath = spec.fetchSpec
1292
- const { installLinks, legacyPeerDeps } = this
1293
- const { content: pkg } = await PackageJson.normalize(realpath).catch(() => {
1294
- return { content: {} }
1295
- })
1296
- const link = new Link({ name, parent, realpath, pkg, installLinks, legacyPeerDeps })
1297
- this.#linkNodes.add(link)
1298
- return link
1299
- }
1300
-
1301
1298
  // load all peer deps and meta-peer deps into the node's parent
1302
1299
  // At the end of this, the node's peer-type outward edges are all
1303
1300
  // resolved, and so are all of theirs, but other dep types are not.
@@ -1431,7 +1428,8 @@ This is a one-time fix-up, please be patient...
1431
1428
  // - if a path under an existing node, then assign that as the fsParent,
1432
1429
  // and add it to the _depsQueue
1433
1430
  //
1434
- // call buildDepStep if anything was added to the queue, otherwise we're done
1431
+ // call buildDepStep if anything was added to the queue; otherwise, we're done
1432
+ // XXX load-virtual also has a #resolveLinks, is there overlap?
1435
1433
  #resolveLinks () {
1436
1434
  for (const link of this.#linkNodes) {
1437
1435
  this.#linkNodes.delete(link)
@@ -1495,11 +1493,7 @@ This is a one-time fix-up, please be patient...
1495
1493
  } else {
1496
1494
  // otherwise just unset all the flags on the root node
1497
1495
  // since they will sometimes have the default value
1498
- this.idealTree.extraneous = false
1499
- this.idealTree.dev = false
1500
- this.idealTree.optional = false
1501
- this.idealTree.devOptional = false
1502
- this.idealTree.peer = false
1496
+ this.idealTree.unsetDepFlags()
1503
1497
  }
1504
1498
 
1505
1499
  // at this point, any node marked as extraneous should be pruned.
@@ -1542,12 +1536,7 @@ This is a one-time fix-up, please be patient...
1542
1536
 
1543
1537
  #idealTreePrune () {
1544
1538
  for (const node of this.idealTree.inventory.values()) {
1545
- // optional peer dependencies are meant to be added to the tree
1546
- // through an explicit required dependency (most commonly in the
1547
- // root package.json), at which point they won't be optional so
1548
- // any dependencies still marked as both optional and peer at
1549
- // this point can be pruned as a special kind of extraneous
1550
- if (node.extraneous || (node.peer && node.optional)) {
1539
+ if (node.extraneous) {
1551
1540
  node.parent = null
1552
1541
  }
1553
1542
  }
@@ -1561,7 +1550,7 @@ This is a one-time fix-up, please be patient...
1561
1550
 
1562
1551
  const set = optionalSet(node)
1563
1552
  for (const node of set) {
1564
- node.ideallyInert = true
1553
+ node.inert = true
1565
1554
  }
1566
1555
  }
1567
1556
  }
@@ -1582,7 +1571,7 @@ This is a one-time fix-up, please be patient...
1582
1571
  node.parent !== null
1583
1572
  && !node.isProjectRoot
1584
1573
  && !excludeNodes.has(node)
1585
- && !node.ideallyInert
1574
+ && !node.inert
1586
1575
  ) {
1587
1576
  this[_addNodeToTrashList](node)
1588
1577
  }
@@ -68,6 +68,34 @@ class Arborist extends Base {
68
68
  constructor (options = {}) {
69
69
  const timeEnd = time.start('arborist:ctor')
70
70
  super(options)
71
+
72
+ // normalize trailing slash
73
+ const registry = options.registry || 'https://registry.npmjs.org'
74
+ options.registry = this.registry = registry.replace(/(?<!\/)\/+$/, '') + '/'
75
+
76
+ // TODO as we consolidate constructors it's more apparent that we are not parsing options and using this.options consistently
77
+ const {
78
+ actualTree,
79
+ global,
80
+ idealTree = null,
81
+ installLinks = false,
82
+ legacyPeerDeps = false,
83
+ virtualTree,
84
+ workspaces,
85
+ } = options
86
+
87
+ if (workspaces?.length && global) {
88
+ throw new Error('Cannot operate on workspaces in global mode')
89
+ }
90
+
91
+ // the tree of nodes on disk
92
+ this.actualTree = actualTree
93
+ this.idealTree = idealTree
94
+ this.installLinks = installLinks
95
+ this.legacyPeerDeps = legacyPeerDeps
96
+ // the virtual tree we load from a shrinkwrap
97
+ this.virtualTree = virtualTree
98
+
71
99
  this.options = {
72
100
  nodeVersion: process.version,
73
101
  ...options,
@@ -88,6 +116,7 @@ class Arborist extends Base {
88
116
  replaceRegistryHost: options.replaceRegistryHost,
89
117
  savePrefix: 'savePrefix' in options ? options.savePrefix : '^',
90
118
  scriptShell: options.scriptShell,
119
+ usePackageLock: 'packageLock' in options ? options.packageLock : true,
91
120
  workspaces: options.workspaces || [],
92
121
  workspacesEnabled: options.workspacesEnabled !== false,
93
122
  }
@@ -104,6 +133,7 @@ class Arborist extends Base {
104
133
  this.cache = resolve(this.options.cache)
105
134
  this.diff = null
106
135
  this.path = resolve(this.options.path)
136
+ this.scriptsRun = new Set()
107
137
  timeEnd()
108
138
  }
109
139
 
@@ -1,6 +1,5 @@
1
1
  const _makeIdealGraph = Symbol('makeIdealGraph')
2
2
  const _createIsolatedTree = Symbol.for('createIsolatedTree')
3
- const _createBundledTree = Symbol('createBundledTree')
4
3
  const { mkdirSync } = require('node:fs')
5
4
  const pacote = require('pacote')
6
5
  const { join } = require('node:path')
@@ -81,7 +80,7 @@ module.exports = cls => class IsolatedReifier extends cls {
81
80
  }
82
81
  queue.push(e.to)
83
82
  })
84
- if (!next.isProjectRoot && !next.isWorkspace && !next.ideallyInert) {
83
+ if (!next.isProjectRoot && !next.isWorkspace && !next.inert) {
85
84
  root.external.push(await this.externalProxyMemo(next))
86
85
  }
87
86
  }
@@ -140,15 +139,15 @@ module.exports = cls => class IsolatedReifier extends cls {
140
139
 
141
140
  async assignCommonProperties (node, result) {
142
141
  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))
142
+ return [...node.edgesOut.values()].filter(e => e.to && e.to.target && !(node.package.bundledDependencies || node.package.bundleDependencies || []).includes(e.to.name))
144
143
  }
145
144
  const edges = validEdgesOut(node)
146
145
  const optionalDeps = edges.filter(e => e.optional).map(e => e.to.target)
147
146
  const nonOptionalDeps = edges.filter(e => !e.optional).map(e => e.to.target)
148
147
 
149
148
  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))
149
+ result.externalDependencies = await Promise.all(nonOptionalDeps.filter(n => !n.isWorkspace && !n.inert).map(this.externalProxyMemo))
150
+ result.externalOptionalDependencies = await Promise.all(optionalDeps.filter(n => !n.inert).map(this.externalProxyMemo))
152
151
  result.dependencies = [
153
152
  ...result.externalDependencies,
154
153
  ...result.localDependencies,
@@ -162,7 +161,7 @@ module.exports = cls => class IsolatedReifier extends cls {
162
161
  result.hasInstallScript = node.hasInstallScript
163
162
  }
164
163
 
165
- async [_createBundledTree] () {
164
+ async #createBundledTree () {
166
165
  // TODO: make sure that idealTree object exists
167
166
  const idealTree = this.idealTree
168
167
  // TODO: test workspaces having bundled deps
@@ -217,7 +216,7 @@ module.exports = cls => class IsolatedReifier extends cls {
217
216
 
218
217
  const proxiedIdealTree = this.idealGraph
219
218
 
220
- const bundledTree = await this[_createBundledTree]()
219
+ const bundledTree = await this.#createBundledTree()
221
220
 
222
221
  const treeHash = (startNode) => {
223
222
  // generate short hash based on the dependency tree
@@ -36,24 +36,11 @@ 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
43
43
 
44
- constructor (options) {
45
- super(options)
46
-
47
- // the tree of nodes on disk
48
- this.actualTree = options.actualTree
49
-
50
- // caches for cached realpath calls
51
- const cwd = process.cwd()
52
- // assume that the cwd is real enough for our purposes
53
- this[_rpcache] = new Map([[cwd, cwd]])
54
- this[_stcache] = new Map()
55
- }
56
-
57
44
  // public method
58
45
  // TODO remove options param in next semver major
59
46
  async loadActual (options = {}) {
@@ -18,14 +18,6 @@ const setWorkspaces = Symbol.for('setWorkspaces')
18
18
  module.exports = cls => class VirtualLoader extends cls {
19
19
  #rootOptionProvided
20
20
 
21
- constructor (options) {
22
- super(options)
23
-
24
- // the virtual tree we load from a shrinkwrap
25
- this.virtualTree = options.virtualTree
26
- this[flagsSuspect] = false
27
- }
28
-
29
21
  // public method
30
22
  async loadVirtual (options = {}) {
31
23
  if (this.virtualTree) {
@@ -69,11 +61,7 @@ module.exports = cls => class VirtualLoader extends cls {
69
61
  if (!this.#rootOptionProvided) {
70
62
  // root is never any of these things, but might be a brand new
71
63
  // baby Node object that never had its dep flags calculated.
72
- root.extraneous = false
73
- root.dev = false
74
- root.optional = false
75
- root.devOptional = false
76
- root.peer = false
64
+ root.unsetDepFlags()
77
65
  } else {
78
66
  this[flagsSuspect] = true
79
67
  }
@@ -81,7 +69,21 @@ module.exports = cls => class VirtualLoader extends cls {
81
69
  this.#checkRootEdges(s, root)
82
70
  root.meta = s
83
71
  this.virtualTree = root
84
- const { links, nodes } = this.#resolveNodes(s, root)
72
+ // separate out link metadata, and create Node objects for nodes
73
+ const links = new Map()
74
+ const nodes = new Map([['', root]])
75
+ for (const [location, meta] of Object.entries(s.data.packages)) {
76
+ // skip the root because we already got it
77
+ if (!location) {
78
+ continue
79
+ }
80
+
81
+ if (meta.link) {
82
+ links.set(location, meta)
83
+ } else {
84
+ nodes.set(location, this.#loadNode(location, meta))
85
+ }
86
+ }
85
87
  await this.#resolveLinks(links, nodes)
86
88
  if (!(s.originalLockfileVersion >= 2)) {
87
89
  this.#assignBundles(nodes)
@@ -93,11 +95,7 @@ module.exports = cls => class VirtualLoader extends cls {
93
95
  if (node.isRoot || node === this.#rootOptionProvided) {
94
96
  continue
95
97
  }
96
- node.extraneous = true
97
- node.dev = true
98
- node.optional = true
99
- node.devOptional = true
100
- node.peer = true
98
+ node.resetDepFlags()
101
99
  }
102
100
  calcDepFlags(this.virtualTree, !this.#rootOptionProvided)
103
101
  }
@@ -168,27 +166,9 @@ module.exports = cls => class VirtualLoader extends cls {
168
166
  }
169
167
  }
170
168
 
171
- // separate out link metadatas, and create Node objects for nodes
172
- #resolveNodes (s, root) {
173
- const links = new Map()
174
- const nodes = new Map([['', root]])
175
- for (const [location, meta] of Object.entries(s.data.packages)) {
176
- // skip the root because we already got it
177
- if (!location) {
178
- continue
179
- }
180
-
181
- if (meta.link) {
182
- links.set(location, meta)
183
- } else {
184
- nodes.set(location, this.#loadNode(location, meta))
185
- }
186
- }
187
- return { links, nodes }
188
- }
189
-
190
169
  // links is the set of metadata, and nodes is the map of non-Link nodes
191
170
  // Set the targets to nodes in the set, if we have them (we might not)
171
+ // XXX build-ideal-tree also has a #resolveLinks, is there overlap?
192
172
  async #resolveLinks (links, nodes) {
193
173
  for (const [location, meta] of links.entries()) {
194
174
  const targetPath = resolve(this.path, meta.resolved)
@@ -255,11 +235,6 @@ To fix:
255
235
  sw.name = nameFromFolder(path)
256
236
  }
257
237
 
258
- const dev = sw.dev
259
- const optional = sw.optional
260
- const devOptional = dev || optional || sw.devOptional
261
- const peer = sw.peer
262
-
263
238
  const node = new Node({
264
239
  installLinks: this.installLinks,
265
240
  legacyPeerDeps: this.legacyPeerDeps,
@@ -269,20 +244,16 @@ To fix:
269
244
  integrity: sw.integrity,
270
245
  resolved: consistentResolve(sw.resolved, this.path, path),
271
246
  pkg: sw,
272
- ideallyInert: sw.ideallyInert,
273
247
  hasShrinkwrap: sw.hasShrinkwrap,
274
- dev,
275
- optional,
276
- devOptional,
277
- peer,
278
248
  loadOverrides,
249
+ // cast to boolean because they're undefined in the lock file when false
250
+ extraneous: !!sw.extraneous,
251
+ devOptional: !!(sw.devOptional || sw.dev || sw.optional),
252
+ peer: !!sw.peer,
253
+ optional: !!sw.optional,
254
+ dev: !!sw.dev,
279
255
  })
280
- // cast to boolean because they're undefined in the lock file when false
281
- node.extraneous = !!sw.extraneous
282
- node.devOptional = !!(sw.devOptional || sw.dev || sw.optional)
283
- node.peer = !!sw.peer
284
- node.optional = !!sw.optional
285
- node.dev = !!sw.dev
256
+
286
257
  return node
287
258
  }
288
259
 
@@ -24,13 +24,12 @@ const _trashList = Symbol.for('trashList')
24
24
  module.exports = cls => class Builder extends cls {
25
25
  #doHandleOptionalFailure
26
26
  #oldMeta = null
27
- #queues
28
-
29
- constructor (options) {
30
- super(options)
31
-
32
- this.scriptsRun = new Set()
33
- this.#resetQueues()
27
+ #queues = {
28
+ preinstall: [],
29
+ install: [],
30
+ postinstall: [],
31
+ prepare: [],
32
+ bin: [],
34
33
  }
35
34
 
36
35
  async rebuild ({ nodes, handleOptionalFailure = false } = {}) {
@@ -62,7 +61,13 @@ module.exports = cls => class Builder extends cls {
62
61
 
63
62
  // build link deps
64
63
  if (linkNodes.size) {
65
- this.#resetQueues()
64
+ this.#queues = {
65
+ preinstall: [],
66
+ install: [],
67
+ postinstall: [],
68
+ prepare: [],
69
+ bin: [],
70
+ }
66
71
  await this.#build(linkNodes, { type: 'links' })
67
72
  }
68
73
 
@@ -132,16 +137,6 @@ module.exports = cls => class Builder extends cls {
132
137
  }
133
138
  }
134
139
 
135
- #resetQueues () {
136
- this.#queues = {
137
- preinstall: [],
138
- install: [],
139
- postinstall: [],
140
- prepare: [],
141
- bin: [],
142
- }
143
- }
144
-
145
140
  async #build (nodes, { type = 'deps' }) {
146
141
  const timeEnd = time.start(`build:${type}`)
147
142