@npmcli/arborist 6.2.8 → 6.2.10

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,5 +1,4 @@
1
1
  // mixin providing the loadVirtual method
2
- const localeCompare = require('@isaacs/string-locale-compare')('en')
3
2
  const mapWorkspaces = require('@npmcli/map-workspaces')
4
3
 
5
4
  const { resolve } = require('path')
@@ -14,23 +13,12 @@ const calcDepFlags = require('../calc-dep-flags.js')
14
13
  const rpj = require('read-package-json-fast')
15
14
  const treeCheck = require('../tree-check.js')
16
15
 
17
- const loadFromShrinkwrap = Symbol('loadFromShrinkwrap')
18
- const resolveNodes = Symbol('resolveNodes')
19
- const resolveLinks = Symbol('resolveLinks')
20
- const assignBundles = Symbol('assignBundles')
21
- const loadRoot = Symbol('loadRoot')
22
- const loadNode = Symbol('loadVirtualNode')
23
- const loadLink = Symbol('loadVirtualLink')
24
- const loadWorkspaces = Symbol.for('loadWorkspaces')
25
16
  const flagsSuspect = Symbol.for('flagsSuspect')
26
- const reCalcDepFlags = Symbol('reCalcDepFlags')
27
- const checkRootEdges = Symbol('checkRootEdges')
28
- const rootOptionProvided = Symbol('rootOptionProvided')
29
-
30
- const depsToEdges = (type, deps) =>
31
- Object.entries(deps).map(d => [type, ...d])
17
+ const setWorkspaces = Symbol.for('setWorkspaces')
32
18
 
33
19
  module.exports = cls => class VirtualLoader extends cls {
20
+ #rootOptionProvided
21
+
34
22
  constructor (options) {
35
23
  super(options)
36
24
 
@@ -50,7 +38,7 @@ module.exports = cls => class VirtualLoader extends cls {
50
38
  options = { ...this.options, ...options }
51
39
 
52
40
  if (options.root && options.root.meta) {
53
- await this[loadFromShrinkwrap](options.root.meta, options.root)
41
+ await this.#loadFromShrinkwrap(options.root.meta, options.root)
54
42
  return treeCheck(this.virtualTree)
55
43
  }
56
44
 
@@ -67,24 +55,24 @@ module.exports = cls => class VirtualLoader extends cls {
67
55
  // when building the ideal tree, we pass in a root node to this function
68
56
  // otherwise, load it from the root package json or the lockfile
69
57
  const {
70
- root = await this[loadRoot](s),
58
+ root = await this.#loadRoot(s),
71
59
  } = options
72
60
 
73
- this[rootOptionProvided] = options.root
61
+ this.#rootOptionProvided = options.root
74
62
 
75
- await this[loadFromShrinkwrap](s, root)
63
+ await this.#loadFromShrinkwrap(s, root)
76
64
  root.assertRootOverrides()
77
65
  return treeCheck(this.virtualTree)
78
66
  }
79
67
 
80
- async [loadRoot] (s) {
68
+ async #loadRoot (s) {
81
69
  const pj = this.path + '/package.json'
82
70
  const pkg = await rpj(pj).catch(() => s.data.packages['']) || {}
83
- return this[loadWorkspaces](this[loadNode]('', pkg, true))
71
+ return this[setWorkspaces](this.#loadNode('', pkg, true))
84
72
  }
85
73
 
86
- async [loadFromShrinkwrap] (s, root) {
87
- if (!this[rootOptionProvided]) {
74
+ async #loadFromShrinkwrap (s, root) {
75
+ if (!this.#rootOptionProvided) {
88
76
  // root is never any of these things, but might be a brand new
89
77
  // baby Node object that never had its dep flags calculated.
90
78
  root.extraneous = false
@@ -96,41 +84,37 @@ module.exports = cls => class VirtualLoader extends cls {
96
84
  this[flagsSuspect] = true
97
85
  }
98
86
 
99
- this[checkRootEdges](s, root)
87
+ this.#checkRootEdges(s, root)
100
88
  root.meta = s
101
89
  this.virtualTree = root
102
- const { links, nodes } = this[resolveNodes](s, root)
103
- await this[resolveLinks](links, nodes)
90
+ const { links, nodes } = this.#resolveNodes(s, root)
91
+ await this.#resolveLinks(links, nodes)
104
92
  if (!(s.originalLockfileVersion >= 2)) {
105
- this[assignBundles](nodes)
93
+ this.#assignBundles(nodes)
106
94
  }
107
95
  if (this[flagsSuspect]) {
108
- this[reCalcDepFlags](nodes.values())
109
- }
110
- return root
111
- }
112
-
113
- [reCalcDepFlags] (nodes) {
114
- // reset all dep flags
115
- // can't use inventory here, because virtualTree might not be root
116
- for (const node of nodes) {
117
- if (node.isRoot || node === this[rootOptionProvided]) {
118
- continue
96
+ // reset all dep flags
97
+ // can't use inventory here, because virtualTree might not be root
98
+ for (const node of nodes.values()) {
99
+ if (node.isRoot || node === this.#rootOptionProvided) {
100
+ continue
101
+ }
102
+ node.extraneous = true
103
+ node.dev = true
104
+ node.optional = true
105
+ node.devOptional = true
106
+ node.peer = true
119
107
  }
120
- node.extraneous = true
121
- node.dev = true
122
- node.optional = true
123
- node.devOptional = true
124
- node.peer = true
108
+ calcDepFlags(this.virtualTree, !this.#rootOptionProvided)
125
109
  }
126
- calcDepFlags(this.virtualTree, !this[rootOptionProvided])
110
+ return root
127
111
  }
128
112
 
129
113
  // check the lockfile deps, and see if they match. if they do not
130
114
  // then we have to reset dep flags at the end. for example, if the
131
115
  // user manually edits their package.json file, then we need to know
132
116
  // that the idealTree is no longer entirely trustworthy.
133
- [checkRootEdges] (s, root) {
117
+ #checkRootEdges (s, root) {
134
118
  // loaded virtually from tree, no chance of being out of sync
135
119
  // ancient lockfiles are critically damaged by this process,
136
120
  // so we need to just hope for the best in those cases.
@@ -144,6 +128,7 @@ module.exports = cls => class VirtualLoader extends cls {
144
128
  const optional = lock.optionalDependencies || {}
145
129
  const peer = lock.peerDependencies || {}
146
130
  const peerOptional = {}
131
+
147
132
  if (lock.peerDependenciesMeta) {
148
133
  for (const [name, meta] of Object.entries(lock.peerDependenciesMeta)) {
149
134
  if (meta.optional && peer[name] !== undefined) {
@@ -152,50 +137,45 @@ module.exports = cls => class VirtualLoader extends cls {
152
137
  }
153
138
  }
154
139
  }
140
+
155
141
  for (const name of Object.keys(optional)) {
156
142
  delete prod[name]
157
143
  }
158
144
 
159
- const lockWS = []
145
+ const lockWS = {}
160
146
  const workspaces = mapWorkspaces.virtual({
161
147
  cwd: this.path,
162
148
  lockfile: s.data,
163
149
  })
150
+
164
151
  for (const [name, path] of workspaces.entries()) {
165
- lockWS.push(['workspace', name, `file:${path.replace(/#/g, '%23')}`])
152
+ lockWS[name] = `file:${path.replace(/#/g, '%23')}`
166
153
  }
167
154
 
168
- const lockEdges = [
169
- ...depsToEdges('prod', prod),
170
- ...depsToEdges('dev', dev),
171
- ...depsToEdges('optional', optional),
172
- ...depsToEdges('peer', peer),
173
- ...depsToEdges('peerOptional', peerOptional),
174
- ...lockWS,
175
- ].sort(([atype, aname], [btype, bname]) =>
176
- localeCompare(atype, btype) || localeCompare(aname, bname))
177
-
178
- const rootEdges = [...root.edgesOut.values()]
179
- .map(e => [e.type, e.name, e.spec])
180
- .sort(([atype, aname], [btype, bname]) =>
181
- localeCompare(atype, btype) || localeCompare(aname, bname))
182
-
183
- if (rootEdges.length !== lockEdges.length) {
184
- // something added or removed
185
- return this[flagsSuspect] = true
186
- }
155
+ // Should rootNames exclude optional?
156
+ const rootNames = new Set(root.edgesOut.keys())
187
157
 
188
- for (let i = 0; i < lockEdges.length; i++) {
189
- if (rootEdges[i][0] !== lockEdges[i][0] ||
190
- rootEdges[i][1] !== lockEdges[i][1] ||
191
- rootEdges[i][2] !== lockEdges[i][2]) {
192
- return this[flagsSuspect] = true
158
+ const lockByType = ({ dev, optional, peer, peerOptional, prod, workspace: lockWS })
159
+
160
+ // Find anything in shrinkwrap deps that doesn't match root's type or spec
161
+ for (const type in lockByType) {
162
+ const deps = lockByType[type]
163
+ for (const name in deps) {
164
+ const edge = root.edgesOut.get(name)
165
+ if (!edge || edge.type !== type || edge.spec !== deps[name]) {
166
+ return this[flagsSuspect] = true
167
+ }
168
+ rootNames.delete(name)
193
169
  }
194
170
  }
171
+ // Something was in root that's not accounted for in shrinkwrap
172
+ if (rootNames.size) {
173
+ return this[flagsSuspect] = true
174
+ }
195
175
  }
196
176
 
197
177
  // separate out link metadatas, and create Node objects for nodes
198
- [resolveNodes] (s, root) {
178
+ #resolveNodes (s, root) {
199
179
  const links = new Map()
200
180
  const nodes = new Map([['', root]])
201
181
  for (const [location, meta] of Object.entries(s.data.packages)) {
@@ -207,7 +187,7 @@ module.exports = cls => class VirtualLoader extends cls {
207
187
  if (meta.link) {
208
188
  links.set(location, meta)
209
189
  } else {
210
- nodes.set(location, this[loadNode](location, meta))
190
+ nodes.set(location, this.#loadNode(location, meta))
211
191
  }
212
192
  }
213
193
  return { links, nodes }
@@ -215,12 +195,12 @@ module.exports = cls => class VirtualLoader extends cls {
215
195
 
216
196
  // links is the set of metadata, and nodes is the map of non-Link nodes
217
197
  // Set the targets to nodes in the set, if we have them (we might not)
218
- async [resolveLinks] (links, nodes) {
198
+ async #resolveLinks (links, nodes) {
219
199
  for (const [location, meta] of links.entries()) {
220
200
  const targetPath = resolve(this.path, meta.resolved)
221
201
  const targetLoc = relpath(this.path, targetPath)
222
202
  const target = nodes.get(targetLoc)
223
- const link = this[loadLink](location, targetLoc, target, meta)
203
+ const link = this.#loadLink(location, targetLoc, target, meta)
224
204
  nodes.set(location, link)
225
205
  nodes.set(targetLoc, link.target)
226
206
 
@@ -236,7 +216,7 @@ module.exports = cls => class VirtualLoader extends cls {
236
216
  }
237
217
  }
238
218
 
239
- [assignBundles] (nodes) {
219
+ #assignBundles (nodes) {
240
220
  for (const [location, node] of nodes) {
241
221
  // Skip assignment of parentage for the root package
242
222
  if (!location || node.isLink && !node.target.location) {
@@ -265,7 +245,7 @@ module.exports = cls => class VirtualLoader extends cls {
265
245
  }
266
246
  }
267
247
 
268
- [loadNode] (location, sw, loadOverrides) {
248
+ #loadNode (location, sw, loadOverrides) {
269
249
  const p = this.virtualTree ? this.virtualTree.realpath : this.path
270
250
  const path = resolve(p, location)
271
251
  // shrinkwrap doesn't include package name unless necessary
@@ -303,7 +283,7 @@ module.exports = cls => class VirtualLoader extends cls {
303
283
  return node
304
284
  }
305
285
 
306
- [loadLink] (location, targetLoc, target, meta) {
286
+ #loadLink (location, targetLoc, target, meta) {
307
287
  const path = resolve(this.path, location)
308
288
  const link = new Link({
309
289
  installLinks: this.installLinks,
@@ -483,17 +483,29 @@ module.exports = cls => class Reifier extends cls {
483
483
 
484
484
  process.emit('time', 'reify:trashOmits')
485
485
 
486
- const filter = node =>
487
- node.top.isProjectRoot &&
488
- (
489
- node.peer && this[_omitPeer] ||
490
- node.dev && this[_omitDev] ||
491
- node.optional && this[_omitOptional] ||
492
- node.devOptional && this[_omitOptional] && this[_omitDev]
493
- )
494
-
495
- for (const node of this.idealTree.inventory.filter(filter)) {
496
- this[_addNodeToTrashList](node)
486
+ for (const node of this.idealTree.inventory.values()) {
487
+ const { top } = node
488
+
489
+ // if the top is not the root or workspace then we do not want to omit it
490
+ if (!top.isProjectRoot && !top.isWorkspace) {
491
+ continue
492
+ }
493
+
494
+ // if a diff filter has been created, then we do not omit the node if the
495
+ // top node is not in that set
496
+ if (this.diff?.filterSet?.size && !this.diff.filterSet.has(top)) {
497
+ continue
498
+ }
499
+
500
+ // omit node if the dep type matches any omit flags that were set
501
+ if (
502
+ node.peer && this[_omitPeer] ||
503
+ node.dev && this[_omitDev] ||
504
+ node.optional && this[_omitOptional] ||
505
+ node.devOptional && this[_omitOptional] && this[_omitDev]
506
+ ) {
507
+ this[_addNodeToTrashList](node)
508
+ }
497
509
  }
498
510
 
499
511
  process.emit('timeEnd', 'reify:trashOmits')
@@ -1,10 +1,10 @@
1
1
  const mapWorkspaces = require('@npmcli/map-workspaces')
2
2
 
3
3
  // shared ref used by other mixins/Arborist
4
- const _loadWorkspaces = Symbol.for('loadWorkspaces')
4
+ const _setWorkspaces = Symbol.for('setWorkspaces')
5
5
 
6
6
  module.exports = cls => class MapWorkspaces extends cls {
7
- async [_loadWorkspaces] (node) {
7
+ async [_setWorkspaces] (node) {
8
8
  const workspaces = await mapWorkspaces({
9
9
  cwd: node.path,
10
10
  pkg: node.package,
@@ -51,14 +51,12 @@ const calcDepFlagsStep = (node) => {
51
51
  // however, for convenience and to save an extra rewalk, we leave
52
52
  // it set when we are in *either* tree, and then omit it from the
53
53
  // package-lock if either dev or optional are set.
54
- const unsetDevOpt = !node.devOptional && !node.dev && !node.optional &&
55
- !dev && !optional
54
+ const unsetDevOpt = !node.devOptional && !node.dev && !node.optional && !dev && !optional
56
55
 
57
56
  // if we are not in the devOpt tree, then we're also not in
58
57
  // either the dev or opt trees
59
58
  const unsetDev = unsetDevOpt || !node.dev && !dev
60
- const unsetOpt = unsetDevOpt ||
61
- !node.optional && !optional
59
+ const unsetOpt = unsetDevOpt || !node.optional && !optional
62
60
  const unsetPeer = !node.peer && !peer
63
61
 
64
62
  if (unsetPeer) {
@@ -91,8 +89,7 @@ const resetParents = (node, flag) => {
91
89
  }
92
90
  }
93
91
 
94
- // typically a short walk, since it only traverses deps that
95
- // have the flag set.
92
+ // typically a short walk, since it only traverses deps that have the flag set.
96
93
  const unsetFlag = (node, flag) => {
97
94
  if (node[flag]) {
98
95
  node[flag] = false
@@ -104,10 +101,17 @@ const unsetFlag = (node, flag) => {
104
101
  node.target.extraneous = node.target[flag] = false
105
102
  }
106
103
  },
107
- getChildren: node => [...node.target.edgesOut.values()]
108
- .filter(edge => edge.to && edge.to[flag] &&
109
- (flag !== 'peer' && edge.type === 'peer' || edge.type === 'prod'))
110
- .map(edge => edge.to),
104
+ getChildren: node => {
105
+ const children = []
106
+ for (const edge of node.target.edgesOut.values()) {
107
+ if (edge.to && edge.to[flag] &&
108
+ (flag !== 'peer' && edge.type === 'peer' || edge.type === 'prod')
109
+ ) {
110
+ children.push(edge.to)
111
+ }
112
+ }
113
+ return children
114
+ },
111
115
  })
112
116
  }
113
117
  }
package/lib/dep-valid.js CHANGED
@@ -84,15 +84,21 @@ const depValid = (child, requested, requestor) => {
84
84
  const reqHost = requested.hosted
85
85
  const reqCommit = /^[a-fA-F0-9]{40}$/.test(requested.gitCommittish || '')
86
86
  const nc = { noCommittish: !reqCommit }
87
- const sameRepo =
88
- resHost ? reqHost && reqHost.ssh(nc) === resHost.ssh(nc)
89
- : resRepo.fetchSpec === requested.fetchSpec
90
-
91
- return !sameRepo ? false
92
- : !requested.gitRange ? true
93
- : semver.satisfies(child.package.version, requested.gitRange, {
94
- loose: true,
95
- })
87
+ if (!resHost) {
88
+ if (resRepo.fetchSpec !== requested.fetchSpec) {
89
+ return false
90
+ }
91
+ } else {
92
+ if (reqHost?.ssh(nc) !== resHost.ssh(nc)) {
93
+ return false
94
+ }
95
+ }
96
+ if (!requested.gitRange) {
97
+ return true
98
+ }
99
+ return semver.satisfies(child.package.version, requested.gitRange, {
100
+ loose: true,
101
+ })
96
102
  }
97
103
 
98
104
  default: // unpossible, just being cautious