@npmcli/arborist 5.5.0 → 6.0.0-pre.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.
package/bin/index.js CHANGED
@@ -99,6 +99,7 @@ for (const file of commandFiles) {
99
99
  if (bin.loglevel !== 'silent') {
100
100
  console[process.exitCode ? 'error' : 'log'](r)
101
101
  }
102
+ return r
102
103
  })
103
104
  }
104
105
  }
@@ -4,8 +4,67 @@ 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 }) => {
7
- for (const spec of add) {
8
- addSingle({ pkg, spec, saveBundle, saveType })
7
+ for (const { name, rawSpec } of add) {
8
+ // if the user does not give us a type, we infer which type(s)
9
+ // to keep based on the same order of priority we do when
10
+ // building the tree as defined in the _loadDeps method of
11
+ // the node class.
12
+ if (!saveType) {
13
+ saveType = inferSaveType(pkg, name)
14
+ }
15
+
16
+ if (saveType === 'prod') {
17
+ // a production dependency can only exist as production (rpj ensures it
18
+ // doesn't coexist w/ optional)
19
+ deleteSubKey(pkg, 'devDependencies', name, 'dependencies')
20
+ deleteSubKey(pkg, 'peerDependencies', name, 'dependencies')
21
+ } else if (saveType === 'dev') {
22
+ // a dev dependency may co-exist as peer, or optional, but not production
23
+ deleteSubKey(pkg, 'dependencies', name, 'devDependencies')
24
+ } else if (saveType === 'optional') {
25
+ // an optional dependency may co-exist as dev (rpj ensures it doesn't
26
+ // coexist w/ prod)
27
+ deleteSubKey(pkg, 'peerDependencies', name, 'optionalDependencies')
28
+ } else { // peer or peerOptional is all that's left
29
+ // a peer dependency may coexist as dev
30
+ deleteSubKey(pkg, 'dependencies', name, 'peerDependencies')
31
+ deleteSubKey(pkg, 'optionalDependencies', name, 'peerDependencies')
32
+ }
33
+
34
+ const depType = saveTypeMap.get(saveType)
35
+
36
+ pkg[depType] = pkg[depType] || {}
37
+ if (rawSpec !== '' || pkg[depType][name] === undefined) {
38
+ pkg[depType][name] = rawSpec || '*'
39
+ }
40
+ if (saveType === 'optional') {
41
+ // Affordance for previous npm versions that require this behaviour
42
+ pkg.dependencies = pkg.dependencies || {}
43
+ pkg.dependencies[name] = pkg.optionalDependencies[name]
44
+ }
45
+
46
+ if (saveType === 'peer' || saveType === 'peerOptional') {
47
+ const pdm = pkg.peerDependenciesMeta || {}
48
+ if (saveType === 'peer' && pdm[name] && pdm[name].optional) {
49
+ pdm[name].optional = false
50
+ } else if (saveType === 'peerOptional') {
51
+ pdm[name] = pdm[name] || {}
52
+ pdm[name].optional = true
53
+ pkg.peerDependenciesMeta = pdm
54
+ }
55
+ // peerDeps are often also a devDep, so that they can be tested when
56
+ // using package managers that don't auto-install peer deps
57
+ if (pkg.devDependencies && pkg.devDependencies[name] !== undefined) {
58
+ pkg.devDependencies[name] = pkg.peerDependencies[name]
59
+ }
60
+ }
61
+
62
+ if (saveBundle && saveType !== 'peer' && saveType !== 'peerOptional') {
63
+ // keep it sorted, keep it unique
64
+ const bd = new Set(pkg.bundleDependencies || [])
65
+ bd.add(name)
66
+ pkg.bundleDependencies = [...bd].sort(localeCompare)
67
+ }
9
68
  }
10
69
 
11
70
  return pkg
@@ -21,71 +80,6 @@ const saveTypeMap = new Map([
21
80
  ['peer', 'peerDependencies'],
22
81
  ])
23
82
 
24
- const addSingle = ({ pkg, spec, saveBundle, saveType }) => {
25
- const { name, rawSpec } = spec
26
-
27
- // if the user does not give us a type, we infer which type(s)
28
- // to keep based on the same order of priority we do when
29
- // building the tree as defined in the _loadDeps method of
30
- // the node class.
31
- if (!saveType) {
32
- saveType = inferSaveType(pkg, spec.name)
33
- }
34
-
35
- if (saveType === 'prod') {
36
- // a production dependency can only exist as production (rpj ensures it
37
- // doesn't coexist w/ optional)
38
- deleteSubKey(pkg, 'devDependencies', name, 'dependencies')
39
- deleteSubKey(pkg, 'peerDependencies', name, 'dependencies')
40
- } else if (saveType === 'dev') {
41
- // a dev dependency may co-exist as peer, or optional, but not production
42
- deleteSubKey(pkg, 'dependencies', name, 'devDependencies')
43
- } else if (saveType === 'optional') {
44
- // an optional dependency may co-exist as dev (rpj ensures it doesn't
45
- // coexist w/ prod)
46
- deleteSubKey(pkg, 'peerDependencies', name, 'optionalDependencies')
47
- } else { // peer or peerOptional is all that's left
48
- // a peer dependency may coexist as dev
49
- deleteSubKey(pkg, 'dependencies', name, 'peerDependencies')
50
- deleteSubKey(pkg, 'optionalDependencies', name, 'peerDependencies')
51
- }
52
-
53
- const depType = saveTypeMap.get(saveType)
54
-
55
- pkg[depType] = pkg[depType] || {}
56
- if (rawSpec !== '' || pkg[depType][name] === undefined) {
57
- pkg[depType][name] = rawSpec || '*'
58
- }
59
- if (saveType === 'optional') {
60
- // Affordance for previous npm versions that require this behaviour
61
- pkg.dependencies = pkg.dependencies || {}
62
- pkg.dependencies[name] = pkg.optionalDependencies[name]
63
- }
64
-
65
- if (saveType === 'peer' || saveType === 'peerOptional') {
66
- const pdm = pkg.peerDependenciesMeta || {}
67
- if (saveType === 'peer' && pdm[name] && pdm[name].optional) {
68
- pdm[name].optional = false
69
- } else if (saveType === 'peerOptional') {
70
- pdm[name] = pdm[name] || {}
71
- pdm[name].optional = true
72
- pkg.peerDependenciesMeta = pdm
73
- }
74
- // peerDeps are often also a devDep, so that they can be tested when
75
- // using package managers that don't auto-install peer deps
76
- if (pkg.devDependencies && pkg.devDependencies[name] !== undefined) {
77
- pkg.devDependencies[name] = pkg.peerDependencies[name]
78
- }
79
- }
80
-
81
- if (saveBundle && saveType !== 'peer' && saveType !== 'peerOptional') {
82
- // keep it sorted, keep it unique
83
- const bd = new Set(pkg.bundleDependencies || [])
84
- bd.add(spec.name)
85
- pkg.bundleDependencies = [...bd].sort(localeCompare)
86
- }
87
- }
88
-
89
83
  // Finds where the package is already in the spec and infers saveType from that
90
84
  const inferSaveType = (pkg, name) => {
91
85
  for (const saveType of saveTypeMap.keys()) {
@@ -103,9 +97,8 @@ const inferSaveType = (pkg, name) => {
103
97
  return 'prod'
104
98
  }
105
99
 
106
- const { hasOwnProperty } = Object.prototype
107
100
  const hasSubKey = (pkg, depType, name) => {
108
- return pkg[depType] && hasOwnProperty.call(pkg[depType], name)
101
+ return pkg[depType] && Object.prototype.hasOwnProperty.call(pkg[depType], name)
109
102
  }
110
103
 
111
104
  // Removes a subkey and warns about it if it's being replaced
@@ -81,18 +81,11 @@ const _linkNodes = Symbol('linkNodes')
81
81
  const _follow = Symbol('follow')
82
82
  const _globalStyle = Symbol('globalStyle')
83
83
  const _globalRootNode = Symbol('globalRootNode')
84
- const _isVulnerable = Symbol.for('isVulnerable')
85
84
  const _usePackageLock = Symbol.for('usePackageLock')
86
85
  const _rpcache = Symbol.for('realpathCache')
87
86
  const _stcache = Symbol.for('statCache')
88
- const _updateFilePath = Symbol('updateFilePath')
89
- const _followSymlinkPath = Symbol('followSymlinkPath')
90
- const _getRelpathSpec = Symbol('getRelpathSpec')
91
- const _retrieveSpecName = Symbol('retrieveSpecName')
92
87
  const _strictPeerDeps = Symbol('strictPeerDeps')
93
88
  const _checkEngineAndPlatform = Symbol('checkEngineAndPlatform')
94
- const _checkEngine = Symbol('checkEngine')
95
- const _checkPlatform = Symbol('checkPlatform')
96
89
  const _virtualRoots = Symbol('virtualRoots')
97
90
  const _virtualRoot = Symbol('virtualRoot')
98
91
  const _includeWorkspaceRoot = Symbol.for('includeWorkspaceRoot')
@@ -228,34 +221,22 @@ module.exports = cls => class IdealTreeBuilder extends cls {
228
221
  }
229
222
 
230
223
  async [_checkEngineAndPlatform] () {
224
+ const { engineStrict, npmVersion, nodeVersion } = this.options
231
225
  for (const node of this.idealTree.inventory.values()) {
232
226
  if (!node.optional) {
233
- this[_checkEngine](node)
234
- this[_checkPlatform](node)
235
- }
236
- }
237
- }
238
-
239
- [_checkPlatform] (node) {
240
- checkPlatform(node.package, this[_force])
241
- }
242
-
243
- [_checkEngine] (node) {
244
- const { engineStrict, npmVersion, nodeVersion } = this.options
245
- const c = () =>
246
- checkEngine(node.package, npmVersion, nodeVersion, this[_force])
247
-
248
- if (engineStrict) {
249
- c()
250
- } else {
251
- try {
252
- c()
253
- } catch (er) {
254
- log.warn(er.code, er.message, {
255
- package: er.pkgid,
256
- required: er.required,
257
- current: er.current,
258
- })
227
+ try {
228
+ checkEngine(node.package, npmVersion, nodeVersion, this[_force])
229
+ } catch (err) {
230
+ if (engineStrict) {
231
+ throw err
232
+ }
233
+ log.warn(err.code, err.message, {
234
+ package: err.pkgid,
235
+ required: err.required,
236
+ current: err.current,
237
+ })
238
+ }
239
+ checkPlatform(node.package, this[_force])
259
240
  }
260
241
  }
261
242
  }
@@ -378,6 +359,7 @@ Try using the package name instead, e.g:
378
359
  this.idealTree = tree
379
360
  this.virtualTree = null
380
361
  process.emit('timeEnd', 'idealTree:init')
362
+ return tree
381
363
  })
382
364
  }
383
365
 
@@ -529,84 +511,59 @@ Try using the package name instead, e.g:
529
511
  this[_depsQueue].push(tree)
530
512
  }
531
513
 
532
- // This returns a promise because we might not have the name yet,
533
- // and need to call pacote.manifest to find the name.
534
- [_add] (tree, { add, saveType = null, saveBundle = false }) {
535
- // get the name for each of the specs in the list.
536
- // ie, doing `foo@bar` we just return foo
537
- // but if it's a url or git, we don't know the name until we
538
- // fetch it and look in its manifest.
539
- return Promise.all(add.map(async rawSpec => {
540
- // We do NOT provide the path to npa here, because user-additions
541
- // need to be resolved relative to the CWD the user is in.
542
- const spec = await this[_retrieveSpecName](npa(rawSpec))
543
- .then(spec => this[_updateFilePath](spec))
544
- .then(spec => this[_followSymlinkPath](spec))
545
- spec.tree = tree
546
- return spec
547
- })).then(add => {
548
- this[_resolvedAdd].push(...add)
549
- // now add is a list of spec objects with names.
550
- // find a home for each of them!
551
- addRmPkgDeps.add({
552
- pkg: tree.package,
553
- add,
554
- saveBundle,
555
- saveType,
556
- path: this.path,
557
- })
558
- })
559
- }
560
-
561
- async [_retrieveSpecName] (spec) {
562
- // if it's just @'' then we reload whatever's there, or get latest
563
- // if it's an explicit tag, we need to install that specific tag version
564
- const isTag = spec.rawSpec && spec.type === 'tag'
565
-
566
- if (spec.name && !isTag) {
567
- return spec
568
- }
569
-
570
- const mani = await pacote.manifest(spec, { ...this.options })
571
- // if it's a tag type, then we need to run it down to an actual version
572
- if (isTag) {
573
- return npa(`${mani.name}@${mani.version}`)
574
- }
575
-
576
- spec.name = mani.name
577
- return spec
578
- }
579
-
580
- async [_updateFilePath] (spec) {
581
- if (spec.type === 'file') {
582
- return this[_getRelpathSpec](spec, spec.fetchSpec)
583
- }
584
-
585
- return spec
586
- }
587
-
588
- async [_followSymlinkPath] (spec) {
589
- if (spec.type === 'directory') {
590
- const real = await (
591
- realpath(spec.fetchSpec, this[_rpcache], this[_stcache])
592
- // TODO: create synthetic test case to simulate realpath failure
593
- .catch(/* istanbul ignore next */() => null)
594
- )
514
+ // This returns a promise because we might not have the name yet, and need to
515
+ // call pacote.manifest to find the name.
516
+ async [_add] (tree, { add, saveType = null, saveBundle = false }) {
517
+ // If we have a link it will need to be added relative to the target's path
518
+ const path = tree.target.path
595
519
 
596
- return this[_getRelpathSpec](spec, real)
597
- }
598
- return spec
599
- }
520
+ // get the name for each of the specs in the list.
521
+ // ie, doing `foo@bar` we just return foo but if it's a url or git, we
522
+ // don't know the name until we fetch it and look in its manifest.
523
+ await Promise.all(add.map(async rawSpec => {
524
+ // We do NOT provide the path to npa here, because user-additions need to
525
+ // be resolved relative to the tree being added to.
526
+ let spec = npa(rawSpec)
527
+
528
+ // if it's just @'' then we reload whatever's there, or get latest
529
+ // if it's an explicit tag, we need to install that specific tag version
530
+ const isTag = spec.rawSpec && spec.type === 'tag'
531
+
532
+ // look up the names of file/directory/git specs
533
+ if (!spec.name || isTag) {
534
+ const mani = await pacote.manifest(spec, { ...this.options })
535
+ if (isTag) {
536
+ // translate tag to a version
537
+ spec = npa(`${mani.name}@${mani.version}`)
538
+ }
539
+ spec.name = mani.name
540
+ }
600
541
 
601
- [_getRelpathSpec] (spec, filepath) {
602
- /* istanbul ignore else - should also be covered by realpath failure */
603
- if (filepath) {
604
542
  const { name } = spec
605
- const tree = this.idealTree.target
606
- spec = npa(`file:${relpath(tree.path, filepath).replace(/#/g, '%23')}`, tree.path)
607
- spec.name = name
608
- }
609
- return spec
543
+ if (spec.type === 'file') {
544
+ spec = npa(`file:${relpath(path, spec.fetchSpec).replace(/#/g, '%23')}`, path)
545
+ spec.name = name
546
+ } else if (spec.type === 'directory') {
547
+ try {
548
+ const real = await realpath(spec.fetchSpec, this[_rpcache], this[_stcache])
549
+ spec = npa(`file:${relpath(path, real).replace(/#/g, '%23')}`, path)
550
+ spec.name = name
551
+ } catch {
552
+ // TODO: create synthetic test case to simulate realpath failure
553
+ }
554
+ }
555
+ spec.tree = tree
556
+ this[_resolvedAdd].push(spec)
557
+ }))
558
+
559
+ // now this._resolvedAdd is a list of spec objects with names.
560
+ // find a home for each of them!
561
+ addRmPkgDeps.add({
562
+ pkg: tree.package,
563
+ add: this[_resolvedAdd],
564
+ saveBundle,
565
+ saveType,
566
+ })
610
567
  }
611
568
 
612
569
  // TODO: provide a way to fix bundled deps by exposing metadata about
@@ -686,10 +643,6 @@ Try using the package name instead, e.g:
686
643
  }
687
644
  }
688
645
 
689
- [_isVulnerable] (node) {
690
- return this.auditReport && this.auditReport.isVulnerable(node)
691
- }
692
-
693
646
  [_avoidRange] (name) {
694
647
  if (!this.auditReport) {
695
648
  return null
@@ -781,17 +734,18 @@ This is a one-time fix-up, please be patient...
781
734
  const spec = npa.resolve(name, id, dirname(path))
782
735
  const t = `idealTree:inflate:${location}`
783
736
  this.addTracker(t)
784
- await pacote.manifest(spec, {
785
- ...this.options,
786
- resolved: resolved,
787
- integrity: integrity,
788
- fullMetadata: false,
789
- }).then(mani => {
737
+ try {
738
+ const mani = await pacote.manifest(spec, {
739
+ ...this.options,
740
+ resolved: resolved,
741
+ integrity: integrity,
742
+ fullMetadata: false,
743
+ })
790
744
  node.package = { ...mani, _id: `${mani.name}@${mani.version}` }
791
- }).catch((er) => {
745
+ } catch (er) {
792
746
  const warning = `Could not fetch metadata for ${name}@${id}`
793
747
  log.warn(heading, warning, er)
794
- })
748
+ }
795
749
  this.finishTracker(t)
796
750
  })
797
751
  }
@@ -1233,7 +1187,7 @@ This is a one-time fix-up, please be patient...
1233
1187
  }
1234
1188
 
1235
1189
  // fixing a security vulnerability with this package, problem
1236
- if (this[_isVulnerable](edge.to)) {
1190
+ if (this.auditReport && this.auditReport.isVulnerable(edge.to)) {
1237
1191
  return true
1238
1192
  }
1239
1193
 
@@ -134,7 +134,7 @@ class Arborist extends Base {
134
134
  return wsDepSet
135
135
  }
136
136
 
137
- // returns a set of root dependencies, excluding depdencies that are
137
+ // returns a set of root dependencies, excluding dependencies that are
138
138
  // exclusively workspace dependencies
139
139
  excludeWorkspacesDependencySet (tree) {
140
140
  const rootDepSet = new Set()