@npmcli/arborist 4.3.1 → 5.0.2

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.
@@ -1,59 +1,123 @@
1
- const options = module.exports = {
2
- path: undefined,
3
- cache: `${process.env.HOME}/.npm/_cacache`,
4
- _: [],
1
+ const nopt = require('nopt')
2
+ const path = require('path')
3
+
4
+ const has = (o, k) => Object.prototype.hasOwnProperty.call(o, k)
5
+
6
+ const cleanPath = (val) => {
7
+ const k = Symbol('key')
8
+ const data = {}
9
+ nopt.typeDefs.path.validate(data, k, val)
10
+ return data[k]
5
11
  }
6
12
 
7
- for (const arg of process.argv.slice(2)) {
8
- if (/^--add=/.test(arg)) {
9
- options.add = options.add || []
10
- options.add.push(arg.substr('--add='.length))
11
- } else if (/^--rm=/.test(arg)) {
12
- options.rm = options.rm || []
13
- options.rm.push(arg.substr('--rm='.length))
14
- } else if (arg === '--global') {
15
- options.global = true
16
- } else if (arg === '--global-style') {
17
- options.globalStyle = true
18
- } else if (arg === '--prefer-dedupe') {
19
- options.preferDedupe = true
20
- } else if (arg === '--legacy-peer-deps') {
21
- options.legacyPeerDeps = true
22
- } else if (arg === '--force') {
23
- options.force = true
24
- } else if (arg === '--update-all') {
25
- options.update = options.update || {}
26
- options.update.all = true
27
- } else if (/^--update=/.test(arg)) {
28
- options.update = options.update || {}
29
- options.update.names = options.update.names || []
30
- options.update.names.push(arg.substr('--update='.length))
31
- } else if (/^--omit=/.test(arg)) {
32
- options.omit = options.omit || []
33
- options.omit.push(arg.substr('--omit='.length))
34
- } else if (/^--before=/.test(arg)) {
35
- options.before = new Date(arg.substr('--before='.length))
36
- } else if (/^-w.+/.test(arg)) {
37
- options.workspaces = options.workspaces || []
38
- options.workspaces.push(arg.replace(/^-w/, ''))
39
- } else if (/^--workspace=/.test(arg)) {
40
- options.workspaces = options.workspaces || []
41
- options.workspaces.push(arg.replace(/^--workspace=/, ''))
42
- } else if (/^--[^=]+=/.test(arg)) {
43
- const [key, ...v] = arg.replace(/^--/, '').split('=')
44
- const val = v.join('=')
45
- options[key] = val === 'false' ? false : val === 'true' ? true : val
46
- } else if (/^--.+/.test(arg)) {
47
- options[arg.replace(/^--/, '')] = true
48
- } else if (options.path === undefined) {
49
- options.path = arg
50
- } else {
51
- options._.push(arg)
13
+ const parse = (...noptArgs) => {
14
+ const binOnlyOpts = {
15
+ command: String,
16
+ loglevel: String,
17
+ colors: Boolean,
18
+ timing: ['always', Boolean],
19
+ logfile: String,
20
+ }
21
+
22
+ const arbOpts = {
23
+ add: Array,
24
+ rm: Array,
25
+ omit: Array,
26
+ update: Array,
27
+ workspaces: Array,
28
+ global: Boolean,
29
+ force: Boolean,
30
+ 'global-style': Boolean,
31
+ 'prefer-dedupe': Boolean,
32
+ 'legacy-peer-deps': Boolean,
33
+ 'update-all': Boolean,
34
+ before: Date,
35
+ path: path,
36
+ cache: path,
37
+ ...binOnlyOpts,
38
+ }
39
+
40
+ const short = {
41
+ quiet: ['--loglevel', 'warn'],
42
+ logs: ['--logfile', 'true'],
43
+ w: '--workspaces',
44
+ g: '--global',
45
+ f: '--force',
46
+ }
47
+
48
+ const defaults = {
49
+ // key order is important for command and path
50
+ // since they shift positional args
51
+ // command is 1st, path is 2nd
52
+ command: (o) => o.argv.remain.shift(),
53
+ path: (o) => cleanPath(o.argv.remain.shift() || '.'),
54
+ colors: has(process.env, 'NO_COLOR') ? false : !!process.stderr.isTTY,
55
+ loglevel: 'silly',
56
+ timing: (o) => o.loglevel === 'silly',
57
+ cache: `${process.env.HOME}/.npm/_cacache`,
58
+ }
59
+
60
+ const derived = [
61
+ // making update either `all` or an array of names but not both
62
+ ({ updateAll: all, update: names, ...o }) => {
63
+ if (all || names) {
64
+ o.update = all != null ? { all } : { names }
65
+ }
66
+ return o
67
+ },
68
+ ({ logfile, ...o }) => {
69
+ // logfile is parsed as a string so if its true or set but empty
70
+ // then set the default logfile
71
+ if (logfile === 'true' || logfile === '') {
72
+ logfile = `arb-log-${new Date().toISOString().replace(/[.:]/g, '_')}.log`
73
+ }
74
+ // then parse it the same as nopt parses other paths
75
+ if (logfile) {
76
+ o.logfile = cleanPath(logfile)
77
+ }
78
+ return o
79
+ },
80
+ ]
81
+
82
+ const transforms = [
83
+ // Camelcase all top level keys
84
+ (o) => {
85
+ const entries = Object.entries(o).map(([k, v]) => [
86
+ k.replace(/-./g, s => s[1].toUpperCase()),
87
+ v,
88
+ ])
89
+ return Object.fromEntries(entries)
90
+ },
91
+ // Set defaults on unset keys
92
+ (o) => {
93
+ for (const [k, v] of Object.entries(defaults)) {
94
+ if (!has(o, k)) {
95
+ o[k] = typeof v === 'function' ? v(o) : v
96
+ }
97
+ }
98
+ return o
99
+ },
100
+ // Set/unset derived values
101
+ ...derived.map((derive) => (o) => derive(o) || o),
102
+ // Separate bin and arborist options
103
+ ({ argv: { remain: _ }, ...o }) => {
104
+ const bin = { _ }
105
+ for (const k of Object.keys(binOnlyOpts)) {
106
+ if (has(o, k)) {
107
+ bin[k] = o[k]
108
+ delete o[k]
109
+ }
110
+ }
111
+ return { bin, arb: o }
112
+ },
113
+ ]
114
+
115
+ let options = nopt(arbOpts, short, ...noptArgs)
116
+ for (const t of transforms) {
117
+ options = t(options)
52
118
  }
53
- }
54
119
 
55
- if (options.path === undefined) {
56
- options.path = '.'
120
+ return options
57
121
  }
58
122
 
59
- console.error(options)
123
+ module.exports = parse()
@@ -1,5 +1,4 @@
1
1
  const { inspect } = require('util')
2
- const { quiet } = require('./options.js')
2
+ const log = require('./logging.js')
3
3
 
4
- module.exports = quiet ? () => {}
5
- : tree => console.log(inspect(tree.toJSON(), { depth: Infinity }))
4
+ module.exports = tree => log.info(inspect(tree.toJSON(), { depth: Infinity }))
package/bin/lib/timers.js CHANGED
@@ -1,31 +1,33 @@
1
- const timers = Object.create(null)
2
- const { format } = require('util')
3
- const options = require('./options.js')
1
+ const { bin: options } = require('./options.js')
2
+ const log = require('./logging.js')
3
+
4
+ const timers = new Map()
5
+ const finished = new Map()
4
6
 
5
7
  process.on('time', name => {
6
- if (timers[name]) {
8
+ if (timers.has(name)) {
7
9
  throw new Error('conflicting timer! ' + name)
8
10
  }
9
- timers[name] = process.hrtime()
11
+ timers.set(name, process.hrtime.bigint())
10
12
  })
11
13
 
12
- const dim = process.stderr.isTTY ? msg => `\x1B[2m${msg}\x1B[22m` : m => m
13
- const red = process.stderr.isTTY ? msg => `\x1B[31m${msg}\x1B[39m` : m => m
14
14
  process.on('timeEnd', name => {
15
- if (!timers[name]) {
15
+ if (!timers.has(name)) {
16
16
  throw new Error('timer not started! ' + name)
17
17
  }
18
- const res = process.hrtime(timers[name])
19
- delete timers[name]
20
- const msg = format(`${process.pid} ${name}`, res[0] * 1e3 + res[1] / 1e6)
21
- if (options.timers !== false) {
22
- console.error(dim(msg))
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' }))
23
23
  }
24
24
  })
25
25
 
26
26
  process.on('exit', () => {
27
- for (const name of Object.keys(timers)) {
28
- console.error(red('Dangling timer:'), name)
27
+ for (const name of timers.keys()) {
28
+ log.error('timeError', 'Dangling timer:', name)
29
29
  process.exitCode = 1
30
30
  }
31
31
  })
32
+
33
+ module.exports = finished
package/bin/license.js CHANGED
@@ -1,38 +1,48 @@
1
+ const localeCompare = require('@isaacs/string-locale-compare')('en')
1
2
  const Arborist = require('../')
2
- const options = require('./lib/options.js')
3
- require('./lib/logging.js')
4
- require('./lib/timers.js')
3
+ const log = require('./lib/logging.js')
5
4
 
6
- const a = new Arborist(options)
7
- const query = options._.shift()
5
+ module.exports = (options, time) => {
6
+ const query = options._.shift()
7
+ const a = new Arborist(options)
8
+ return a
9
+ .loadVirtual()
10
+ .then(tree => {
11
+ // only load the actual tree if the virtual one doesn't have modern metadata
12
+ if (!tree.meta || !(tree.meta.originalLockfileVersion >= 2)) {
13
+ throw 'load actual'
14
+ } else {
15
+ return tree
16
+ }
17
+ }).catch((er) => {
18
+ log.error('loading actual tree', er)
19
+ return a.loadActual()
20
+ })
21
+ .then(time)
22
+ .then(({ result: tree }) => {
23
+ const output = []
24
+ if (!query) {
25
+ const set = []
26
+ for (const license of tree.inventory.query('license')) {
27
+ set.push([tree.inventory.query('license', license).size, license])
28
+ }
8
29
 
9
- a.loadVirtual().then(tree => {
10
- // only load the actual tree if the virtual one doesn't have modern metadata
11
- if (!tree.meta || !(tree.meta.originalLockfileVersion >= 2)) {
12
- throw 'load actual'
13
- } else {
14
- return tree
15
- }
16
- }).catch((er) => {
17
- console.error('loading actual tree', er)
18
- return a.loadActual()
19
- }).then(tree => {
20
- if (!query) {
21
- const set = []
22
- for (const license of tree.inventory.query('license')) {
23
- set.push([tree.inventory.query('license', license).size, license])
24
- }
30
+ for (const [count, license] of set.sort((a, b) =>
31
+ a[1] && b[1] ? b[0] - a[0] || localeCompare(a[1], b[1])
32
+ : a[1] ? -1
33
+ : b[1] ? 1
34
+ : 0)) {
35
+ output.push(`${count} ${license}`)
36
+ log.info(count, license)
37
+ }
38
+ } else {
39
+ for (const node of tree.inventory.query('license', query === 'undefined' ? undefined : query)) {
40
+ const msg = `${node.name} ${node.location} ${node.package.description || ''}`
41
+ output.push(msg)
42
+ log.info(msg)
43
+ }
44
+ }
25
45
 
26
- for (const [count, license] of set.sort((a, b) =>
27
- a[1] && b[1] ? b[0] - a[0] || a[1].localeCompare(b[1], 'en')
28
- : a[1] ? -1
29
- : b[1] ? 1
30
- : 0)) {
31
- console.log(count, license)
32
- }
33
- } else {
34
- for (const node of tree.inventory.query('license', query === 'undefined' ? undefined : query)) {
35
- console.log(`${node.name} ${node.location} ${node.package.description || ''}`)
36
- }
37
- }
38
- })
46
+ return output.join('\n')
47
+ })
48
+ }
package/bin/prune.js CHANGED
@@ -1,9 +1,7 @@
1
1
  const Arborist = require('../')
2
2
 
3
- const options = require('./lib/options.js')
4
- const print = require('./lib/print-tree.js')
5
- require('./lib/logging.js')
6
- require('./lib/timers.js')
3
+ const printTree = require('./lib/print-tree.js')
4
+ const log = require('./lib/logging.js')
7
5
 
8
6
  const printDiff = diff => {
9
7
  const { depth } = require('treeverse')
@@ -15,13 +13,13 @@ const printDiff = diff => {
15
13
  }
16
14
  switch (d.action) {
17
15
  case 'REMOVE':
18
- console.error('REMOVE', d.actual.location)
16
+ log.info('REMOVE', d.actual.location)
19
17
  break
20
18
  case 'ADD':
21
- console.error('ADD', d.ideal.location, d.ideal.resolved)
19
+ log.info('ADD', d.ideal.location, d.ideal.resolved)
22
20
  break
23
21
  case 'CHANGE':
24
- console.error('CHANGE', d.actual.location, {
22
+ log.info('CHANGE', d.actual.location, {
25
23
  from: d.actual.resolved,
26
24
  to: d.ideal.resolved,
27
25
  })
@@ -32,18 +30,19 @@ const printDiff = diff => {
32
30
  })
33
31
  }
34
32
 
35
- const start = process.hrtime()
36
- process.emit('time', 'install')
37
- const arb = new Arborist(options)
38
- arb.prune(options).then(tree => {
39
- process.emit('timeEnd', 'install')
40
- const end = process.hrtime(start)
41
- print(tree)
42
- if (options.dryRun) {
43
- printDiff(arb.diff)
44
- }
45
- console.error(`resolved ${tree.inventory.size} deps in ${end[0] + end[1] / 1e9}s`)
46
- if (tree.meta && options.save) {
47
- tree.meta.save()
48
- }
49
- }).catch(er => console.error(require('util').inspect(er, { depth: Infinity })))
33
+ module.exports = (options, time) => {
34
+ const arb = new Arborist(options)
35
+ return arb
36
+ .prune(options)
37
+ .then(time)
38
+ .then(async ({ timing, result: tree }) => {
39
+ printTree(tree)
40
+ if (options.dryRun) {
41
+ printDiff(arb.diff)
42
+ }
43
+ if (tree.meta && options.save) {
44
+ await tree.meta.save()
45
+ }
46
+ return `resolved ${tree.inventory.size} deps in ${timing.seconds}`
47
+ })
48
+ }
package/bin/reify.js CHANGED
@@ -1,9 +1,7 @@
1
1
  const Arborist = require('../')
2
2
 
3
- const options = require('./lib/options.js')
4
- const print = require('./lib/print-tree.js')
5
- require('./lib/logging.js')
6
- require('./lib/timers.js')
3
+ const printTree = require('./lib/print-tree.js')
4
+ const log = require('./lib/logging.js')
7
5
 
8
6
  const printDiff = diff => {
9
7
  const { depth } = require('treeverse')
@@ -15,13 +13,13 @@ const printDiff = diff => {
15
13
  }
16
14
  switch (d.action) {
17
15
  case 'REMOVE':
18
- console.error('REMOVE', d.actual.location)
16
+ log.info('REMOVE', d.actual.location)
19
17
  break
20
18
  case 'ADD':
21
- console.error('ADD', d.ideal.location, d.ideal.resolved)
19
+ log.info('ADD', d.ideal.location, d.ideal.resolved)
22
20
  break
23
21
  case 'CHANGE':
24
- console.error('CHANGE', d.actual.location, {
22
+ log.info('CHANGE', d.actual.location, {
25
23
  from: d.actual.resolved,
26
24
  to: d.ideal.resolved,
27
25
  })
@@ -32,18 +30,19 @@ const printDiff = diff => {
32
30
  })
33
31
  }
34
32
 
35
- const start = process.hrtime()
36
- process.emit('time', 'install')
37
- const arb = new Arborist(options)
38
- arb.reify(options).then(tree => {
39
- process.emit('timeEnd', 'install')
40
- const end = process.hrtime(start)
41
- print(tree)
42
- if (options.dryRun) {
43
- printDiff(arb.diff)
44
- }
45
- console.error(`resolved ${tree.inventory.size} deps in ${end[0] + end[1] / 1e9}s`)
46
- if (tree.meta && options.save) {
47
- tree.meta.save()
48
- }
49
- }).catch(er => console.error(require('util').inspect(er, { depth: Infinity })))
33
+ module.exports = (options, time) => {
34
+ const arb = new Arborist(options)
35
+ return arb
36
+ .reify(options)
37
+ .then(time)
38
+ .then(async ({ timing, result: tree }) => {
39
+ printTree(tree)
40
+ if (options.dryRun) {
41
+ printDiff(arb.diff)
42
+ }
43
+ if (tree.meta && options.save) {
44
+ await tree.meta.save()
45
+ }
46
+ return `resolved ${tree.inventory.size} deps in ${timing.seconds}`
47
+ })
48
+ }
package/bin/shrinkwrap.js CHANGED
@@ -1,12 +1,7 @@
1
1
  const Shrinkwrap = require('../lib/shrinkwrap.js')
2
- const options = require('./lib/options.js')
3
- require('./lib/logging.js')
4
- require('./lib/timers.js')
5
2
 
6
- const { quiet } = options
7
- Shrinkwrap.load(options)
8
- .then(s => quiet || console.log(JSON.stringify(s.commit(), 0, 2)))
9
- .catch(er => {
10
- console.error('shrinkwrap load failure', er)
11
- process.exit(1)
12
- })
3
+ module.exports = (options, time) => Shrinkwrap
4
+ .load(options)
5
+ .then((s) => s.commit())
6
+ .then(time)
7
+ .then(({ result: s }) => JSON.stringify(s, 0, 2))
package/bin/virtual.js CHANGED
@@ -1,18 +1,14 @@
1
1
  const Arborist = require('../')
2
2
 
3
- const print = require('./lib/print-tree.js')
4
- const options = require('./lib/options.js')
5
- require('./lib/logging.js')
6
- require('./lib/timers.js')
3
+ const printTree = require('./lib/print-tree.js')
7
4
 
8
- const start = process.hrtime()
9
- new Arborist(options).loadVirtual().then(tree => {
10
- const end = process.hrtime(start)
11
- if (!options.quiet) {
12
- print(tree)
13
- }
14
- if (options.save) {
15
- tree.meta.save()
16
- }
17
- console.error(`read ${tree.inventory.size} deps in ${end[0] * 1000 + end[1] / 1e6}ms`)
18
- }).catch(er => console.error(er))
5
+ module.exports = (options, time) => new Arborist(options)
6
+ .loadVirtual()
7
+ .then(time)
8
+ .then(async ({ timing, result: tree }) => {
9
+ printTree(tree)
10
+ if (options.save) {
11
+ await tree.meta.save()
12
+ }
13
+ return `read ${tree.inventory.size} deps in ${timing.ms}`
14
+ })
@@ -1,10 +1,11 @@
1
1
  // add and remove dependency specs to/from pkg manifest
2
2
 
3
+ const log = require('proc-log')
3
4
  const localeCompare = require('@isaacs/string-locale-compare')('en')
4
5
 
5
- const add = ({ pkg, add, saveBundle, saveType, log }) => {
6
+ const add = ({ pkg, add, saveBundle, saveType }) => {
6
7
  for (const spec of add) {
7
- addSingle({ pkg, spec, saveBundle, saveType, log })
8
+ addSingle({ pkg, spec, saveBundle, saveType })
8
9
  }
9
10
 
10
11
  return pkg
@@ -20,7 +21,7 @@ const saveTypeMap = new Map([
20
21
  ['peer', 'peerDependencies'],
21
22
  ])
22
23
 
23
- const addSingle = ({ pkg, spec, saveBundle, saveType, log }) => {
24
+ const addSingle = ({ pkg, spec, saveBundle, saveType }) => {
24
25
  const { name, rawSpec } = spec
25
26
 
26
27
  // if the user does not give us a type, we infer which type(s)
@@ -34,19 +35,19 @@ const addSingle = ({ pkg, spec, saveBundle, saveType, log }) => {
34
35
  if (saveType === 'prod') {
35
36
  // a production dependency can only exist as production (rpj ensures it
36
37
  // doesn't coexist w/ optional)
37
- deleteSubKey(pkg, 'devDependencies', name, 'dependencies', log)
38
- deleteSubKey(pkg, 'peerDependencies', name, 'dependencies', log)
38
+ deleteSubKey(pkg, 'devDependencies', name, 'dependencies')
39
+ deleteSubKey(pkg, 'peerDependencies', name, 'dependencies')
39
40
  } else if (saveType === 'dev') {
40
41
  // a dev dependency may co-exist as peer, or optional, but not production
41
- deleteSubKey(pkg, 'dependencies', name, 'devDependencies', log)
42
+ deleteSubKey(pkg, 'dependencies', name, 'devDependencies')
42
43
  } else if (saveType === 'optional') {
43
44
  // an optional dependency may co-exist as dev (rpj ensures it doesn't
44
45
  // coexist w/ prod)
45
- deleteSubKey(pkg, 'peerDependencies', name, 'optionalDependencies', log)
46
+ deleteSubKey(pkg, 'peerDependencies', name, 'optionalDependencies')
46
47
  } else { // peer or peerOptional is all that's left
47
48
  // a peer dependency may coexist as dev
48
- deleteSubKey(pkg, 'dependencies', name, 'peerDependencies', log)
49
- deleteSubKey(pkg, 'optionalDependencies', name, 'peerDependencies', log)
49
+ deleteSubKey(pkg, 'dependencies', name, 'peerDependencies')
50
+ deleteSubKey(pkg, 'optionalDependencies', name, 'peerDependencies')
50
51
  }
51
52
 
52
53
  const depType = saveTypeMap.get(saveType)
@@ -108,9 +109,9 @@ const hasSubKey = (pkg, depType, name) => {
108
109
  }
109
110
 
110
111
  // Removes a subkey and warns about it if it's being replaced
111
- const deleteSubKey = (pkg, depType, name, replacedBy, log) => {
112
+ const deleteSubKey = (pkg, depType, name, replacedBy) => {
112
113
  if (hasSubKey(pkg, depType, name)) {
113
- if (replacedBy && log) {
114
+ if (replacedBy) {
114
115
  log.warn('idealTree', `Removing ${depType}.${name} in favor of ${replacedBy}.${name}`)
115
116
  }
116
117
  delete pkg[depType][name]