@npmcli/arborist 6.2.7 → 6.2.9

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/lib/edge.js CHANGED
@@ -4,194 +4,210 @@
4
4
  const util = require('util')
5
5
  const npa = require('npm-package-arg')
6
6
  const depValid = require('./dep-valid.js')
7
- const _from = Symbol('_from')
8
- const _to = Symbol('_to')
9
- const _type = Symbol('_type')
10
- const _spec = Symbol('_spec')
11
- const _accept = Symbol('_accept')
12
- const _name = Symbol('_name')
13
- const _error = Symbol('_error')
14
- const _loadError = Symbol('_loadError')
15
- const _setFrom = Symbol('_setFrom')
16
- const _explain = Symbol('_explain')
17
- const _explanation = Symbol('_explanation')
18
-
19
- const types = new Set([
20
- 'prod',
21
- 'dev',
22
- 'optional',
23
- 'peer',
24
- 'peerOptional',
25
- 'workspace',
26
- ])
27
-
28
- class ArboristEdge {}
29
- const printableEdge = (edge) => {
30
- const edgeFrom = edge.from && edge.from.location
31
- const edgeTo = edge.to && edge.to.location
32
- const override = edge.overrides && edge.overrides.value
33
-
34
- return Object.assign(new ArboristEdge(), {
35
- name: edge.name,
36
- spec: edge.spec,
37
- type: edge.type,
38
- ...(edgeFrom != null ? { from: edgeFrom } : {}),
39
- ...(edgeTo ? { to: edgeTo } : {}),
40
- ...(edge.error ? { error: edge.error } : {}),
41
- ...(edge.peerConflicted ? { peerConflicted: true } : {}),
42
- ...(override ? { overridden: override } : {}),
43
- })
7
+
8
+ class ArboristEdge {
9
+ constructor (edge) {
10
+ this.name = edge.name
11
+ this.spec = edge.spec
12
+ this.type = edge.type
13
+
14
+ const edgeFrom = edge.from?.location
15
+ const edgeTo = edge.to?.location
16
+ const override = edge.overrides?.value
17
+
18
+ if (edgeFrom != null) {
19
+ this.from = edgeFrom
20
+ }
21
+ if (edgeTo) {
22
+ this.to = edgeTo
23
+ }
24
+ if (edge.error) {
25
+ this.error = edge.error
26
+ }
27
+ if (edge.peerConflicted) {
28
+ this.peerConflicted = true
29
+ }
30
+ if (override) {
31
+ this.overridden = override
32
+ }
33
+ }
44
34
  }
45
35
 
46
36
  class Edge {
37
+ #accept
38
+ #error
39
+ #explanation
40
+ #from
41
+ #name
42
+ #spec
43
+ #to
44
+ #type
45
+
46
+ static types = Object.freeze([
47
+ 'prod',
48
+ 'dev',
49
+ 'optional',
50
+ 'peer',
51
+ 'peerOptional',
52
+ 'workspace',
53
+ ])
54
+
55
+ // XXX where is this used?
56
+ static errors = Object.freeze([
57
+ 'DETACHED',
58
+ 'MISSING',
59
+ 'PEER LOCAL',
60
+ 'INVALID',
61
+ ])
62
+
47
63
  constructor (options) {
48
64
  const { type, name, spec, accept, from, overrides } = options
49
65
 
66
+ // XXX are all of these error states even possible?
50
67
  if (typeof spec !== 'string') {
51
68
  throw new TypeError('must provide string spec')
52
69
  }
53
-
70
+ if (!Edge.types.includes(type)) {
71
+ throw new TypeError(`invalid type: ${type}\n(valid types are: ${Edge.types.join(', ')})`)
72
+ }
54
73
  if (type === 'workspace' && npa(spec).type !== 'directory') {
55
74
  throw new TypeError('workspace edges must be a symlink')
56
75
  }
57
-
58
- this[_spec] = spec
59
-
60
- if (overrides !== undefined) {
61
- this.overrides = overrides
76
+ if (typeof name !== 'string') {
77
+ throw new TypeError('must provide dependency name')
78
+ }
79
+ if (!from) {
80
+ throw new TypeError('must provide "from" node')
62
81
  }
63
-
64
82
  if (accept !== undefined) {
65
83
  if (typeof accept !== 'string') {
66
84
  throw new TypeError('accept field must be a string if provided')
67
85
  }
68
- this[_accept] = accept || '*'
86
+ this.#accept = accept || '*'
69
87
  }
70
-
71
- if (typeof name !== 'string') {
72
- throw new TypeError('must provide dependency name')
88
+ if (overrides !== undefined) {
89
+ this.overrides = overrides
73
90
  }
74
- this[_name] = name
75
91
 
76
- if (!types.has(type)) {
77
- throw new TypeError(
78
- `invalid type: ${type}\n` +
79
- `(valid types are: ${Edge.types.join(', ')})`)
80
- }
81
- this[_type] = type
82
- if (!from) {
83
- throw new TypeError('must provide "from" node')
84
- }
85
- this[_setFrom](from)
86
- this[_error] = this[_loadError]()
92
+ this.#name = name
93
+ this.#type = type
94
+ this.#spec = spec
95
+ this.#explanation = null
96
+ this.#from = from
97
+
98
+ from.edgesOut.get(this.#name)?.detach()
99
+ from.addEdgeOut(this)
100
+
101
+ this.reload(true)
87
102
  this.peerConflicted = false
88
103
  }
89
104
 
90
105
  satisfiedBy (node) {
91
- if (node.name !== this.name) {
106
+ if (node.name !== this.#name) {
92
107
  return false
93
108
  }
94
109
 
95
110
  // NOTE: this condition means we explicitly do not support overriding
96
111
  // bundled or shrinkwrapped dependencies
97
- const spec = (node.hasShrinkwrap || node.inShrinkwrap || node.inBundle)
98
- ? this.rawSpec
99
- : this.spec
100
- return depValid(node, spec, this.accept, this.from)
101
- }
102
-
103
- explain (seen = []) {
104
- if (this[_explanation]) {
105
- return this[_explanation]
112
+ if (node.hasShrinkwrap || node.inShrinkwrap || node.inBundle) {
113
+ return depValid(node, this.rawSpec, this.#accept, this.#from)
106
114
  }
107
-
108
- return this[_explanation] = this[_explain](seen)
115
+ return depValid(node, this.spec, this.#accept, this.#from)
109
116
  }
110
117
 
111
118
  // return the edge data, and an explanation of how that edge came to be here
112
- [_explain] (seen) {
113
- const { error, from, bundled } = this
114
- return {
115
- type: this.type,
116
- name: this.name,
117
- spec: this.spec,
118
- ...(this.rawSpec !== this.spec ? {
119
- rawSpec: this.rawSpec,
120
- overridden: true,
121
- } : {}),
122
- ...(bundled ? { bundled } : {}),
123
- ...(error ? { error } : {}),
124
- ...(from ? { from: from.explain(null, seen) } : {}),
119
+ explain (seen = []) {
120
+ if (!this.#explanation) {
121
+ const explanation = {
122
+ type: this.#type,
123
+ name: this.#name,
124
+ spec: this.spec,
125
+ }
126
+ if (this.rawSpec !== this.spec) {
127
+ explanation.rawSpec = this.rawSpec
128
+ explanation.overridden = true
129
+ }
130
+ if (this.bundled) {
131
+ explanation.bundled = this.bundled
132
+ }
133
+ if (this.error) {
134
+ explanation.error = this.error
135
+ }
136
+ if (this.#from) {
137
+ explanation.from = this.#from.explain(null, seen)
138
+ }
139
+ this.#explanation = explanation
125
140
  }
141
+ return this.#explanation
126
142
  }
127
143
 
128
144
  get bundled () {
129
- if (!this.from) {
130
- return false
131
- }
132
- const { package: { bundleDependencies = [] } } = this.from
133
- return bundleDependencies.includes(this.name)
145
+ return !!this.#from?.package?.bundleDependencies?.includes(this.#name)
134
146
  }
135
147
 
136
148
  get workspace () {
137
- return this[_type] === 'workspace'
149
+ return this.#type === 'workspace'
138
150
  }
139
151
 
140
152
  get prod () {
141
- return this[_type] === 'prod'
153
+ return this.#type === 'prod'
142
154
  }
143
155
 
144
156
  get dev () {
145
- return this[_type] === 'dev'
157
+ return this.#type === 'dev'
146
158
  }
147
159
 
148
160
  get optional () {
149
- return this[_type] === 'optional' || this[_type] === 'peerOptional'
161
+ return this.#type === 'optional' || this.#type === 'peerOptional'
150
162
  }
151
163
 
152
164
  get peer () {
153
- return this[_type] === 'peer' || this[_type] === 'peerOptional'
165
+ return this.#type === 'peer' || this.#type === 'peerOptional'
154
166
  }
155
167
 
156
168
  get type () {
157
- return this[_type]
169
+ return this.#type
158
170
  }
159
171
 
160
172
  get name () {
161
- return this[_name]
173
+ return this.#name
162
174
  }
163
175
 
164
176
  get rawSpec () {
165
- return this[_spec]
177
+ return this.#spec
166
178
  }
167
179
 
168
180
  get spec () {
169
- if (this.overrides?.value && this.overrides.value !== '*' && this.overrides.name === this.name) {
181
+ if (this.overrides?.value && this.overrides.value !== '*' && this.overrides.name === this.#name) {
170
182
  if (this.overrides.value.startsWith('$')) {
171
183
  const ref = this.overrides.value.slice(1)
172
184
  // we may be a virtual root, if we are we want to resolve reference overrides
173
185
  // from the real root, not the virtual one
174
- const pkg = this.from.sourceReference
175
- ? this.from.sourceReference.root.package
176
- : this.from.root.package
177
- const overrideSpec = (pkg.devDependencies && pkg.devDependencies[ref]) ||
178
- (pkg.optionalDependencies && pkg.optionalDependencies[ref]) ||
179
- (pkg.dependencies && pkg.dependencies[ref]) ||
180
- (pkg.peerDependencies && pkg.peerDependencies[ref])
181
-
182
- if (overrideSpec) {
183
- return overrideSpec
186
+ const pkg = this.#from.sourceReference
187
+ ? this.#from.sourceReference.root.package
188
+ : this.#from.root.package
189
+ if (pkg.devDependencies?.[ref]) {
190
+ return pkg.devDependencies[ref]
191
+ }
192
+ if (pkg.optionalDependencies?.[ref]) {
193
+ return pkg.optionalDependencies[ref]
194
+ }
195
+ if (pkg.dependencies?.[ref]) {
196
+ return pkg.dependencies[ref]
197
+ }
198
+ if (pkg.peerDependencies?.[ref]) {
199
+ return pkg.peerDependencies[ref]
184
200
  }
185
201
 
186
202
  throw new Error(`Unable to resolve reference ${this.overrides.value}`)
187
203
  }
188
204
  return this.overrides.value
189
205
  }
190
- return this[_spec]
206
+ return this.#spec
191
207
  }
192
208
 
193
209
  get accept () {
194
- return this[_accept]
210
+ return this.#accept
195
211
  }
196
212
 
197
213
  get valid () {
@@ -211,71 +227,70 @@ class Edge {
211
227
  }
212
228
 
213
229
  get error () {
214
- this[_error] = this[_error] || this[_loadError]()
215
- return this[_error] === 'OK' ? null : this[_error]
216
- }
217
-
218
- [_loadError] () {
219
- return !this[_to] ? (this.optional ? null : 'MISSING')
220
- : this.peer && this.from === this.to.parent && !this.from.isTop ? 'PEER LOCAL'
221
- : !this.satisfiedBy(this.to) ? 'INVALID'
222
- : 'OK'
230
+ if (!this.#error) {
231
+ if (!this.#to) {
232
+ if (this.optional) {
233
+ this.#error = null
234
+ } else {
235
+ this.#error = 'MISSING'
236
+ }
237
+ } else if (this.peer && this.#from === this.#to.parent && !this.#from.isTop) {
238
+ this.#error = 'PEER LOCAL'
239
+ } else if (!this.satisfiedBy(this.#to)) {
240
+ this.#error = 'INVALID'
241
+ } else {
242
+ this.#error = 'OK'
243
+ }
244
+ }
245
+ if (this.#error === 'OK') {
246
+ return null
247
+ }
248
+ return this.#error
223
249
  }
224
250
 
225
251
  reload (hard = false) {
226
- this[_explanation] = null
227
- if (this[_from].overrides) {
228
- this.overrides = this[_from].overrides.getEdgeRule(this)
252
+ this.#explanation = null
253
+ if (this.#from.overrides) {
254
+ this.overrides = this.#from.overrides.getEdgeRule(this)
229
255
  } else {
230
256
  delete this.overrides
231
257
  }
232
- const newTo = this[_from].resolve(this.name)
233
- if (newTo !== this[_to]) {
234
- if (this[_to]) {
235
- this[_to].edgesIn.delete(this)
258
+ const newTo = this.#from.resolve(this.#name)
259
+ if (newTo !== this.#to) {
260
+ if (this.#to) {
261
+ this.#to.edgesIn.delete(this)
236
262
  }
237
- this[_to] = newTo
238
- this[_error] = this[_loadError]()
239
- if (this[_to]) {
240
- this[_to].addEdgeIn(this)
263
+ this.#to = newTo
264
+ this.#error = null
265
+ if (this.#to) {
266
+ this.#to.addEdgeIn(this)
241
267
  }
242
268
  } else if (hard) {
243
- this[_error] = this[_loadError]()
269
+ this.#error = null
244
270
  }
245
271
  }
246
272
 
247
273
  detach () {
248
- this[_explanation] = null
249
- if (this[_to]) {
250
- this[_to].edgesIn.delete(this)
274
+ this.#explanation = null
275
+ if (this.#to) {
276
+ this.#to.edgesIn.delete(this)
251
277
  }
252
- this[_from].edgesOut.delete(this.name)
253
- this[_to] = null
254
- this[_error] = 'DETACHED'
255
- this[_from] = null
256
- }
257
-
258
- [_setFrom] (node) {
259
- this[_explanation] = null
260
- this[_from] = node
261
- if (node.edgesOut.has(this.name)) {
262
- node.edgesOut.get(this.name).detach()
263
- }
264
-
265
- node.addEdgeOut(this)
266
- this.reload()
278
+ this.#from.edgesOut.delete(this.#name)
279
+ this.#to = null
280
+ this.#error = 'DETACHED'
281
+ this.#from = null
267
282
  }
268
283
 
269
284
  get from () {
270
- return this[_from]
285
+ return this.#from
271
286
  }
272
287
 
273
288
  get to () {
274
- return this[_to]
289
+ return this.#to
275
290
  }
276
291
 
277
292
  toJSON () {
278
- return printableEdge(this)
293
+ return new ArboristEdge(this)
279
294
  }
280
295
 
281
296
  [util.inspect.custom] () {
@@ -283,12 +298,4 @@ class Edge {
283
298
  }
284
299
  }
285
300
 
286
- Edge.types = [...types]
287
- Edge.errors = [
288
- 'DETACHED',
289
- 'MISSING',
290
- 'PEER LOCAL',
291
- 'INVALID',
292
- ]
293
-
294
301
  module.exports = Edge
package/lib/from-path.js CHANGED
@@ -1,24 +1,30 @@
1
- // file dependencies need their dependencies resolved based on the
2
- // location where the tarball was found, not the location where they
3
- // end up getting installed. directory (ie, symlink) deps also need
4
- // to be resolved based on their targets, but that's what realpath is
1
+ // file dependencies need their dependencies resolved based on the location
2
+ // where the tarball was found, not the location where they end up getting
3
+ // installed. directory (ie, symlink) deps also need to be resolved based on
4
+ // their targets, but that's what realpath is
5
5
 
6
6
  const { dirname } = require('path')
7
7
  const npa = require('npm-package-arg')
8
8
 
9
- const fromPath = (node, spec, edge) => {
9
+ const fromPath = (node, edge) => {
10
10
  if (edge && edge.overrides && edge.overrides.name === edge.name && edge.overrides.value) {
11
- // fromPath could be called with a node that has a virtual root, if that happens
12
- // we want to make sure we get the real root node when overrides are in use. this
13
- // is to allow things like overriding a dependency with a tarball file that's a
14
- // relative path from the project root
15
- return node.sourceReference
16
- ? node.sourceReference.root.realpath
17
- : node.root.realpath
11
+ // fromPath could be called with a node that has a virtual root, if that
12
+ // happens we want to make sure we get the real root node when overrides
13
+ // are in use. this is to allow things like overriding a dependency with a
14
+ // tarball file that's a relative path from the project root
15
+ if (node.sourceReference) {
16
+ return node.sourceReference.root.realpath
17
+ }
18
+ return node.root.realpath
18
19
  }
19
20
 
20
- return spec && spec.type === 'file' ? dirname(spec.fetchSpec)
21
- : node.realpath
21
+ if (node.resolved) {
22
+ const spec = npa(node.resolved)
23
+ if (spec?.type === 'file') {
24
+ return dirname(spec.fetchSpec)
25
+ }
26
+ }
27
+ return node.realpath
22
28
  }
23
29
 
24
- module.exports = (node, edge) => fromPath(node, node.resolved && npa(node.resolved), edge)
30
+ module.exports = fromPath
package/lib/inventory.js CHANGED
@@ -1,45 +1,28 @@
1
- // a class to manage an inventory and set of indexes of
2
- // a set of objects based on specific fields.
3
- // primary is the primary index key.
4
- // keys is the set of fields to be able to query.
5
- const _primaryKey = Symbol('_primaryKey')
6
- const _index = Symbol('_index')
7
- const defaultKeys = ['name', 'license', 'funding', 'realpath', 'packageName']
1
+ // a class to manage an inventory and set of indexes of a set of objects based
2
+ // on specific fields.
8
3
  const { hasOwnProperty } = Object.prototype
9
4
  const debug = require('./debug.js')
10
5
 
11
- // handling for the outdated "licenses" array, just pick the first one
12
- // also support the alternative spelling "licence"
13
- const getLicense = pkg => {
14
- if (pkg) {
15
- const lic = pkg.license || pkg.licence
16
- if (lic) {
17
- return lic
18
- }
19
- const lics = pkg.licenses || pkg.licences
20
- if (Array.isArray(lics)) {
21
- return lics[0]
22
- }
23
- }
24
- }
25
-
6
+ const keys = ['name', 'license', 'funding', 'realpath', 'packageName']
26
7
  class Inventory extends Map {
27
- constructor (opt = {}) {
28
- const { primary, keys } = opt
8
+ #index
9
+
10
+ constructor () {
29
11
  super()
30
- this[_primaryKey] = primary || 'location'
31
- this[_index] = (keys || defaultKeys).reduce((index, i) => {
32
- index.set(i, new Map())
33
- return index
34
- }, new Map())
12
+ this.#index = new Map()
13
+ for (const key of keys) {
14
+ this.#index.set(key, new Map())
15
+ }
35
16
  }
36
17
 
18
+ // XXX where is this used?
37
19
  get primaryKey () {
38
- return this[_primaryKey]
20
+ return 'location'
39
21
  }
40
22
 
23
+ // XXX where is this used?
41
24
  get indexes () {
42
- return [...this[_index].keys()]
25
+ return [...keys]
43
26
  }
44
27
 
45
28
  * filter (fn) {
@@ -63,28 +46,49 @@ class Inventory extends Map {
63
46
  return
64
47
  }
65
48
 
66
- const current = super.get(node[this.primaryKey])
49
+ const current = super.get(node.location)
67
50
  if (current) {
68
51
  if (current === node) {
69
52
  return
70
53
  }
71
54
  this.delete(current)
72
55
  }
73
- super.set(node[this.primaryKey], node)
74
- for (const [key, map] of this[_index].entries()) {
75
- // if the node has the value, but it's false, then use that
76
- const val_ = hasOwnProperty.call(node, key) ? node[key]
77
- : key === 'license' ? getLicense(node.package)
78
- : node[key] ? node[key]
79
- : node.package && node.package[key]
80
- const val = typeof val_ === 'string' ? val_
81
- : !val_ || typeof val_ !== 'object' ? val_
82
- : key === 'license' ? val_.type
83
- : key === 'funding' ? val_.url
84
- : /* istanbul ignore next - not used */ val_
85
- const set = map.get(val) || new Set()
86
- set.add(node)
87
- map.set(val, set)
56
+ super.set(node.location, node)
57
+ for (const [key, map] of this.#index.entries()) {
58
+ let val
59
+ if (hasOwnProperty.call(node, key)) {
60
+ // if the node has the value, use it even if it's false
61
+ val = node[key]
62
+ } else if (key === 'license' && node.package) {
63
+ // handling for the outdated "licenses" array, just pick the first one
64
+ // also support the alternative spelling "licence"
65
+ if (node.package.license) {
66
+ val = node.package.license
67
+ } else if (node.package.licence) {
68
+ val = node.package.licence
69
+ } else if (Array.isArray(node.package.licenses)) {
70
+ val = node.package.licenses[0]
71
+ } else if (Array.isArray(node.package.licences)) {
72
+ val = node.package.licences[0]
73
+ }
74
+ } else if (node[key]) {
75
+ val = node[key]
76
+ } else {
77
+ val = node.package?.[key]
78
+ }
79
+ if (val && typeof val === 'object') {
80
+ // We currently only use license and funding
81
+ /* istanbul ignore next - not used */
82
+ if (key === 'license') {
83
+ val = val.type
84
+ } else if (key === 'funding') {
85
+ val = val.url
86
+ }
87
+ }
88
+ if (!map.has(val)) {
89
+ map.set(val, new Set())
90
+ }
91
+ map.get(val).add(node)
88
92
  }
89
93
  }
90
94
 
@@ -93,10 +97,14 @@ class Inventory extends Map {
93
97
  return
94
98
  }
95
99
 
96
- super.delete(node[this.primaryKey])
97
- for (const [key, map] of this[_index].entries()) {
98
- const val = node[key] !== undefined ? node[key]
99
- : (node[key] || (node.package && node.package[key]))
100
+ super.delete(node.location)
101
+ for (const [key, map] of this.#index.entries()) {
102
+ let val
103
+ if (node[key] !== undefined) {
104
+ val = node[key]
105
+ } else {
106
+ val = node.package?.[key]
107
+ }
100
108
  const set = map.get(val)
101
109
  if (set) {
102
110
  set.delete(node)
@@ -108,13 +116,18 @@ class Inventory extends Map {
108
116
  }
109
117
 
110
118
  query (key, val) {
111
- const map = this[_index].get(key)
112
- return map && (arguments.length === 2 ? map.get(val) : map.keys()) ||
113
- new Set()
119
+ const map = this.#index.get(key)
120
+ if (arguments.length === 2) {
121
+ if (map.has(val)) {
122
+ return map.get(val)
123
+ }
124
+ return new Set()
125
+ }
126
+ return map.keys()
114
127
  }
115
128
 
116
129
  has (node) {
117
- return super.get(node[this.primaryKey]) === node
130
+ return super.get(node.location) === node
118
131
  }
119
132
 
120
133
  set (k, v) {