@npmcli/arborist 2.8.2 → 2.9.0

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.
Files changed (49) hide show
  1. package/bin/actual.js +4 -2
  2. package/bin/audit.js +12 -6
  3. package/bin/dedupe.js +6 -3
  4. package/bin/funding.js +4 -2
  5. package/bin/ideal.js +2 -1
  6. package/bin/lib/logging.js +4 -3
  7. package/bin/lib/options.js +14 -12
  8. package/bin/lib/timers.js +6 -3
  9. package/bin/license.js +9 -5
  10. package/bin/prune.js +6 -3
  11. package/bin/reify.js +6 -3
  12. package/bin/virtual.js +4 -2
  13. package/lib/add-rm-pkg-deps.js +28 -15
  14. package/lib/arborist/audit.js +2 -1
  15. package/lib/arborist/build-ideal-tree.js +139 -72
  16. package/lib/arborist/deduper.js +2 -1
  17. package/lib/arborist/index.js +8 -4
  18. package/lib/arborist/load-actual.js +28 -13
  19. package/lib/arborist/load-virtual.js +37 -20
  20. package/lib/arborist/load-workspaces.js +4 -2
  21. package/lib/arborist/rebuild.js +34 -17
  22. package/lib/arborist/reify.js +153 -76
  23. package/lib/audit-report.js +44 -23
  24. package/lib/calc-dep-flags.js +18 -9
  25. package/lib/can-place-dep.js +59 -30
  26. package/lib/case-insensitive-map.js +4 -2
  27. package/lib/consistent-resolve.js +2 -1
  28. package/lib/deepest-nesting-target.js +4 -2
  29. package/lib/dep-valid.js +8 -4
  30. package/lib/diff.js +74 -22
  31. package/lib/edge.js +26 -13
  32. package/lib/gather-dep-set.js +2 -1
  33. package/lib/inventory.js +12 -6
  34. package/lib/link.js +14 -9
  35. package/lib/node.js +216 -113
  36. package/lib/optional-set.js +4 -2
  37. package/lib/peer-entry-sets.js +10 -5
  38. package/lib/place-dep.js +111 -37
  39. package/lib/printable.js +46 -25
  40. package/lib/realpath.js +12 -6
  41. package/lib/shrinkwrap.js +164 -90
  42. package/lib/signal-handling.js +6 -3
  43. package/lib/spec-from-lock.js +7 -4
  44. package/lib/tracker.js +24 -18
  45. package/lib/tree-check.js +12 -6
  46. package/lib/version-from-tgz.js +4 -2
  47. package/lib/vuln.js +44 -22
  48. package/lib/yarn-lock.js +34 -21
  49. package/package.json +8 -10
package/lib/tracker.js CHANGED
@@ -11,35 +11,37 @@ module.exports = cls => class Tracker extends cls {
11
11
 
12
12
  addTracker (section, subsection = null, key = null) {
13
13
  // TrackerGroup type object not found
14
- if (!this.log.newGroup)
14
+ if (!this.log.newGroup) {
15
15
  return
16
+ }
16
17
 
17
- if (section === null || section === undefined)
18
+ if (section === null || section === undefined) {
18
19
  this[_onError](`Tracker can't be null or undefined`)
20
+ }
19
21
 
20
- if (key === null)
22
+ if (key === null) {
21
23
  key = subsection
24
+ }
22
25
 
23
26
  const hasTracker = this[_progress].has(section)
24
27
  const hasSubtracker = this[_progress].has(`${section}:${key}`)
25
28
 
26
- if (hasTracker && subsection === null)
29
+ if (hasTracker && subsection === null) {
27
30
  // 0. existing tracker, no subsection
28
31
  this[_onError](`Tracker "${section}" already exists`)
29
-
30
- else if (!hasTracker && subsection === null) {
32
+ } else if (!hasTracker && subsection === null) {
31
33
  // 1. no existing tracker, no subsection
32
34
  // Create a new tracker from this.log
33
35
  // starts progress bar
34
- if (this[_progress].size === 0)
36
+ if (this[_progress].size === 0) {
35
37
  this.log.enableProgress()
38
+ }
36
39
 
37
40
  this[_progress].set(section, this.log.newGroup(section))
38
- } else if (!hasTracker && subsection !== null)
41
+ } else if (!hasTracker && subsection !== null) {
39
42
  // 2. no parent tracker and subsection
40
43
  this[_onError](`Parent tracker "${section}" does not exist`)
41
-
42
- else if (!hasTracker || !hasSubtracker) {
44
+ } else if (!hasTracker || !hasSubtracker) {
43
45
  // 3. existing parent tracker, no subsection tracker
44
46
  // Create a new subtracker in this[_progress] from parent tracker
45
47
  this[_progress].set(`${section}:${key}`,
@@ -52,14 +54,17 @@ module.exports = cls => class Tracker extends cls {
52
54
 
53
55
  finishTracker (section, subsection = null, key = null) {
54
56
  // TrackerGroup type object not found
55
- if (!this.log.newGroup)
57
+ if (!this.log.newGroup) {
56
58
  return
59
+ }
57
60
 
58
- if (section === null || section === undefined)
61
+ if (section === null || section === undefined) {
59
62
  this[_onError](`Tracker can't be null or undefined`)
63
+ }
60
64
 
61
- if (key === null)
65
+ if (key === null) {
62
66
  key = subsection
67
+ }
63
68
 
64
69
  const hasTracker = this[_progress].has(section)
65
70
  const hasSubtracker = this[_progress].has(`${section}:${key}`)
@@ -71,8 +76,9 @@ module.exports = cls => class Tracker extends cls {
71
76
  // not have any remaining children
72
77
  const keys = this[_progress].keys()
73
78
  for (const key of keys) {
74
- if (key.match(new RegExp(section + ':')))
79
+ if (key.match(new RegExp(section + ':'))) {
75
80
  this.finishTracker(section, key)
81
+ }
76
82
  }
77
83
 
78
84
  // remove parent tracker
@@ -81,13 +87,13 @@ module.exports = cls => class Tracker extends cls {
81
87
 
82
88
  // remove progress bar if all
83
89
  // trackers are finished
84
- if (this[_progress].size === 0)
90
+ if (this[_progress].size === 0) {
85
91
  this.log.disableProgress()
86
- } else if (!hasTracker && subsection === null)
92
+ }
93
+ } else if (!hasTracker && subsection === null) {
87
94
  // 1. no existing parent tracker, no subsection
88
95
  this[_onError](`Tracker "${section}" does not exist`)
89
-
90
- else if (!hasTracker || hasSubtracker) {
96
+ } else if (!hasTracker || hasSubtracker) {
91
97
  // 2. subtracker exists
92
98
  // Finish subtracker and remove from this[_progress]
93
99
  this[_progress].get(`${section}:${key}`).finish()
package/lib/tree-check.js CHANGED
@@ -5,8 +5,9 @@ const checkTree = (tree, checkUnreachable = true) => {
5
5
 
6
6
  // this can only happen in tests where we have a "tree" object
7
7
  // that isn't actually a tree.
8
- if (!tree.root || !tree.root.inventory)
8
+ if (!tree.root || !tree.root.inventory) {
9
9
  return tree
10
+ }
10
11
 
11
12
  const { inventory } = tree.root
12
13
  const seen = new Set()
@@ -21,8 +22,9 @@ const checkTree = (tree, checkUnreachable = true) => {
21
22
  'root=' + !!(node && node.isRoot),
22
23
  ])
23
24
 
24
- if (!node || seen.has(node) || node.then)
25
+ if (!node || seen.has(node) || node.then) {
25
26
  return
27
+ }
26
28
 
27
29
  seen.add(node)
28
30
 
@@ -116,14 +118,18 @@ const checkTree = (tree, checkUnreachable = true) => {
116
118
  check(fsParent, node, 'fsParent')
117
119
  check(target, node, 'target')
118
120
  log.push(['CHILDREN', node.location, ...node.children.keys()])
119
- for (const kid of node.children.values())
121
+ for (const kid of node.children.values()) {
120
122
  check(kid, node, 'children')
121
- for (const kid of node.fsChildren)
123
+ }
124
+ for (const kid of node.fsChildren) {
122
125
  check(kid, node, 'fsChildren')
123
- for (const link of node.linksIn)
126
+ }
127
+ for (const link of node.linksIn) {
124
128
  check(link, node, 'linksIn')
125
- for (const top of node.tops)
129
+ }
130
+ for (const top of node.tops) {
126
131
  check(top, node, 'tops')
132
+ }
127
133
  log.push(['DONE', node.location])
128
134
  }
129
135
  check(tree)
@@ -4,8 +4,9 @@ const {basename} = require('path')
4
4
  const {parse} = require('url')
5
5
  module.exports = (name, tgz) => {
6
6
  const base = basename(tgz)
7
- if (!base.endsWith('.tgz'))
7
+ if (!base.endsWith('.tgz')) {
8
8
  return null
9
+ }
9
10
 
10
11
  const u = parse(tgz)
11
12
  if (/^https?:/.test(u.protocol)) {
@@ -35,8 +36,9 @@ module.exports = (name, tgz) => {
35
36
  }
36
37
 
37
38
  const versionFromBaseScopeName = (base, scope, name) => {
38
- if (!base.startsWith(name + '-'))
39
+ if (!base.startsWith(name + '-')) {
39
40
  return null
41
+ }
40
42
 
41
43
  const parsed = semver.parse(base.substring(name.length + 1, base.length - 4))
42
44
  return parsed ? {
package/lib/vuln.js CHANGED
@@ -14,6 +14,7 @@
14
14
  const {satisfies, simplifyRange} = require('semver')
15
15
  const semverOpt = { loose: true, includePrerelease: true }
16
16
 
17
+ const localeCompare = require('@isaacs/string-locale-compare')('en')
17
18
  const npa = require('npm-package-arg')
18
19
  const _range = Symbol('_range')
19
20
  const _simpleRange = Symbol('_simpleRange')
@@ -28,8 +29,9 @@ const severities = new Map([
28
29
  [null, -1],
29
30
  ])
30
31
 
31
- for (const [name, val] of severities.entries())
32
+ for (const [name, val] of severities.entries()) {
32
33
  severities.set(val, name)
34
+ }
33
35
 
34
36
  class Vuln {
35
37
  constructor ({ name, advisory }) {
@@ -43,7 +45,7 @@ class Vuln {
43
45
  this[_simpleRange] = null
44
46
  this.nodes = new Set()
45
47
  // assume a fix is available unless it hits a top node
46
- // that locks it in place, setting this to false or {isSemVerMajor, version}.
48
+ // that locks it in place, setting this false or {isSemVerMajor, version}.
47
49
  this[_fixAvailable] = true
48
50
  this.addAdvisory(advisory)
49
51
  this.packument = advisory.packument
@@ -65,39 +67,55 @@ class Vuln {
65
67
  // - true: fix does not require -f
66
68
  for (const v of this.via) {
67
69
  // don't blow up on loops
68
- if (v.fixAvailable === f)
70
+ if (v.fixAvailable === f) {
69
71
  continue
72
+ }
70
73
 
71
- if (f === false)
74
+ if (f === false) {
72
75
  v.fixAvailable = f
73
- else if (v.fixAvailable === true)
76
+ } else if (v.fixAvailable === true) {
74
77
  v.fixAvailable = f
75
- else if (typeof f === 'object' && (
76
- typeof v.fixAvailable !== 'object' || !v.fixAvailable.isSemVerMajor))
78
+ } else if (typeof f === 'object' && (
79
+ typeof v.fixAvailable !== 'object' || !v.fixAvailable.isSemVerMajor)) {
77
80
  v.fixAvailable = f
81
+ }
78
82
  }
79
83
  }
80
84
 
85
+ get isDirect () {
86
+ for (const node of this.nodes.values()) {
87
+ for (const edge of node.edgesIn) {
88
+ if (edge.from.isProjectRoot || edge.from.isWorkspace) {
89
+ return true
90
+ }
91
+ }
92
+ }
93
+ return false
94
+ }
95
+
81
96
  testSpec (spec) {
82
97
  const specObj = npa(spec)
83
- if (!specObj.registry)
98
+ if (!specObj.registry) {
84
99
  return true
100
+ }
85
101
 
86
- if (specObj.subSpec)
102
+ if (specObj.subSpec) {
87
103
  spec = specObj.subSpec.rawSpec
104
+ }
88
105
 
89
106
  for (const v of this.versions) {
90
- if (satisfies(v, spec) && !satisfies(v, this.range, semverOpt))
107
+ if (satisfies(v, spec) && !satisfies(v, this.range, semverOpt)) {
91
108
  return false
109
+ }
92
110
  }
93
111
  return true
94
112
  }
95
113
 
96
114
  toJSON () {
97
- // sort so that they're always in a consistent order
98
115
  return {
99
116
  name: this.name,
100
117
  severity: this.severity,
118
+ isDirect: this.isDirect,
101
119
  // just loop over the advisories, since via is only Vuln objects,
102
120
  // and calculated advisories have all the info we need
103
121
  via: [...this.advisories].map(v => v.type === 'metavuln' ? v.dependency : {
@@ -106,12 +124,10 @@ class Vuln {
106
124
  vulnerableVersions: undefined,
107
125
  id: undefined,
108
126
  }).sort((a, b) =>
109
- String(a.source || a).localeCompare(String(b.source || b, 'en'))),
110
- effects: [...this.effects].map(v => v.name)
111
- .sort(/* istanbul ignore next */(a, b) => a.localeCompare(b, 'en')),
127
+ localeCompare(String(a.source || a), String(b.source || b))),
128
+ effects: [...this.effects].map(v => v.name).sort(localeCompare),
112
129
  range: this.simpleRange,
113
- nodes: [...this.nodes].map(n => n.location)
114
- .sort(/* istanbul ignore next */(a, b) => a.localeCompare(b, 'en')),
130
+ nodes: [...this.nodes].map(n => n.location).sort(localeCompare),
115
131
  fixAvailable: this[_fixAvailable],
116
132
  }
117
133
  }
@@ -135,14 +151,16 @@ class Vuln {
135
151
  this[_range] = null
136
152
  this[_simpleRange] = null
137
153
  // refresh severity
138
- for (const advisory of this.advisories)
154
+ for (const advisory of this.advisories) {
139
155
  this.addAdvisory(advisory)
156
+ }
140
157
 
141
158
  // remove any effects that are no longer relevant
142
159
  const vias = new Set([...this.advisories].map(a => a.dependency))
143
160
  for (const via of this.via) {
144
- if (!vias.has(via.name))
161
+ if (!vias.has(via.name)) {
145
162
  this.deleteVia(via)
163
+ }
146
164
  }
147
165
  }
148
166
 
@@ -151,8 +169,9 @@ class Vuln {
151
169
  const sev = severities.get(advisory.severity)
152
170
  this[_range] = null
153
171
  this[_simpleRange] = null
154
- if (sev > severities.get(this.severity))
172
+ if (sev > severities.get(this.severity)) {
155
173
  this.severity = advisory.severity
174
+ }
156
175
  }
157
176
 
158
177
  get range () {
@@ -161,8 +180,9 @@ class Vuln {
161
180
  }
162
181
 
163
182
  get simpleRange () {
164
- if (this[_simpleRange] && this[_simpleRange] === this[_range])
183
+ if (this[_simpleRange] && this[_simpleRange] === this[_range]) {
165
184
  return this[_simpleRange]
185
+ }
166
186
 
167
187
  const versions = [...this.advisories][0].versions
168
188
  const range = this.range
@@ -171,12 +191,14 @@ class Vuln {
171
191
  }
172
192
 
173
193
  isVulnerable (node) {
174
- if (this.nodes.has(node))
194
+ if (this.nodes.has(node)) {
175
195
  return true
196
+ }
176
197
 
177
198
  const { version } = node.package
178
- if (!version)
199
+ if (!version) {
179
200
  return false
201
+ }
180
202
 
181
203
  for (const v of this.advisories) {
182
204
  if (v.testVersion(version)) {
package/lib/yarn-lock.js CHANGED
@@ -28,13 +28,14 @@
28
28
  // is an impenetrable 10kloc of webpack flow output, which is overkill
29
29
  // for something relatively simple and tailored to Arborist's use case.
30
30
 
31
+ const localeCompare = require('@isaacs/string-locale-compare')('en')
31
32
  const consistentResolve = require('./consistent-resolve.js')
32
33
  const {dirname} = require('path')
33
34
  const {breadth} = require('treeverse')
34
35
 
35
36
  // sort a key/value object into a string of JSON stringified keys and vals
36
37
  const sortKV = obj => Object.keys(obj)
37
- .sort((a, b) => a.localeCompare(b, 'en'))
38
+ .sort(localeCompare)
38
39
  .map(k => ` ${JSON.stringify(k)} ${JSON.stringify(obj[k])}`)
39
40
  .join('\n')
40
41
 
@@ -82,13 +83,15 @@ class YarnLock {
82
83
  const linere = /([^\r\n]*)\r?\n/gm
83
84
  let match
84
85
  let lineNum = 0
85
- if (!/\n$/.test(data))
86
+ if (!/\n$/.test(data)) {
86
87
  data += '\n'
88
+ }
87
89
  while (match = linere.exec(data)) {
88
90
  const line = match[1]
89
91
  lineNum++
90
- if (line.charAt(0) === '#')
92
+ if (line.charAt(0) === '#') {
91
93
  continue
94
+ }
92
95
  if (line === '') {
93
96
  this.endCurrent()
94
97
  continue
@@ -117,8 +120,9 @@ class YarnLock {
117
120
  const metadata = this.splitQuoted(line.trimLeft(), ' ')
118
121
  if (metadata.length === 2) {
119
122
  // strip off the legacy shasum hashes
120
- if (metadata[0] === 'resolved')
123
+ if (metadata[0] === 'resolved') {
121
124
  metadata[1] = metadata[1].replace(/#.*/, '')
125
+ }
122
126
  this.current[metadata[0]] = metadata[1]
123
127
  continue
124
128
  }
@@ -141,9 +145,9 @@ class YarnLock {
141
145
  let o = 0
142
146
  for (let i = 0; i < split.length; i++) {
143
147
  const chunk = split[i]
144
- if (/^".*"$/.test(chunk))
148
+ if (/^".*"$/.test(chunk)) {
145
149
  out[o++] = chunk.trim().slice(1, -1)
146
- else if (/^"/.test(chunk)) {
150
+ } else if (/^"/.test(chunk)) {
147
151
  let collect = chunk.trimLeft().slice(1)
148
152
  while (++i < split.length) {
149
153
  const n = split[i]
@@ -152,12 +156,14 @@ class YarnLock {
152
156
  if (/[^\\](\\\\)*"$/.test(n)) {
153
157
  collect += n.trimRight().slice(0, -1)
154
158
  break
155
- } else
159
+ } else {
156
160
  collect += n
161
+ }
157
162
  }
158
163
  out[o++] = collect
159
- } else
164
+ } else {
160
165
  out[o++] = chunk.trim()
166
+ }
161
167
  }
162
168
  return out
163
169
  }
@@ -165,7 +171,7 @@ class YarnLock {
165
171
  toString () {
166
172
  return prefix + [...new Set([...this.entries.values()])]
167
173
  .map(e => e.toString())
168
- .sort((a, b) => a.localeCompare(b, 'en')).join('\n\n') + '\n'
174
+ .sort(localeCompare).join('\n\n') + '\n'
169
175
  }
170
176
 
171
177
  fromTree (tree) {
@@ -175,7 +181,7 @@ class YarnLock {
175
181
  tree,
176
182
  visit: node => this.addEntryFromNode(node),
177
183
  getChildren: node => [...node.children.values(), ...node.fsChildren]
178
- .sort((a, b) => a.depth - b.depth || a.name.localeCompare(b.name, 'en')),
184
+ .sort((a, b) => a.depth - b.depth || localeCompare(a.name, b.name)),
179
185
  })
180
186
  return this
181
187
  }
@@ -183,7 +189,7 @@ class YarnLock {
183
189
  addEntryFromNode (node) {
184
190
  const specs = [...node.edgesIn]
185
191
  .map(e => `${node.name}@${e.spec}`)
186
- .sort((a, b) => a.localeCompare(b, 'en'))
192
+ .sort(localeCompare)
187
193
 
188
194
  // Note:
189
195
  // yarn will do excessive duplication in a case like this:
@@ -226,17 +232,19 @@ class YarnLock {
226
232
  // no previous entry for this spec at all, so it's new
227
233
  if (!prev) {
228
234
  // if we saw a match already, then assign this spec to it as well
229
- if (priorEntry)
235
+ if (priorEntry) {
230
236
  priorEntry.addSpec(s)
231
- else
237
+ } else {
232
238
  newSpecs.push(s)
239
+ }
233
240
  continue
234
241
  }
235
242
 
236
243
  const m = match(prev, n)
237
244
  // there was a prior entry, but a different thing. skip this one
238
- if (!m)
245
+ if (!m) {
239
246
  continue
247
+ }
240
248
 
241
249
  // previous matches, but first time seeing it, so already has this spec.
242
250
  // go ahead and add all the previously unseen specs, though
@@ -259,8 +267,9 @@ class YarnLock {
259
267
  // if we never found a matching prior, then this is a whole new thing
260
268
  if (!priorEntry) {
261
269
  const entry = Object.assign(new YarnLockEntry(newSpecs), n)
262
- for (const s of newSpecs)
270
+ for (const s of newSpecs) {
263
271
  this.entries.set(s, entry)
272
+ }
264
273
  } else {
265
274
  // pick up any new info that we got for this node, so that we can
266
275
  // decorate with integrity/resolved/etc.
@@ -270,12 +279,15 @@ class YarnLock {
270
279
 
271
280
  entryDataFromNode (node) {
272
281
  const n = {}
273
- if (node.package.dependencies)
282
+ if (node.package.dependencies) {
274
283
  n.dependencies = node.package.dependencies
275
- if (node.package.optionalDependencies)
284
+ }
285
+ if (node.package.optionalDependencies) {
276
286
  n.optionalDependencies = node.package.optionalDependencies
277
- if (node.version)
287
+ }
288
+ if (node.version) {
278
289
  n.version = node.version
290
+ }
279
291
  if (node.resolved) {
280
292
  n.resolved = consistentResolve(
281
293
  node.resolved,
@@ -284,8 +296,9 @@ class YarnLock {
284
296
  true
285
297
  )
286
298
  }
287
- if (node.integrity)
299
+ if (node.integrity) {
288
300
  n.integrity = node.integrity
301
+ }
289
302
 
290
303
  return n
291
304
  }
@@ -309,7 +322,7 @@ class YarnLockEntry {
309
322
  toString () {
310
323
  // sort objects to the bottom, then alphabetical
311
324
  return ([...this[_specs]]
312
- .sort((a, b) => a.localeCompare(b, 'en'))
325
+ .sort(localeCompare)
313
326
  .map(JSON.stringify).join(', ') +
314
327
  ':\n' +
315
328
  Object.getOwnPropertyNames(this)
@@ -318,7 +331,7 @@ class YarnLockEntry {
318
331
  (a, b) =>
319
332
  /* istanbul ignore next - sort call order is unpredictable */
320
333
  (typeof this[a] === 'object') === (typeof this[b] === 'object')
321
- ? a.localeCompare(b, 'en')
334
+ ? localeCompare(a, b)
322
335
  : typeof this[a] === 'object' ? 1 : -1)
323
336
  .map(prop =>
324
337
  typeof this[prop] !== 'object'
package/package.json CHANGED
@@ -1,8 +1,9 @@
1
1
  {
2
2
  "name": "@npmcli/arborist",
3
- "version": "2.8.2",
3
+ "version": "2.9.0",
4
4
  "description": "Manage node_modules trees",
5
5
  "dependencies": {
6
+ "@isaacs/string-locale-compare": "^1.0.1",
6
7
  "@npmcli/installed-package-contents": "^1.0.7",
7
8
  "@npmcli/map-workspaces": "^1.0.2",
8
9
  "@npmcli/metavuln-calculator": "^1.1.0",
@@ -36,13 +37,9 @@
36
37
  "walk-up-path": "^1.0.0"
37
38
  },
38
39
  "devDependencies": {
40
+ "@npmcli/lint": "^1.0.2",
39
41
  "benchmark": "^2.1.4",
40
42
  "chalk": "^4.1.0",
41
- "eslint": "^7.9.0",
42
- "eslint-plugin-import": "^2.22.0",
43
- "eslint-plugin-node": "^11.1.0",
44
- "eslint-plugin-promise": "^4.2.1",
45
- "eslint-plugin-standard": "^4.0.1",
46
43
  "minify-registry-metadata": "^2.1.0",
47
44
  "tap": "^15.0.9",
48
45
  "tcompare": "^5.0.6"
@@ -50,18 +47,19 @@
50
47
  "scripts": {
51
48
  "test": "npm run test-only --",
52
49
  "test-only": "tap",
53
- "posttest": "npm run lint",
50
+ "posttest": "npm run lint --",
54
51
  "snap": "tap",
55
- "postsnap": "npm run lintfix",
52
+ "postsnap": "npm run lintfix --",
56
53
  "test-proxy": "ARBORIST_TEST_PROXY=1 tap --snapshot",
57
54
  "preversion": "npm test",
58
55
  "postversion": "npm publish",
59
56
  "prepublishOnly": "git push origin --follow-tags",
60
57
  "eslint": "eslint",
61
- "lint": "npm run eslint -- \"lib/**/*.js\" \"test/arborist/*.js\" \"test/*.js\" \"bin/**/*.js\"",
58
+ "lint": "npm run npmclilint -- \"lib/**/*.*js\" \"bin/**/*.*js\" \"test/*.*js\" \"test/arborist/*.*js\"",
62
59
  "lintfix": "npm run lint -- --fix",
63
60
  "benchmark": "node scripts/benchmark.js",
64
- "benchclean": "rm -rf scripts/benchmark/*/"
61
+ "benchclean": "rm -rf scripts/benchmark/*/",
62
+ "npmclilint": "npmcli-lint"
65
63
  },
66
64
  "repository": {
67
65
  "type": "git",