@npmcli/arborist 7.3.1 → 7.4.1

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.
@@ -19,67 +19,37 @@ const boolEnv = b => b ? '1' : ''
19
19
  const sortNodes = (a, b) =>
20
20
  (a.depth - b.depth) || localeCompare(a.path, b.path)
21
21
 
22
- const _workspaces = Symbol.for('workspaces')
23
- const _build = Symbol('build')
24
- const _loadDefaultNodes = Symbol('loadDefaultNodes')
25
- const _retrieveNodesByType = Symbol('retrieveNodesByType')
26
- const _resetQueues = Symbol('resetQueues')
27
- const _rebuildBundle = Symbol('rebuildBundle')
28
- const _ignoreScripts = Symbol('ignoreScripts')
29
- const _binLinks = Symbol('binLinks')
30
- const _oldMeta = Symbol('oldMeta')
31
- const _createBinLinks = Symbol('createBinLinks')
32
- const _doHandleOptionalFailure = Symbol('doHandleOptionalFailure')
33
- const _linkAllBins = Symbol('linkAllBins')
34
- const _runScripts = Symbol('runScripts')
35
- const _buildQueues = Symbol('buildQueues')
36
- const _addToBuildSet = Symbol('addToBuildSet')
37
22
  const _checkBins = Symbol.for('checkBins')
38
- const _queues = Symbol('queues')
39
- const _scriptShell = Symbol('scriptShell')
40
- const _includeWorkspaceRoot = Symbol.for('includeWorkspaceRoot')
41
- const _workspacesEnabled = Symbol.for('workspacesEnabled')
42
-
43
- const _force = Symbol.for('force')
44
- const _global = Symbol.for('global')
45
23
 
46
24
  // defined by reify mixin
47
25
  const _handleOptionalFailure = Symbol.for('handleOptionalFailure')
48
26
  const _trashList = Symbol.for('trashList')
49
27
 
50
28
  module.exports = cls => class Builder extends cls {
29
+ #doHandleOptionalFailure
30
+ #oldMeta = null
31
+ #queues
32
+
51
33
  constructor (options) {
52
34
  super(options)
53
35
 
54
- const {
55
- ignoreScripts = false,
56
- scriptShell,
57
- binLinks = true,
58
- rebuildBundle = true,
59
- } = options
60
-
61
36
  this.scriptsRun = new Set()
62
- this[_binLinks] = binLinks
63
- this[_ignoreScripts] = !!ignoreScripts
64
- this[_scriptShell] = scriptShell
65
- this[_rebuildBundle] = !!rebuildBundle
66
- this[_resetQueues]()
67
- this[_oldMeta] = null
37
+ this.#resetQueues()
68
38
  }
69
39
 
70
40
  async rebuild ({ nodes, handleOptionalFailure = false } = {}) {
71
41
  // nothing to do if we're not building anything!
72
- if (this[_ignoreScripts] && !this[_binLinks]) {
42
+ if (this.options.ignoreScripts && !this.options.binLinks) {
73
43
  return
74
44
  }
75
45
 
76
46
  // when building for the first time, as part of reify, we ignore
77
47
  // failures in optional nodes, and just delete them. however, when
78
48
  // running JUST a rebuild, we treat optional failures as real fails
79
- this[_doHandleOptionalFailure] = handleOptionalFailure
49
+ this.#doHandleOptionalFailure = handleOptionalFailure
80
50
 
81
51
  if (!nodes) {
82
- nodes = await this[_loadDefaultNodes]()
52
+ nodes = await this.#loadDefaultNodes()
83
53
  }
84
54
 
85
55
  // separates links nodes so that it can run
@@ -89,15 +59,15 @@ module.exports = cls => class Builder extends cls {
89
59
  const {
90
60
  depNodes,
91
61
  linkNodes,
92
- } = this[_retrieveNodesByType](nodes)
62
+ } = this.#retrieveNodesByType(nodes)
93
63
 
94
64
  // build regular deps
95
- await this[_build](depNodes, {})
65
+ await this.#build(depNodes, {})
96
66
 
97
67
  // build link deps
98
68
  if (linkNodes.size) {
99
- this[_resetQueues]()
100
- await this[_build](linkNodes, { type: 'links' })
69
+ this.#resetQueues()
70
+ await this.#build(linkNodes, { type: 'links' })
101
71
  }
102
72
 
103
73
  process.emit('timeEnd', 'build')
@@ -105,20 +75,20 @@ module.exports = cls => class Builder extends cls {
105
75
 
106
76
  // if we don't have a set of nodes, then just rebuild
107
77
  // the actual tree on disk.
108
- async [_loadDefaultNodes] () {
78
+ async #loadDefaultNodes () {
109
79
  let nodes
110
80
  const tree = await this.loadActual()
111
81
  let filterSet
112
- if (!this[_workspacesEnabled]) {
82
+ if (!this.options.workspacesEnabled) {
113
83
  filterSet = this.excludeWorkspacesDependencySet(tree)
114
84
  nodes = tree.inventory.filter(node =>
115
85
  filterSet.has(node) || node.isProjectRoot
116
86
  )
117
- } else if (this[_workspaces] && this[_workspaces].length) {
87
+ } else if (this.options.workspaces.length) {
118
88
  filterSet = this.workspaceDependencySet(
119
89
  tree,
120
- this[_workspaces],
121
- this[_includeWorkspaceRoot]
90
+ this.options.workspaces,
91
+ this.options.includeWorkspaceRoot
122
92
  )
123
93
  nodes = tree.inventory.filter(node => filterSet.has(node))
124
94
  } else {
@@ -127,7 +97,7 @@ module.exports = cls => class Builder extends cls {
127
97
  return nodes
128
98
  }
129
99
 
130
- [_retrieveNodesByType] (nodes) {
100
+ #retrieveNodesByType (nodes) {
131
101
  const depNodes = new Set()
132
102
  const linkNodes = new Set()
133
103
  const storeNodes = new Set()
@@ -154,7 +124,7 @@ module.exports = cls => class Builder extends cls {
154
124
  //
155
125
  // we avoid doing so if global=true since `bin-links` relies
156
126
  // on having the target nodes available in global mode.
157
- if (!this[_global]) {
127
+ if (!this.options.global) {
158
128
  for (const node of linkNodes) {
159
129
  depNodes.delete(node.target)
160
130
  }
@@ -166,8 +136,8 @@ module.exports = cls => class Builder extends cls {
166
136
  }
167
137
  }
168
138
 
169
- [_resetQueues] () {
170
- this[_queues] = {
139
+ #resetQueues () {
140
+ this.#queues = {
171
141
  preinstall: [],
172
142
  install: [],
173
143
  postinstall: [],
@@ -176,46 +146,46 @@ module.exports = cls => class Builder extends cls {
176
146
  }
177
147
  }
178
148
 
179
- async [_build] (nodes, { type = 'deps' }) {
149
+ async #build (nodes, { type = 'deps' }) {
180
150
  process.emit('time', `build:${type}`)
181
151
 
182
- await this[_buildQueues](nodes)
152
+ await this.#buildQueues(nodes)
183
153
 
184
- if (!this[_ignoreScripts]) {
185
- await this[_runScripts]('preinstall')
154
+ if (!this.options.ignoreScripts) {
155
+ await this.#runScripts('preinstall')
186
156
  }
187
157
 
188
158
  // links should run prepare scripts and only link bins after that
189
159
  if (type === 'links') {
190
- await this[_runScripts]('prepare')
160
+ await this.#runScripts('prepare')
191
161
  }
192
- if (this[_binLinks]) {
193
- await this[_linkAllBins]()
162
+ if (this.options.binLinks) {
163
+ await this.#linkAllBins()
194
164
  }
195
165
 
196
- if (!this[_ignoreScripts]) {
197
- await this[_runScripts]('install')
198
- await this[_runScripts]('postinstall')
166
+ if (!this.options.ignoreScripts) {
167
+ await this.#runScripts('install')
168
+ await this.#runScripts('postinstall')
199
169
  }
200
170
 
201
171
  process.emit('timeEnd', `build:${type}`)
202
172
  }
203
173
 
204
- async [_buildQueues] (nodes) {
174
+ async #buildQueues (nodes) {
205
175
  process.emit('time', 'build:queue')
206
176
  const set = new Set()
207
177
 
208
178
  const promises = []
209
179
  for (const node of nodes) {
210
- promises.push(this[_addToBuildSet](node, set))
180
+ promises.push(this.#addToBuildSet(node, set))
211
181
 
212
182
  // if it has bundle deps, add those too, if rebuildBundle
213
- if (this[_rebuildBundle] !== false) {
183
+ if (this.options.rebuildBundle !== false) {
214
184
  const bd = node.package.bundleDependencies
215
185
  if (bd && bd.length) {
216
186
  dfwalk({
217
187
  tree: node,
218
- leave: node => promises.push(this[_addToBuildSet](node, set)),
188
+ leave: node => promises.push(this.#addToBuildSet(node, set)),
219
189
  getChildren: node => [...node.children.values()],
220
190
  filter: node => node.inBundle,
221
191
  })
@@ -236,7 +206,7 @@ module.exports = cls => class Builder extends cls {
236
206
  const tests = { bin, preinstall, install, postinstall, prepare }
237
207
  for (const [key, has] of Object.entries(tests)) {
238
208
  if (has) {
239
- this[_queues][key].push(node)
209
+ this.#queues[key].push(node)
240
210
  }
241
211
  }
242
212
  }
@@ -249,21 +219,21 @@ module.exports = cls => class Builder extends cls {
249
219
  // the node path. Otherwise a package can have a preinstall script
250
220
  // that unlinks something, to allow them to silently overwrite system
251
221
  // binaries, which is unsafe and insecure.
252
- if (!node.globalTop || this[_force]) {
222
+ if (!node.globalTop || this.options.force) {
253
223
  return
254
224
  }
255
225
  const { path, package: pkg } = node
256
226
  await binLinks.checkBins({ pkg, path, top: true, global: true })
257
227
  }
258
228
 
259
- async [_addToBuildSet] (node, set, refreshed = false) {
229
+ async #addToBuildSet (node, set, refreshed = false) {
260
230
  if (set.has(node)) {
261
231
  return
262
232
  }
263
233
 
264
- if (this[_oldMeta] === null) {
234
+ if (this.#oldMeta === null) {
265
235
  const { root: { meta } } = node
266
- this[_oldMeta] = meta && meta.loadedFromDisk &&
236
+ this.#oldMeta = meta && meta.loadedFromDisk &&
267
237
  !(meta.originalLockfileVersion >= 2)
268
238
  }
269
239
 
@@ -272,7 +242,7 @@ module.exports = cls => class Builder extends cls {
272
242
 
273
243
  const { preinstall, install, postinstall, prepare } = scripts
274
244
  const anyScript = preinstall || install || postinstall || prepare
275
- if (!refreshed && !anyScript && (hasInstallScript || this[_oldMeta])) {
245
+ if (!refreshed && !anyScript && (hasInstallScript || this.#oldMeta)) {
276
246
  // we either have an old metadata (and thus might have scripts)
277
247
  // or we have an indication that there's install scripts (but
278
248
  // don't yet know what they are) so we have to load the package.json
@@ -286,7 +256,7 @@ module.exports = cls => class Builder extends cls {
286
256
 
287
257
  const { scripts = {} } = pkg
288
258
  node.package.scripts = scripts
289
- return this[_addToBuildSet](node, set, true)
259
+ return this.#addToBuildSet(node, set, true)
290
260
  }
291
261
 
292
262
  // Rebuild node-gyp dependencies lacking an install or preinstall script
@@ -309,8 +279,8 @@ module.exports = cls => class Builder extends cls {
309
279
  }
310
280
  }
311
281
 
312
- async [_runScripts] (event) {
313
- const queue = this[_queues][event]
282
+ async #runScripts (event) {
283
+ const queue = this.#queues[event]
314
284
 
315
285
  if (!queue.length) {
316
286
  return
@@ -358,7 +328,7 @@ module.exports = cls => class Builder extends cls {
358
328
  pkg,
359
329
  stdio,
360
330
  env,
361
- scriptShell: this[_scriptShell],
331
+ scriptShell: this.options.scriptShell,
362
332
  }
363
333
  const p = runScript(runOpts).catch(er => {
364
334
  const { code, signal } = er
@@ -382,7 +352,7 @@ module.exports = cls => class Builder extends cls {
382
352
  log.info('run', pkg._id, event, { code, signal })
383
353
  })
384
354
 
385
- await (this[_doHandleOptionalFailure]
355
+ await (this.#doHandleOptionalFailure
386
356
  ? this[_handleOptionalFailure](node, p)
387
357
  : p)
388
358
 
@@ -391,8 +361,8 @@ module.exports = cls => class Builder extends cls {
391
361
  process.emit('timeEnd', `build:run:${event}`)
392
362
  }
393
363
 
394
- async [_linkAllBins] () {
395
- const queue = this[_queues].bin
364
+ async #linkAllBins () {
365
+ const queue = this.#queues.bin
396
366
  if (!queue.length) {
397
367
  return
398
368
  }
@@ -402,14 +372,15 @@ module.exports = cls => class Builder extends cls {
402
372
  // sort the queue by node path, so that the module-local collision
403
373
  // detector in bin-links will always resolve the same way.
404
374
  for (const node of queue.sort(sortNodes)) {
405
- promises.push(this[_createBinLinks](node))
375
+ // TODO these run before they're awaited
376
+ promises.push(this.#createBinLinks(node))
406
377
  }
407
378
 
408
379
  await promiseAllRejectLate(promises)
409
380
  process.emit('timeEnd', 'build:link')
410
381
  }
411
382
 
412
- async [_createBinLinks] (node) {
383
+ async #createBinLinks (node) {
413
384
  if (this[_trashList].has(node.path)) {
414
385
  return
415
386
  }
@@ -420,11 +391,11 @@ module.exports = cls => class Builder extends cls {
420
391
  pkg: node.package,
421
392
  path: node.path,
422
393
  top: !!(node.isTop || node.globalTop),
423
- force: this[_force],
394
+ force: this.options.force,
424
395
  global: !!node.globalTop,
425
396
  })
426
397
 
427
- await (this[_doHandleOptionalFailure]
398
+ await (this.#doHandleOptionalFailure
428
399
  ? this[_handleOptionalFailure](node, p)
429
400
  : p)
430
401
 
@@ -24,7 +24,6 @@ const PackageJson = require('@npmcli/package-json')
24
24
  const packageContents = require('@npmcli/installed-package-contents')
25
25
  const runScript = require('@npmcli/run-script')
26
26
  const { checkEngine, checkPlatform } = require('npm-install-checks')
27
- const _force = Symbol.for('force')
28
27
 
29
28
  const treeCheck = require('../tree-check.js')
30
29
  const relpath = require('../relpath.js')
@@ -48,8 +47,6 @@ const _retireShallowNodes = Symbol.for('retireShallowNodes')
48
47
  const _getBundlesByDepth = Symbol('getBundlesByDepth')
49
48
  const _registryResolved = Symbol('registryResolved')
50
49
  const _addNodeToTrashList = Symbol.for('addNodeToTrashList')
51
- const _workspaces = Symbol.for('workspaces')
52
- const _workspacesEnabled = Symbol.for('workspacesEnabled')
53
50
 
54
51
  // shared by rebuild mixin
55
52
  const _trashList = Symbol.for('trashList')
@@ -91,14 +88,11 @@ const _validateNodeModules = Symbol('validateNodeModules')
91
88
  const _nmValidated = Symbol('nmValidated')
92
89
  const _validatePath = Symbol('validatePath')
93
90
  const _reifyPackages = Symbol.for('reifyPackages')
94
- const _includeWorkspaceRoot = Symbol.for('includeWorkspaceRoot')
95
91
 
96
92
  const _omitDev = Symbol('omitDev')
97
93
  const _omitOptional = Symbol('omitOptional')
98
94
  const _omitPeer = Symbol('omitPeer')
99
95
 
100
- const _global = Symbol.for('global')
101
-
102
96
  const _pruneBundledMetadeps = Symbol('pruneBundledMetadeps')
103
97
 
104
98
  // defined by Ideal mixin
@@ -142,7 +136,7 @@ module.exports = cls => class Reifier extends cls {
142
136
  async reify (options = {}) {
143
137
  const linked = (options.installStrategy || this.options.installStrategy) === 'linked'
144
138
 
145
- if (this[_packageLockOnly] && this[_global]) {
139
+ if (this[_packageLockOnly] && this.options.global) {
146
140
  const er = new Error('cannot generate lockfile for global packages')
147
141
  er.code = 'ESHRINKWRAPGLOBAL'
148
142
  throw er
@@ -287,7 +281,7 @@ module.exports = cls => class Reifier extends cls {
287
281
  .then(() => process.emit('timeEnd', 'reify:loadTrees'))
288
282
  }
289
283
 
290
- const actualOpt = this[_global] ? {
284
+ const actualOpt = this.options.global ? {
291
285
  ignoreMissing: true,
292
286
  global: true,
293
287
  filter: (node, kid) => {
@@ -314,7 +308,7 @@ module.exports = cls => class Reifier extends cls {
314
308
  },
315
309
  } : { ignoreMissing: true }
316
310
 
317
- if (!this[_global]) {
311
+ if (!this.options.global) {
318
312
  return Promise.all([
319
313
  this.loadActual(actualOpt),
320
314
  this.buildIdealTree(bitOpt),
@@ -341,12 +335,12 @@ module.exports = cls => class Reifier extends cls {
341
335
  // to just invalidate the parts that changed, but avoid walking the
342
336
  // whole tree again.
343
337
 
344
- const includeWorkspaces = this[_workspacesEnabled]
345
- const includeRootDeps = !this[_workspacesEnabled]
346
- || this[_includeWorkspaceRoot] && this[_workspaces].length > 0
338
+ const includeWorkspaces = this.options.workspacesEnabled
339
+ const includeRootDeps = !includeWorkspaces
340
+ || this.options.includeWorkspaceRoot && this.options.workspaces.length > 0
347
341
 
348
342
  const filterNodes = []
349
- if (this[_global] && this.explicitRequests.size) {
343
+ if (this.options.global && this.explicitRequests.size) {
350
344
  const idealTree = this.idealTree.target
351
345
  const actualTree = this.actualTree.target
352
346
  // we ONLY are allowed to make changes in the global top-level
@@ -364,7 +358,7 @@ module.exports = cls => class Reifier extends cls {
364
358
  } else {
365
359
  if (includeWorkspaces) {
366
360
  // add all ws nodes to filterNodes
367
- for (const ws of this[_workspaces]) {
361
+ for (const ws of this.options.workspaces) {
368
362
  const ideal = this.idealTree.children.get(ws)
369
363
  if (ideal) {
370
364
  filterNodes.push(ideal)
@@ -656,7 +650,7 @@ module.exports = cls => class Reifier extends cls {
656
650
 
657
651
  // do not allow node_modules to be a symlink
658
652
  async [_validateNodeModules] (nm) {
659
- if (this[_force] || this[_nmValidated].has(nm)) {
653
+ if (this.options.force || this[_nmValidated].has(nm)) {
660
654
  return
661
655
  }
662
656
  const st = await lstat(nm).catch(() => null)
@@ -992,11 +986,11 @@ module.exports = cls => class Reifier extends cls {
992
986
  const tree = this.idealTree
993
987
 
994
988
  // if we're operating on a workspace, only audit the workspace deps
995
- if (this[_workspaces] && this[_workspaces].length) {
989
+ if (this.options.workspaces.length) {
996
990
  options.filterSet = this.workspaceDependencySet(
997
991
  tree,
998
- this[_workspaces],
999
- this[_includeWorkspaceRoot]
992
+ this.options.workspaces,
993
+ this.options.includeWorkspaceRoot
1000
994
  )
1001
995
  }
1002
996
 
@@ -1220,7 +1214,7 @@ module.exports = cls => class Reifier extends cls {
1220
1214
  // saveIdealTree to be able to write the lockfile by default.
1221
1215
  const saveIdealTree = !(
1222
1216
  (!save && !hasUpdates)
1223
- || this[_global]
1217
+ || this.options.global
1224
1218
  || this[_dryRun]
1225
1219
  )
1226
1220
 
@@ -1566,7 +1560,7 @@ module.exports = cls => class Reifier extends cls {
1566
1560
  this.actualTree = this.idealTree
1567
1561
  this.idealTree = null
1568
1562
 
1569
- if (!this[_global]) {
1563
+ if (!this.options.global) {
1570
1564
  await this.actualTree.meta.save()
1571
1565
  const ignoreScripts = !!this.options.ignoreScripts
1572
1566
  // if we aren't doing a dry run or ignoring scripts and we actually made changes to the dep
@@ -1593,4 +1587,22 @@ module.exports = cls => class Reifier extends cls {
1593
1587
  }
1594
1588
  }
1595
1589
  }
1590
+
1591
+ async dedupe (options = {}) {
1592
+ // allow the user to set options on the ctor as well.
1593
+ // XXX: deprecate separate method options objects.
1594
+ options = { ...this.options, ...options }
1595
+ const tree = await this.loadVirtual().catch(() => this.loadActual())
1596
+ const names = []
1597
+ for (const name of tree.inventory.query('name')) {
1598
+ if (tree.inventory.query('name', name).size > 1) {
1599
+ names.push(name)
1600
+ }
1601
+ }
1602
+ return this.reify({
1603
+ ...options,
1604
+ preferDedupe: true,
1605
+ update: { names },
1606
+ })
1607
+ }
1596
1608
  }
@@ -2,49 +2,49 @@
2
2
  // are case-insensitive and unicode-normalizing, so we need to treat
3
3
  // node.children.get('FOO') and node.children.get('foo') as the same thing.
4
4
 
5
- const _keys = Symbol('keys')
6
- const _normKey = Symbol('normKey')
7
- const normalize = s => s.normalize('NFKD').toLowerCase()
8
- const OGMap = Map
9
- module.exports = class Map extends OGMap {
5
+ module.exports = class CIMap extends Map {
6
+ #keys = new Map()
7
+
10
8
  constructor (items = []) {
11
9
  super()
12
- this[_keys] = new OGMap()
13
10
  for (const [key, val] of items) {
14
11
  this.set(key, val)
15
12
  }
16
13
  }
17
14
 
18
- [_normKey] (key) {
19
- return typeof key === 'string' ? normalize(key) : key
15
+ #normKey (key) {
16
+ if (typeof key !== 'string') {
17
+ return key
18
+ }
19
+ return key.normalize('NFKD').toLowerCase()
20
20
  }
21
21
 
22
22
  get (key) {
23
- const normKey = this[_normKey](key)
24
- return this[_keys].has(normKey) ? super.get(this[_keys].get(normKey))
23
+ const normKey = this.#normKey(key)
24
+ return this.#keys.has(normKey) ? super.get(this.#keys.get(normKey))
25
25
  : undefined
26
26
  }
27
27
 
28
28
  set (key, val) {
29
- const normKey = this[_normKey](key)
30
- if (this[_keys].has(normKey)) {
31
- super.delete(this[_keys].get(normKey))
29
+ const normKey = this.#normKey(key)
30
+ if (this.#keys.has(normKey)) {
31
+ super.delete(this.#keys.get(normKey))
32
32
  }
33
- this[_keys].set(normKey, key)
33
+ this.#keys.set(normKey, key)
34
34
  return super.set(key, val)
35
35
  }
36
36
 
37
37
  delete (key) {
38
- const normKey = this[_normKey](key)
39
- if (this[_keys].has(normKey)) {
40
- const prevKey = this[_keys].get(normKey)
41
- this[_keys].delete(normKey)
38
+ const normKey = this.#normKey(key)
39
+ if (this.#keys.has(normKey)) {
40
+ const prevKey = this.#keys.get(normKey)
41
+ this.#keys.delete(normKey)
42
42
  return super.delete(prevKey)
43
43
  }
44
44
  }
45
45
 
46
46
  has (key) {
47
- const normKey = this[_normKey](key)
48
- return this[_keys].has(normKey) && super.has(this[_keys].get(normKey))
47
+ const normKey = this.#normKey(key)
48
+ return this.#keys.has(normKey) && super.has(this.#keys.get(normKey))
49
49
  }
50
50
  }
package/lib/index.js CHANGED
@@ -4,5 +4,3 @@ module.exports.Node = require('./node.js')
4
4
  module.exports.Link = require('./link.js')
5
5
  module.exports.Edge = require('./edge.js')
6
6
  module.exports.Shrinkwrap = require('./shrinkwrap.js')
7
- // XXX export the other classes, too. shrinkwrap, diff, etc.
8
- // they're handy!