@npmcli/arborist 7.4.2 → 7.5.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.
package/bin/index.js CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  const fs = require('fs')
4
4
  const path = require('path')
5
+ const { time } = require('proc-log')
5
6
 
6
7
  const { bin, arb: options } = require('./lib/options')
7
8
  const version = require('../package.json').version
@@ -72,11 +73,11 @@ for (const file of commandFiles) {
72
73
 
73
74
  log.info(name, options)
74
75
 
75
- process.emit('time', totalTime)
76
- process.emit('time', scriptTime)
76
+ const timeEnd = time.start(totalTime)
77
+ const scriptEnd = time.start(scriptTime)
77
78
 
78
79
  return command(options, (result) => {
79
- process.emit('timeEnd', scriptTime)
80
+ scriptEnd()
80
81
  return {
81
82
  result,
82
83
  timing: {
@@ -95,7 +96,7 @@ for (const file of commandFiles) {
95
96
  return err
96
97
  })
97
98
  .then((r) => {
98
- process.emit('timeEnd', totalTime)
99
+ timeEnd()
99
100
  if (bin.loglevel !== 'silent') {
100
101
  console[process.exitCode ? 'error' : 'log'](r)
101
102
  }
@@ -1,4 +1,4 @@
1
- const log = require('proc-log')
1
+ const { log } = require('proc-log')
2
2
  const fs = require('fs')
3
3
  const { dirname } = require('path')
4
4
  const os = require('os')
package/bin/lib/timers.js CHANGED
@@ -4,22 +4,22 @@ const log = require('./logging.js')
4
4
  const timers = new Map()
5
5
  const finished = new Map()
6
6
 
7
- process.on('time', name => {
8
- if (timers.has(name)) {
9
- throw new Error('conflicting timer! ' + name)
10
- }
11
- timers.set(name, process.hrtime.bigint())
12
- })
13
-
14
- process.on('timeEnd', name => {
15
- if (!timers.has(name)) {
16
- throw new Error('timer not started! ' + name)
17
- }
18
- const elapsed = Number(process.hrtime.bigint() - timers.get(name))
19
- timers.delete(name)
20
- finished.set(name, elapsed)
21
- if (options.timing) {
22
- log.info('timeEnd', `${name} ${elapsed / 1e9}s`, log.meta({ force: options.timing === 'always' }))
7
+ process.on('time', (level, name) => {
8
+ if (level === 'start') {
9
+ if (timers.has(name)) {
10
+ throw new Error('conflicting timer! ' + name)
11
+ }
12
+ timers.set(name, process.hrtime.bigint())
13
+ } else if (level === 'end') {
14
+ if (!timers.has(name)) {
15
+ throw new Error('timer not started! ' + name)
16
+ }
17
+ const elapsed = Number(process.hrtime.bigint() - timers.get(name))
18
+ timers.delete(name)
19
+ finished.set(name, elapsed)
20
+ if (options.timing) {
21
+ log.info('timeEnd', `${name} ${elapsed / 1e9}s`, log.meta({ force: options.timing === 'always' }))
22
+ }
23
23
  }
24
24
  })
25
25
 
@@ -1,6 +1,6 @@
1
1
  // add and remove dependency specs to/from pkg manifest
2
2
 
3
- const log = require('proc-log')
3
+ const { log } = require('proc-log')
4
4
  const localeCompare = require('@isaacs/string-locale-compare')('en')
5
5
 
6
6
  const add = ({ pkg, add, saveBundle, saveType }) => {
@@ -11,7 +11,7 @@ const treeCheck = require('../tree-check.js')
11
11
  const { readdirScoped } = require('@npmcli/fs')
12
12
  const { lstat, readlink } = require('fs/promises')
13
13
  const { depth } = require('treeverse')
14
- const log = require('proc-log')
14
+ const { log, time } = require('proc-log')
15
15
  const { redact } = require('@npmcli/redact')
16
16
 
17
17
  const {
@@ -179,7 +179,7 @@ module.exports = cls => class IdealTreeBuilder extends cls {
179
179
  options.rm = null
180
180
  }
181
181
 
182
- process.emit('time', 'idealTree')
182
+ const timeEnd = time.start('idealTree')
183
183
 
184
184
  if (!options.add && !options.rm && !options.update && this.options.global) {
185
185
  throw new Error('global requires add, rm, or update option')
@@ -205,7 +205,7 @@ module.exports = cls => class IdealTreeBuilder extends cls {
205
205
  await this.#pruneFailedOptional()
206
206
  await this.#checkEngineAndPlatform()
207
207
  } finally {
208
- process.emit('timeEnd', 'idealTree')
208
+ timeEnd()
209
209
  this.finishTracker('idealTree')
210
210
  }
211
211
 
@@ -278,7 +278,7 @@ module.exports = cls => class IdealTreeBuilder extends cls {
278
278
  // load the initial tree, either the virtualTree from a shrinkwrap,
279
279
  // or just the root node from a package.json
280
280
  async #initTree () {
281
- process.emit('time', 'idealTree:init')
281
+ const timeEnd = time.start('idealTree:init')
282
282
  let root
283
283
  if (this.options.global) {
284
284
  root = await this.#globalRootNode()
@@ -356,7 +356,7 @@ module.exports = cls => class IdealTreeBuilder extends cls {
356
356
  // if you want another one, load another copy.
357
357
  this.idealTree = tree
358
358
  this.virtualTree = null
359
- process.emit('timeEnd', 'idealTree:init')
359
+ timeEnd()
360
360
  return tree
361
361
  })
362
362
  }
@@ -420,7 +420,7 @@ module.exports = cls => class IdealTreeBuilder extends cls {
420
420
  // process the add/rm requests by modifying the root node, and the
421
421
  // update.names request by queueing nodes dependent on those named.
422
422
  async #applyUserRequests (options) {
423
- process.emit('time', 'idealTree:userRequests')
423
+ const timeEnd = time.start('idealTree:userRequests')
424
424
  const tree = this.idealTree.target
425
425
 
426
426
  if (!this.options.workspaces.length) {
@@ -436,7 +436,7 @@ module.exports = cls => class IdealTreeBuilder extends cls {
436
436
  await Promise.all(appliedRequests)
437
437
  }
438
438
 
439
- process.emit('timeEnd', 'idealTree:userRequests')
439
+ timeEnd()
440
440
  }
441
441
 
442
442
  async #applyUserRequestsToNode (tree, options) {
@@ -463,7 +463,7 @@ module.exports = cls => class IdealTreeBuilder extends cls {
463
463
  }
464
464
  const dir = resolve(nm, name)
465
465
  const st = await lstat(dir)
466
- .catch(/* istanbul ignore next */ er => null)
466
+ .catch(/* istanbul ignore next */ () => null)
467
467
  if (st && st.isSymbolicLink()) {
468
468
  const target = await readlink(dir)
469
469
  const real = resolve(dirname(dir), target).replace(/#/g, '%23')
@@ -691,7 +691,7 @@ module.exports = cls => class IdealTreeBuilder extends cls {
691
691
  // if the lockfile is from node v5 or earlier, then we'll have to reload
692
692
  // all the manifests of everything we encounter. this is costly, but at
693
693
  // least it's just a one-time hit.
694
- process.emit('time', 'idealTree:inflate')
694
+ const timeEnd = time.start('idealTree:inflate')
695
695
 
696
696
  // don't warn if we're not gonna actually write it back anyway.
697
697
  const heading = ancient ? 'ancient lockfile' : 'old lockfile'
@@ -758,14 +758,14 @@ This is a one-time fix-up, please be patient...
758
758
  meta.originalLockfileVersion = defaultLockfileVersion
759
759
  }
760
760
  this.finishTracker('idealTree:inflate')
761
- process.emit('timeEnd', 'idealTree:inflate')
761
+ timeEnd()
762
762
  }
763
763
 
764
764
  // at this point we have a virtual tree with the actual root node's
765
765
  // package deps, which may be partly or entirely incomplete, invalid
766
766
  // or extraneous.
767
767
  #buildDeps () {
768
- process.emit('time', 'idealTree:buildDeps')
768
+ const timeEnd = time.start('idealTree:buildDeps')
769
769
  const tree = this.idealTree.target
770
770
  tree.assertRootOverrides()
771
771
  this.#depsQueue.push(tree)
@@ -773,15 +773,14 @@ This is a one-time fix-up, please be patient...
773
773
  // in the override list
774
774
  log.silly('idealTree', 'buildDeps')
775
775
  this.addTracker('idealTree', tree.name, '')
776
- return this.#buildDepStep()
777
- .then(() => process.emit('timeEnd', 'idealTree:buildDeps'))
776
+ return this.#buildDepStep().then(timeEnd)
778
777
  }
779
778
 
780
779
  async #buildDepStep () {
781
780
  // removes tracker of previous dependency in the queue
782
781
  if (this.#currentDep) {
783
782
  const { location, name } = this.#currentDep
784
- process.emit('timeEnd', `idealTree:${location || '#root'}`)
783
+ time.end(`idealTree:${location || '#root'}`)
785
784
  this.finishTracker('idealTree', name, location)
786
785
  this.#currentDep = null
787
786
  }
@@ -807,7 +806,7 @@ This is a one-time fix-up, please be patient...
807
806
 
808
807
  this.#depsSeen.add(node)
809
808
  this.#currentDep = node
810
- process.emit('time', `idealTree:${node.location || '#root'}`)
809
+ time.start(`idealTree:${node.location || '#root'}`)
811
810
 
812
811
  // if we're loading a _complete_ ideal tree, for a --package-lock-only
813
812
  // installation for example, we have to crack open the tarball and
@@ -1025,7 +1024,7 @@ This is a one-time fix-up, please be patient...
1025
1024
  for (const e of this.#problemEdges(placed)) {
1026
1025
  promises.push(() =>
1027
1026
  this.#fetchManifest(npa.resolve(e.name, e.spec, fromPath(placed, e)))
1028
- .catch(er => null)
1027
+ .catch(() => null)
1029
1028
  )
1030
1029
  }
1031
1030
  },
@@ -1274,7 +1273,7 @@ This is a one-time fix-up, please be patient...
1274
1273
  })
1275
1274
  }
1276
1275
 
1277
- #linkFromSpec (name, spec, parent, edge) {
1276
+ #linkFromSpec (name, spec, parent) {
1278
1277
  const realpath = spec.fetchSpec
1279
1278
  const { installLinks, legacyPeerDeps } = this
1280
1279
  return rpj(realpath + '/package.json').catch(() => ({})).then(pkg => {
@@ -1449,7 +1448,7 @@ This is a one-time fix-up, please be patient...
1449
1448
  }
1450
1449
 
1451
1450
  #fixDepFlags () {
1452
- process.emit('time', 'idealTree:fixDepFlags')
1451
+ const timeEnd = time.start('idealTree:fixDepFlags')
1453
1452
  const metaFromDisk = this.idealTree.meta.loadedFromDisk
1454
1453
  const flagsSuspect = this[_flagsSuspect]
1455
1454
  const mutateTree = this.#mutateTree
@@ -1496,7 +1495,7 @@ This is a one-time fix-up, please be patient...
1496
1495
  }
1497
1496
  }
1498
1497
 
1499
- process.emit('timeEnd', 'idealTree:fixDepFlags')
1498
+ timeEnd()
1500
1499
  }
1501
1500
 
1502
1501
  #idealTreePrune () {
@@ -30,7 +30,7 @@ const { resolve } = require('path')
30
30
  const { homedir } = require('os')
31
31
  const { depth } = require('treeverse')
32
32
  const mapWorkspaces = require('@npmcli/map-workspaces')
33
- const log = require('proc-log')
33
+ const { log, time } = require('proc-log')
34
34
 
35
35
  const { saveTypeMap } = require('../add-rm-pkg-deps.js')
36
36
  const AuditReport = require('../audit-report.js')
@@ -66,7 +66,7 @@ const lockfileVersion = lfv => {
66
66
 
67
67
  class Arborist extends Base {
68
68
  constructor (options = {}) {
69
- process.emit('time', 'arborist:ctor')
69
+ const timeEnd = time.start('arborist:ctor')
70
70
  super(options)
71
71
  this.options = {
72
72
  nodeVersion: process.version,
@@ -74,20 +74,26 @@ class Arborist extends Base {
74
74
  Arborist: this.constructor,
75
75
  binLinks: 'binLinks' in options ? !!options.binLinks : true,
76
76
  cache: options.cache || `${homedir()}/.npm/_cacache`,
77
+ dryRun: !!options.dryRun,
78
+ formatPackageLock: 'formatPackageLock' in options ? !!options.formatPackageLock : true,
77
79
  force: !!options.force,
78
80
  global: !!options.global,
79
81
  ignoreScripts: !!options.ignoreScripts,
80
82
  installStrategy: options.global ? 'shallow' : (options.installStrategy ? options.installStrategy : 'hoisted'),
81
83
  lockfileVersion: lockfileVersion(options.lockfileVersion),
84
+ packageLockOnly: !!options.packageLockOnly,
82
85
  packumentCache: options.packumentCache || new Map(),
83
86
  path: options.path || '.',
84
87
  rebuildBundle: 'rebuildBundle' in options ? !!options.rebuildBundle : true,
85
88
  replaceRegistryHost: options.replaceRegistryHost,
89
+ savePrefix: 'savePrefix' in options ? options.savePrefix : '^',
86
90
  scriptShell: options.scriptShell,
87
91
  workspaces: options.workspaces || [],
88
92
  workspacesEnabled: options.workspacesEnabled !== false,
89
93
  }
90
- // TODO is this even used? If not is that a bug?
94
+ // TODO we only ever look at this.options.replaceRegistryHost, not
95
+ // this.replaceRegistryHost. Defaulting needs to be written back to
96
+ // this.options to work properly
91
97
  this.replaceRegistryHost = this.options.replaceRegistryHost =
92
98
  (!this.options.replaceRegistryHost || this.options.replaceRegistryHost === 'npmjs') ?
93
99
  'registry.npmjs.org' : this.options.replaceRegistryHost
@@ -96,8 +102,9 @@ class Arborist extends Base {
96
102
  throw new Error(`Invalid saveType ${options.saveType}`)
97
103
  }
98
104
  this.cache = resolve(this.options.cache)
105
+ this.diff = null
99
106
  this.path = resolve(this.options.path)
100
- process.emit('timeEnd', 'arborist:ctor')
107
+ timeEnd()
101
108
  }
102
109
 
103
110
  // TODO: We should change these to static functions instead
@@ -223,7 +230,7 @@ class Arborist extends Base {
223
230
  // XXX: deprecate separate method options objects.
224
231
  options = { ...this.options, ...options }
225
232
 
226
- process.emit('time', 'audit')
233
+ const timeEnd = time.start('audit')
227
234
  let tree
228
235
  if (options.packageLock === false) {
229
236
  // build ideal tree
@@ -246,10 +253,28 @@ class Arborist extends Base {
246
253
  }
247
254
  this.auditReport = await AuditReport.load(tree, options)
248
255
  const ret = options.fix ? this.reify(options) : this.auditReport
249
- process.emit('timeEnd', 'audit')
256
+ timeEnd()
250
257
  this.finishTracker('audit')
251
258
  return ret
252
259
  }
260
+
261
+ async dedupe (options = {}) {
262
+ // allow the user to set options on the ctor as well.
263
+ // XXX: deprecate separate method options objects.
264
+ options = { ...this.options, ...options }
265
+ const tree = await this.loadVirtual().catch(() => this.loadActual())
266
+ const names = []
267
+ for (const name of tree.inventory.query('name')) {
268
+ if (tree.inventory.query('name', name).size > 1) {
269
+ names.push(name)
270
+ }
271
+ }
272
+ return this.reify({
273
+ ...options,
274
+ preferDedupe: true,
275
+ update: { names },
276
+ })
277
+ }
253
278
  }
254
279
 
255
280
  module.exports = Arborist
@@ -1,7 +1,7 @@
1
1
  const _makeIdealGraph = Symbol('makeIdealGraph')
2
2
  const _createIsolatedTree = Symbol.for('createIsolatedTree')
3
3
  const _createBundledTree = Symbol('createBundledTree')
4
- const fs = require('fs')
4
+ const { mkdirSync } = require('fs')
5
5
  const pacote = require('pacote')
6
6
  const { join } = require('path')
7
7
  const { depth } = require('treeverse')
@@ -108,7 +108,7 @@ module.exports = cls => class IsolatedReifier extends cls {
108
108
  '.store',
109
109
  `${node.name}@${node.version}`
110
110
  )
111
- fs.mkdirSync(dir, { recursive: true })
111
+ mkdirSync(dir, { recursive: true })
112
112
  // TODO this approach feels wrong
113
113
  // and shouldn't be necessary for shrinkwraps
114
114
  await pacote.extract(node.resolved, dir, {
@@ -212,7 +212,7 @@ module.exports = cls => class IsolatedReifier extends cls {
212
212
  return { edges, nodes }
213
213
  }
214
214
 
215
- async [_createIsolatedTree] (idealTree) {
215
+ async [_createIsolatedTree] () {
216
216
  await this[_makeIdealGraph](this.options)
217
217
 
218
218
  const proxiedIdealTree = this.idealGraph
@@ -336,8 +336,8 @@ module.exports = cls => class ActualLoader extends cls {
336
336
  await this.#loadFSChildren(node.target)
337
337
  return Promise.all(
338
338
  [...node.target.children.entries()]
339
- .filter(([name, kid]) => !did.has(kid.realpath))
340
- .map(([name, kid]) => this.#loadFSTree(kid))
339
+ .filter(([, kid]) => !did.has(kid.realpath))
340
+ .map(([, kid]) => this.#loadFSTree(kid))
341
341
  )
342
342
  }
343
343
  }
@@ -283,7 +283,7 @@ module.exports = cls => class VirtualLoader extends cls {
283
283
  return node
284
284
  }
285
285
 
286
- #loadLink (location, targetLoc, target, meta) {
286
+ #loadLink (location, targetLoc, target) {
287
287
  const path = resolve(this.path, location)
288
288
  const link = new Link({
289
289
  installLinks: this.installLinks,
@@ -9,11 +9,8 @@ const binLinks = require('bin-links')
9
9
  const runScript = require('@npmcli/run-script')
10
10
  const { callLimit: promiseCallLimit } = require('promise-call-limit')
11
11
  const { resolve } = require('path')
12
- const {
13
- isNodeGypPackage,
14
- defaultGypInstallScript,
15
- } = require('@npmcli/node-gyp')
16
- const log = require('proc-log')
12
+ const { isNodeGypPackage, defaultGypInstallScript } = require('@npmcli/node-gyp')
13
+ const { log, time } = require('proc-log')
17
14
 
18
15
  const boolEnv = b => b ? '1' : ''
19
16
  const sortNodes = (a, b) =>
@@ -54,7 +51,7 @@ module.exports = cls => class Builder extends cls {
54
51
 
55
52
  // separates links nodes so that it can run
56
53
  // prepare scripts and link bins in the expected order
57
- process.emit('time', 'build')
54
+ const timeEnd = time.start('build')
58
55
 
59
56
  const {
60
57
  depNodes,
@@ -70,7 +67,7 @@ module.exports = cls => class Builder extends cls {
70
67
  await this.#build(linkNodes, { type: 'links' })
71
68
  }
72
69
 
73
- process.emit('timeEnd', 'build')
70
+ timeEnd()
74
71
  }
75
72
 
76
73
  // if we don't have a set of nodes, then just rebuild
@@ -147,7 +144,7 @@ module.exports = cls => class Builder extends cls {
147
144
  }
148
145
 
149
146
  async #build (nodes, { type = 'deps' }) {
150
- process.emit('time', `build:${type}`)
147
+ const timeEnd = time.start(`build:${type}`)
151
148
 
152
149
  await this.#buildQueues(nodes)
153
150
 
@@ -168,11 +165,11 @@ module.exports = cls => class Builder extends cls {
168
165
  await this.#runScripts('postinstall')
169
166
  }
170
167
 
171
- process.emit('timeEnd', `build:${type}`)
168
+ timeEnd()
172
169
  }
173
170
 
174
171
  async #buildQueues (nodes) {
175
- process.emit('time', 'build:queue')
172
+ const timeEnd = time.start('build:queue')
176
173
  const set = new Set()
177
174
 
178
175
  const promises = []
@@ -210,7 +207,7 @@ module.exports = cls => class Builder extends cls {
210
207
  }
211
208
  }
212
209
  }
213
- process.emit('timeEnd', 'build:queue')
210
+ timeEnd()
214
211
  }
215
212
 
216
213
  async [_checkBins] (node) {
@@ -286,7 +283,7 @@ module.exports = cls => class Builder extends cls {
286
283
  return
287
284
  }
288
285
 
289
- process.emit('time', `build:run:${event}`)
286
+ const timeEnd = time.start(`build:run:${event}`)
290
287
  const stdio = this.options.foregroundScripts ? 'inherit' : 'pipe'
291
288
  const limit = this.options.foregroundScripts ? 1 : undefined
292
289
  await promiseCallLimit(queue.map(node => async () => {
@@ -309,8 +306,7 @@ module.exports = cls => class Builder extends cls {
309
306
  return
310
307
  }
311
308
 
312
- const timer = `build:run:${event}:${location}`
313
- process.emit('time', timer)
309
+ const timeEndLocation = time.start(`build:run:${event}:${location}`)
314
310
  log.info('run', pkg._id, event, location, pkg.scripts[event])
315
311
  const env = {
316
312
  npm_package_resolved: resolved,
@@ -356,9 +352,9 @@ module.exports = cls => class Builder extends cls {
356
352
  ? this[_handleOptionalFailure](node, p)
357
353
  : p)
358
354
 
359
- process.emit('timeEnd', timer)
355
+ timeEndLocation()
360
356
  }), { limit })
361
- process.emit('timeEnd', `build:run:${event}`)
357
+ timeEnd()
362
358
  }
363
359
 
364
360
  async #linkAllBins () {
@@ -367,7 +363,7 @@ module.exports = cls => class Builder extends cls {
367
363
  return
368
364
  }
369
365
 
370
- process.emit('time', 'build:link')
366
+ const timeEnd = time.start('build:link')
371
367
  const promises = []
372
368
  // sort the queue by node path, so that the module-local collision
373
369
  // detector in bin-links will always resolve the same way.
@@ -377,7 +373,7 @@ module.exports = cls => class Builder extends cls {
377
373
  }
378
374
 
379
375
  await promiseAllRejectLate(promises)
380
- process.emit('timeEnd', 'build:link')
376
+ timeEnd()
381
377
  }
382
378
 
383
379
  async #createBinLinks (node) {
@@ -385,7 +381,7 @@ module.exports = cls => class Builder extends cls {
385
381
  return
386
382
  }
387
383
 
388
- process.emit('time', `build:link:${node.location}`)
384
+ const timeEnd = time.start(`build:link:${node.location}`)
389
385
 
390
386
  const p = binLinks({
391
387
  pkg: node.package,
@@ -399,6 +395,6 @@ module.exports = cls => class Builder extends cls {
399
395
  ? this[_handleOptionalFailure](node, p)
400
396
  : p)
401
397
 
402
- process.emit('timeEnd', `build:link:${node.location}`)
398
+ timeEnd()
403
399
  }
404
400
  }