@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.
- package/bin/actual.js +4 -2
- package/bin/audit.js +12 -6
- package/bin/dedupe.js +6 -3
- package/bin/funding.js +4 -2
- package/bin/ideal.js +2 -1
- package/bin/lib/logging.js +4 -3
- package/bin/lib/options.js +14 -12
- package/bin/lib/timers.js +6 -3
- package/bin/license.js +9 -5
- package/bin/prune.js +6 -3
- package/bin/reify.js +6 -3
- package/bin/virtual.js +4 -2
- package/lib/add-rm-pkg-deps.js +28 -15
- package/lib/arborist/audit.js +2 -1
- package/lib/arborist/build-ideal-tree.js +139 -72
- package/lib/arborist/deduper.js +2 -1
- package/lib/arborist/index.js +8 -4
- package/lib/arborist/load-actual.js +28 -13
- package/lib/arborist/load-virtual.js +37 -20
- package/lib/arborist/load-workspaces.js +4 -2
- package/lib/arborist/rebuild.js +34 -17
- package/lib/arborist/reify.js +153 -76
- package/lib/audit-report.js +44 -23
- package/lib/calc-dep-flags.js +18 -9
- package/lib/can-place-dep.js +59 -30
- package/lib/case-insensitive-map.js +4 -2
- package/lib/consistent-resolve.js +2 -1
- package/lib/deepest-nesting-target.js +4 -2
- package/lib/dep-valid.js +8 -4
- package/lib/diff.js +74 -22
- package/lib/edge.js +26 -13
- package/lib/gather-dep-set.js +2 -1
- package/lib/inventory.js +12 -6
- package/lib/link.js +14 -9
- package/lib/node.js +216 -113
- package/lib/optional-set.js +4 -2
- package/lib/peer-entry-sets.js +10 -5
- package/lib/place-dep.js +111 -37
- package/lib/printable.js +46 -25
- package/lib/realpath.js +12 -6
- package/lib/shrinkwrap.js +164 -90
- package/lib/signal-handling.js +6 -3
- package/lib/spec-from-lock.js +7 -4
- package/lib/tracker.js +24 -18
- package/lib/tree-check.js +12 -6
- package/lib/version-from-tgz.js +4 -2
- package/lib/vuln.js +44 -22
- package/lib/yarn-lock.js +34 -21
- package/package.json +8 -10
package/lib/audit-report.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// an object representing the set of vulnerabilities in a tree
|
|
2
2
|
/* eslint camelcase: "off" */
|
|
3
3
|
|
|
4
|
+
const localeCompare = require('@isaacs/string-locale-compare')('en')
|
|
4
5
|
const npa = require('npm-package-arg')
|
|
5
6
|
const pickManifest = require('npm-pick-manifest')
|
|
6
7
|
|
|
@@ -63,8 +64,9 @@ class AuditReport extends Map {
|
|
|
63
64
|
prod = false
|
|
64
65
|
}
|
|
65
66
|
}
|
|
66
|
-
if (prod)
|
|
67
|
+
if (prod) {
|
|
67
68
|
dependencies.prod++
|
|
69
|
+
}
|
|
68
70
|
}
|
|
69
71
|
|
|
70
72
|
// if it doesn't have any topVulns, then it's fixable with audit fix
|
|
@@ -78,7 +80,7 @@ class AuditReport extends Map {
|
|
|
78
80
|
}
|
|
79
81
|
|
|
80
82
|
obj.vulnerabilities = vulnerabilities
|
|
81
|
-
.sort(([a], [b]) =>
|
|
83
|
+
.sort(([a], [b]) => localeCompare(a, b))
|
|
82
84
|
.reduce((set, [name, vuln]) => {
|
|
83
85
|
set[name] = vuln
|
|
84
86
|
return set
|
|
@@ -104,8 +106,9 @@ class AuditReport extends Map {
|
|
|
104
106
|
async run () {
|
|
105
107
|
this.report = await this[_getReport]()
|
|
106
108
|
this.log.silly('audit report', this.report)
|
|
107
|
-
if (this.report)
|
|
109
|
+
if (this.report) {
|
|
108
110
|
await this[_init]()
|
|
111
|
+
}
|
|
109
112
|
return this
|
|
110
113
|
}
|
|
111
114
|
|
|
@@ -119,8 +122,9 @@ class AuditReport extends Map {
|
|
|
119
122
|
|
|
120
123
|
const promises = []
|
|
121
124
|
for (const [name, advisories] of Object.entries(this.report)) {
|
|
122
|
-
for (const advisory of advisories)
|
|
125
|
+
for (const advisory of advisories) {
|
|
123
126
|
promises.push(this.calculator.calculate(name, advisory))
|
|
127
|
+
}
|
|
124
128
|
}
|
|
125
129
|
|
|
126
130
|
// now the advisories are calculated with a set of versions
|
|
@@ -136,43 +140,51 @@ class AuditReport extends Map {
|
|
|
136
140
|
// adding multiple advisories with the same range is fine, but no
|
|
137
141
|
// need to search for nodes we already would have added.
|
|
138
142
|
const k = `${name}@${range}`
|
|
139
|
-
if (seen.has(k))
|
|
143
|
+
if (seen.has(k)) {
|
|
140
144
|
continue
|
|
145
|
+
}
|
|
141
146
|
|
|
142
147
|
seen.add(k)
|
|
143
148
|
|
|
144
149
|
const vuln = this.get(name) || new Vuln({ name, advisory })
|
|
145
|
-
if (this.has(name))
|
|
150
|
+
if (this.has(name)) {
|
|
146
151
|
vuln.addAdvisory(advisory)
|
|
152
|
+
}
|
|
147
153
|
super.set(name, vuln)
|
|
148
154
|
|
|
149
155
|
const p = []
|
|
150
156
|
for (const node of this.tree.inventory.query('packageName', name)) {
|
|
151
|
-
if (!shouldAudit(node, this[_omit], this.filterSet))
|
|
157
|
+
if (!shouldAudit(node, this[_omit], this.filterSet)) {
|
|
152
158
|
continue
|
|
159
|
+
}
|
|
153
160
|
|
|
154
161
|
// if not vulnerable by this advisory, keep searching
|
|
155
|
-
if (!advisory.testVersion(node.version))
|
|
162
|
+
if (!advisory.testVersion(node.version)) {
|
|
156
163
|
continue
|
|
164
|
+
}
|
|
157
165
|
|
|
158
166
|
// we will have loaded the source already if this is a metavuln
|
|
159
|
-
if (advisory.type === 'metavuln')
|
|
167
|
+
if (advisory.type === 'metavuln') {
|
|
160
168
|
vuln.addVia(this.get(advisory.dependency))
|
|
169
|
+
}
|
|
161
170
|
|
|
162
171
|
// already marked this one, no need to do it again
|
|
163
|
-
if (vuln.nodes.has(node))
|
|
172
|
+
if (vuln.nodes.has(node)) {
|
|
164
173
|
continue
|
|
174
|
+
}
|
|
165
175
|
|
|
166
176
|
// haven't marked this one yet. get its dependents.
|
|
167
177
|
vuln.nodes.add(node)
|
|
168
178
|
for (const { from: dep, spec } of node.edgesIn) {
|
|
169
|
-
if (dep.isTop && !vuln.topNodes.has(dep))
|
|
179
|
+
if (dep.isTop && !vuln.topNodes.has(dep)) {
|
|
170
180
|
this[_checkTopNode](dep, vuln, spec)
|
|
171
|
-
else {
|
|
181
|
+
} else {
|
|
172
182
|
// calculate a metavuln, if necessary
|
|
173
|
-
|
|
174
|
-
|
|
183
|
+
const calc = this.calculator.calculate(dep.packageName, advisory)
|
|
184
|
+
p.push(calc.then(meta => {
|
|
185
|
+
if (meta.testVersion(dep.version, spec)) {
|
|
175
186
|
advisories.add(meta)
|
|
187
|
+
}
|
|
176
188
|
}))
|
|
177
189
|
}
|
|
178
190
|
}
|
|
@@ -193,9 +205,11 @@ class AuditReport extends Map {
|
|
|
193
205
|
// the nodes it references, then remove it from the advisory list.
|
|
194
206
|
// happens when using omit with old audit endpoint.
|
|
195
207
|
for (const advisory of vuln.advisories) {
|
|
196
|
-
const relevant = [...vuln.nodes]
|
|
197
|
-
|
|
208
|
+
const relevant = [...vuln.nodes]
|
|
209
|
+
.some(n => advisory.testVersion(n.version))
|
|
210
|
+
if (!relevant) {
|
|
198
211
|
vuln.deleteAdvisory(advisory)
|
|
212
|
+
}
|
|
199
213
|
}
|
|
200
214
|
}
|
|
201
215
|
process.emit('timeEnd', 'auditReport:init')
|
|
@@ -221,18 +235,21 @@ class AuditReport extends Map {
|
|
|
221
235
|
// this will always be set to at least {name, versions:{}}
|
|
222
236
|
const paku = vuln.packument
|
|
223
237
|
|
|
224
|
-
if (!vuln.testSpec(spec))
|
|
238
|
+
if (!vuln.testSpec(spec)) {
|
|
225
239
|
return true
|
|
240
|
+
}
|
|
226
241
|
|
|
227
242
|
// similarly, even if we HAVE a packument, but we're looking for it
|
|
228
243
|
// somewhere other than the registry, and we got something vulnerable,
|
|
229
244
|
// then we're stuck with it.
|
|
230
245
|
const specObj = npa(spec)
|
|
231
|
-
if (!specObj.registry)
|
|
246
|
+
if (!specObj.registry) {
|
|
232
247
|
return false
|
|
248
|
+
}
|
|
233
249
|
|
|
234
|
-
if (specObj.subSpec)
|
|
250
|
+
if (specObj.subSpec) {
|
|
235
251
|
spec = specObj.subSpec.rawSpec
|
|
252
|
+
}
|
|
236
253
|
|
|
237
254
|
// We don't provide fixes for top nodes other than root, but we
|
|
238
255
|
// still check to see if the node is fixable with a different version,
|
|
@@ -287,8 +304,9 @@ class AuditReport extends Map {
|
|
|
287
304
|
|
|
288
305
|
async [_getReport] () {
|
|
289
306
|
// if we're not auditing, just return false
|
|
290
|
-
if (this.options.audit === false || this.tree.inventory.size === 1)
|
|
307
|
+
if (this.options.audit === false || this.tree.inventory.size === 1) {
|
|
291
308
|
return null
|
|
309
|
+
}
|
|
292
310
|
|
|
293
311
|
process.emit('time', 'auditReport:getReport')
|
|
294
312
|
try {
|
|
@@ -299,8 +317,9 @@ class AuditReport extends Map {
|
|
|
299
317
|
|
|
300
318
|
// no sense asking if we don't have anything to audit,
|
|
301
319
|
// we know it'll be empty
|
|
302
|
-
if (!Object.keys(body).length)
|
|
320
|
+
if (!Object.keys(body).length) {
|
|
303
321
|
return null
|
|
322
|
+
}
|
|
304
323
|
|
|
305
324
|
const res = await fetch('/-/npm/v1/security/advisories/bulk', {
|
|
306
325
|
...this.options,
|
|
@@ -353,13 +372,15 @@ const prepareBulkData = (tree, omit, filterSet) => {
|
|
|
353
372
|
for (const name of tree.inventory.query('packageName')) {
|
|
354
373
|
const set = new Set()
|
|
355
374
|
for (const node of tree.inventory.query('packageName', name)) {
|
|
356
|
-
if (!shouldAudit(node, omit, filterSet))
|
|
375
|
+
if (!shouldAudit(node, omit, filterSet)) {
|
|
357
376
|
continue
|
|
377
|
+
}
|
|
358
378
|
|
|
359
379
|
set.add(node.version)
|
|
360
380
|
}
|
|
361
|
-
if (set.size)
|
|
381
|
+
if (set.size) {
|
|
362
382
|
payload[name] = [...set]
|
|
383
|
+
}
|
|
363
384
|
}
|
|
364
385
|
return payload
|
|
365
386
|
}
|
package/lib/calc-dep-flags.js
CHANGED
|
@@ -11,7 +11,8 @@ const calcDepFlags = (tree, resetRoot = true) => {
|
|
|
11
11
|
tree,
|
|
12
12
|
visit: node => calcDepFlagsStep(node),
|
|
13
13
|
filter: node => node,
|
|
14
|
-
getChildren: (node, tree) =>
|
|
14
|
+
getChildren: (node, tree) =>
|
|
15
|
+
[...tree.edgesOut.values()].map(edge => edge.to),
|
|
15
16
|
})
|
|
16
17
|
return ret
|
|
17
18
|
}
|
|
@@ -39,8 +40,9 @@ const calcDepFlagsStep = (node) => {
|
|
|
39
40
|
|
|
40
41
|
node.edgesOut.forEach(({peer, optional, dev, to}) => {
|
|
41
42
|
// if the dep is missing, then its flags are already maximally unset
|
|
42
|
-
if (!to)
|
|
43
|
+
if (!to) {
|
|
43
44
|
return
|
|
45
|
+
}
|
|
44
46
|
|
|
45
47
|
// everything with any kind of edge into it is not extraneous
|
|
46
48
|
to.extraneous = false
|
|
@@ -59,28 +61,34 @@ const calcDepFlagsStep = (node) => {
|
|
|
59
61
|
!node.optional && !optional
|
|
60
62
|
const unsetPeer = !node.peer && !peer
|
|
61
63
|
|
|
62
|
-
if (unsetPeer)
|
|
64
|
+
if (unsetPeer) {
|
|
63
65
|
unsetFlag(to, 'peer')
|
|
66
|
+
}
|
|
64
67
|
|
|
65
|
-
if (unsetDevOpt)
|
|
68
|
+
if (unsetDevOpt) {
|
|
66
69
|
unsetFlag(to, 'devOptional')
|
|
70
|
+
}
|
|
67
71
|
|
|
68
|
-
if (unsetDev)
|
|
72
|
+
if (unsetDev) {
|
|
69
73
|
unsetFlag(to, 'dev')
|
|
74
|
+
}
|
|
70
75
|
|
|
71
|
-
if (unsetOpt)
|
|
76
|
+
if (unsetOpt) {
|
|
72
77
|
unsetFlag(to, 'optional')
|
|
78
|
+
}
|
|
73
79
|
})
|
|
74
80
|
|
|
75
81
|
return node
|
|
76
82
|
}
|
|
77
83
|
|
|
78
84
|
const resetParents = (node, flag) => {
|
|
79
|
-
if (node[flag])
|
|
85
|
+
if (node[flag]) {
|
|
80
86
|
return
|
|
87
|
+
}
|
|
81
88
|
|
|
82
|
-
for (let p = node; p && (p === node || p[flag]); p = p.resolveParent)
|
|
89
|
+
for (let p = node; p && (p === node || p[flag]); p = p.resolveParent) {
|
|
83
90
|
p[flag] = false
|
|
91
|
+
}
|
|
84
92
|
}
|
|
85
93
|
|
|
86
94
|
// typically a short walk, since it only traverses deps that
|
|
@@ -92,8 +100,9 @@ const unsetFlag = (node, flag) => {
|
|
|
92
100
|
tree: node,
|
|
93
101
|
visit: node => {
|
|
94
102
|
node.extraneous = node[flag] = false
|
|
95
|
-
if (node.isLink)
|
|
103
|
+
if (node.isLink) {
|
|
96
104
|
node.target.extraneous = node.target[flag] = false
|
|
105
|
+
}
|
|
97
106
|
},
|
|
98
107
|
getChildren: node => [...node.target.edgesOut.values()]
|
|
99
108
|
.filter(edge => edge.to && edge.to[flag] &&
|
package/lib/can-place-dep.js
CHANGED
|
@@ -35,6 +35,7 @@
|
|
|
35
35
|
// then we will return REPLACE rather than CONFLICT, and Arborist will queue
|
|
36
36
|
// the replaced node for resolution elsewhere.
|
|
37
37
|
|
|
38
|
+
const localeCompare = require('@isaacs/string-locale-compare')('en')
|
|
38
39
|
const semver = require('semver')
|
|
39
40
|
const debug = require('./debug.js')
|
|
40
41
|
const peerEntrySets = require('./peer-entry-sets.js')
|
|
@@ -64,19 +65,22 @@ class CanPlaceDep {
|
|
|
64
65
|
} = options
|
|
65
66
|
|
|
66
67
|
debug(() => {
|
|
67
|
-
if (!dep)
|
|
68
|
+
if (!dep) {
|
|
68
69
|
throw new Error('no dep provided to CanPlaceDep')
|
|
70
|
+
}
|
|
69
71
|
|
|
70
|
-
if (!target)
|
|
72
|
+
if (!target) {
|
|
71
73
|
throw new Error('no target provided to CanPlaceDep')
|
|
74
|
+
}
|
|
72
75
|
|
|
73
|
-
if (!edge)
|
|
76
|
+
if (!edge) {
|
|
74
77
|
throw new Error('no edge provided to CanPlaceDep')
|
|
78
|
+
}
|
|
75
79
|
|
|
76
80
|
this._treeSnapshot = JSON.stringify([...target.root.inventory.entries()]
|
|
77
81
|
.map(([loc, {packageName, version, resolved}]) => {
|
|
78
82
|
return [loc, packageName, version, resolved]
|
|
79
|
-
}).sort(([a], [b]) =>
|
|
83
|
+
}).sort(([a], [b]) => localeCompare(a, b)))
|
|
80
84
|
})
|
|
81
85
|
|
|
82
86
|
// the result of whether we can place it or not
|
|
@@ -108,14 +112,15 @@ class CanPlaceDep {
|
|
|
108
112
|
this.edgeOverride = !dep.satisfies(edge)
|
|
109
113
|
|
|
110
114
|
this.canPlace = this.checkCanPlace()
|
|
111
|
-
if (!this.canPlaceSelf)
|
|
115
|
+
if (!this.canPlaceSelf) {
|
|
112
116
|
this.canPlaceSelf = this.canPlace
|
|
117
|
+
}
|
|
113
118
|
|
|
114
119
|
debug(() => {
|
|
115
120
|
const treeSnapshot = JSON.stringify([...target.root.inventory.entries()]
|
|
116
121
|
.map(([loc, {packageName, version, resolved}]) => {
|
|
117
122
|
return [loc, packageName, version, resolved]
|
|
118
|
-
}).sort(([a], [b]) =>
|
|
123
|
+
}).sort(([a], [b]) => localeCompare(a, b)))
|
|
119
124
|
/* istanbul ignore if */
|
|
120
125
|
if (this._treeSnapshot !== treeSnapshot) {
|
|
121
126
|
throw Object.assign(new Error('tree changed in CanPlaceDep'), {
|
|
@@ -131,15 +136,18 @@ class CanPlaceDep {
|
|
|
131
136
|
|
|
132
137
|
// if the dep failed to load, we're going to fail the build or
|
|
133
138
|
// prune it out anyway, so just move forward placing/replacing it.
|
|
134
|
-
if (dep.errors.length)
|
|
139
|
+
if (dep.errors.length) {
|
|
135
140
|
return current ? REPLACE : OK
|
|
141
|
+
}
|
|
136
142
|
|
|
137
143
|
// cannot place peers inside their dependents, except for tops
|
|
138
|
-
if (targetEdge && targetEdge.peer && !target.isTop)
|
|
144
|
+
if (targetEdge && targetEdge.peer && !target.isTop) {
|
|
139
145
|
return CONFLICT
|
|
146
|
+
}
|
|
140
147
|
|
|
141
|
-
if (targetEdge && !dep.satisfies(targetEdge) && targetEdge !== this.edge)
|
|
148
|
+
if (targetEdge && !dep.satisfies(targetEdge) && targetEdge !== this.edge) {
|
|
142
149
|
return CONFLICT
|
|
150
|
+
}
|
|
143
151
|
|
|
144
152
|
return current ? this.checkCanPlaceCurrent() : this.checkCanPlaceNoCurrent()
|
|
145
153
|
}
|
|
@@ -150,8 +158,9 @@ class CanPlaceDep {
|
|
|
150
158
|
const { preferDedupe, explicitRequest, current, target, edge, dep } = this
|
|
151
159
|
|
|
152
160
|
if (dep.matches(current)) {
|
|
153
|
-
if (current.satisfies(edge) || this.edgeOverride)
|
|
161
|
+
if (current.satisfies(edge) || this.edgeOverride) {
|
|
154
162
|
return explicitRequest ? REPLACE : KEEP
|
|
163
|
+
}
|
|
155
164
|
}
|
|
156
165
|
|
|
157
166
|
const { version: curVer } = current
|
|
@@ -163,19 +172,22 @@ class CanPlaceDep {
|
|
|
163
172
|
* but it is theoretically possible if peer deps are pinned. In
|
|
164
173
|
* that case we treat it like any other conflict, and keep trying */
|
|
165
174
|
const cpp = this.canPlacePeers(REPLACE)
|
|
166
|
-
if (cpp !== CONFLICT)
|
|
175
|
+
if (cpp !== CONFLICT) {
|
|
167
176
|
return cpp
|
|
177
|
+
}
|
|
168
178
|
}
|
|
169
179
|
|
|
170
180
|
// ok, can't replace the current with new one, but maybe current is ok?
|
|
171
|
-
if (current.satisfies(edge) && (!explicitRequest || preferDedupe))
|
|
181
|
+
if (current.satisfies(edge) && (!explicitRequest || preferDedupe)) {
|
|
172
182
|
return KEEP
|
|
183
|
+
}
|
|
173
184
|
|
|
174
185
|
// if we prefer deduping, then try replacing newer with older
|
|
175
186
|
if (preferDedupe && !tryReplace && dep.canReplace(current)) {
|
|
176
187
|
const cpp = this.canPlacePeers(REPLACE)
|
|
177
|
-
if (cpp !== CONFLICT)
|
|
188
|
+
if (cpp !== CONFLICT) {
|
|
178
189
|
return cpp
|
|
190
|
+
}
|
|
179
191
|
}
|
|
180
192
|
|
|
181
193
|
// Check for interesting cases!
|
|
@@ -185,29 +197,33 @@ class CanPlaceDep {
|
|
|
185
197
|
const myDeepest = this.deepestNestingTarget
|
|
186
198
|
|
|
187
199
|
// ok, i COULD be placed deeper, so leave the current one alone.
|
|
188
|
-
if (target !== myDeepest)
|
|
200
|
+
if (target !== myDeepest) {
|
|
189
201
|
return CONFLICT
|
|
202
|
+
}
|
|
190
203
|
|
|
191
204
|
// if we are not checking a peerDep, then we MUST place it here, in the
|
|
192
205
|
// target that has a non-peer dep on it.
|
|
193
|
-
if (!edge.peer && target === edge.from)
|
|
206
|
+
if (!edge.peer && target === edge.from) {
|
|
194
207
|
return this.canPlacePeers(REPLACE)
|
|
208
|
+
}
|
|
195
209
|
|
|
196
210
|
// if we aren't placing a peer in a set, then we're done here.
|
|
197
211
|
// This is ignored because it SHOULD be redundant, as far as I can tell,
|
|
198
212
|
// with the deepest target and target===edge.from tests. But until we
|
|
199
213
|
// can prove that isn't possible, this condition is here for safety.
|
|
200
214
|
/* istanbul ignore if - allegedly impossible */
|
|
201
|
-
if (!this.parent && !edge.peer)
|
|
215
|
+
if (!this.parent && !edge.peer) {
|
|
202
216
|
return CONFLICT
|
|
217
|
+
}
|
|
203
218
|
|
|
204
219
|
// check the deps in the peer group for each edge into that peer group
|
|
205
220
|
// if ALL of them can be pushed deeper, or if it's ok to replace its
|
|
206
221
|
// members with the contents of the new peer group, then we're good.
|
|
207
222
|
let canReplace = true
|
|
208
223
|
for (const [entryEdge, currentPeers] of peerEntrySets(current)) {
|
|
209
|
-
if (entryEdge === this.edge || entryEdge === this.peerEntryEdge)
|
|
224
|
+
if (entryEdge === this.edge || entryEdge === this.peerEntryEdge) {
|
|
210
225
|
continue
|
|
226
|
+
}
|
|
211
227
|
|
|
212
228
|
// First, see if it's ok to just replace the peerSet entirely.
|
|
213
229
|
// we do this by walking out from the entryEdge, because in a case like
|
|
@@ -231,8 +247,9 @@ class CanPlaceDep {
|
|
|
231
247
|
const entryNode = entryEdge.to
|
|
232
248
|
const entryRep = dep.parent.children.get(entryNode.name)
|
|
233
249
|
if (entryRep) {
|
|
234
|
-
if (entryRep.canReplace(entryNode, dep.parent.children.keys()))
|
|
250
|
+
if (entryRep.canReplace(entryNode, dep.parent.children.keys())) {
|
|
235
251
|
continue
|
|
252
|
+
}
|
|
236
253
|
}
|
|
237
254
|
|
|
238
255
|
let canClobber = !entryRep
|
|
@@ -240,12 +257,14 @@ class CanPlaceDep {
|
|
|
240
257
|
const peerReplacementWalk = new Set([entryNode])
|
|
241
258
|
OUTER: for (const currentPeer of peerReplacementWalk) {
|
|
242
259
|
for (const edge of currentPeer.edgesOut.values()) {
|
|
243
|
-
if (!edge.peer || !edge.valid)
|
|
260
|
+
if (!edge.peer || !edge.valid) {
|
|
244
261
|
continue
|
|
262
|
+
}
|
|
245
263
|
const rep = dep.parent.children.get(edge.name)
|
|
246
264
|
if (!rep) {
|
|
247
|
-
if (edge.to)
|
|
265
|
+
if (edge.to) {
|
|
248
266
|
peerReplacementWalk.add(edge.to)
|
|
267
|
+
}
|
|
249
268
|
continue
|
|
250
269
|
}
|
|
251
270
|
if (!rep.satisfies(edge)) {
|
|
@@ -255,14 +274,16 @@ class CanPlaceDep {
|
|
|
255
274
|
}
|
|
256
275
|
}
|
|
257
276
|
}
|
|
258
|
-
if (canClobber)
|
|
277
|
+
if (canClobber) {
|
|
259
278
|
continue
|
|
279
|
+
}
|
|
260
280
|
|
|
261
281
|
// ok, we can't replace, but maybe we can nest the current set deeper?
|
|
262
282
|
let canNestCurrent = true
|
|
263
283
|
for (const currentPeer of currentPeers) {
|
|
264
|
-
if (!canNestCurrent)
|
|
284
|
+
if (!canNestCurrent) {
|
|
265
285
|
break
|
|
286
|
+
}
|
|
266
287
|
|
|
267
288
|
// still possible to nest this peerSet
|
|
268
289
|
const curDeep = deepestNestingTarget(entryEdge.from, currentPeer.name)
|
|
@@ -270,14 +291,16 @@ class CanPlaceDep {
|
|
|
270
291
|
canNestCurrent = false
|
|
271
292
|
canReplace = false
|
|
272
293
|
}
|
|
273
|
-
if (canNestCurrent)
|
|
294
|
+
if (canNestCurrent) {
|
|
274
295
|
continue
|
|
296
|
+
}
|
|
275
297
|
}
|
|
276
298
|
}
|
|
277
299
|
|
|
278
300
|
// if we can nest or replace all the current peer groups, we can replace.
|
|
279
|
-
if (canReplace)
|
|
301
|
+
if (canReplace) {
|
|
280
302
|
return this.canPlacePeers(REPLACE)
|
|
303
|
+
}
|
|
281
304
|
|
|
282
305
|
return CONFLICT
|
|
283
306
|
}
|
|
@@ -293,8 +316,9 @@ class CanPlaceDep {
|
|
|
293
316
|
if (current) {
|
|
294
317
|
for (const edge of current.edgesIn.values()) {
|
|
295
318
|
if (edge.from.isDescendantOf(target) && edge.valid) {
|
|
296
|
-
if (!dep.satisfies(edge))
|
|
319
|
+
if (!dep.satisfies(edge)) {
|
|
297
320
|
return CONFLICT
|
|
321
|
+
}
|
|
298
322
|
}
|
|
299
323
|
}
|
|
300
324
|
}
|
|
@@ -316,8 +340,9 @@ class CanPlaceDep {
|
|
|
316
340
|
get allChildren () {
|
|
317
341
|
const set = new Set(this.children)
|
|
318
342
|
for (const child of set) {
|
|
319
|
-
for (const grandchild of child.children)
|
|
343
|
+
for (const grandchild of child.children) {
|
|
320
344
|
set.add(grandchild)
|
|
345
|
+
}
|
|
321
346
|
}
|
|
322
347
|
return [...set]
|
|
323
348
|
}
|
|
@@ -329,15 +354,17 @@ class CanPlaceDep {
|
|
|
329
354
|
// check if peers can go here. returns state or CONFLICT
|
|
330
355
|
canPlacePeers (state) {
|
|
331
356
|
this.canPlaceSelf = state
|
|
332
|
-
if (this._canPlacePeers)
|
|
357
|
+
if (this._canPlacePeers) {
|
|
333
358
|
return this._canPlacePeers
|
|
359
|
+
}
|
|
334
360
|
|
|
335
361
|
// TODO: represent peerPath in ERESOLVE error somehow?
|
|
336
362
|
const peerPath = [...this.peerPath, this.dep]
|
|
337
363
|
let sawConflict = false
|
|
338
364
|
for (const peerEdge of this.dep.edgesOut.values()) {
|
|
339
|
-
if (!peerEdge.peer || !peerEdge.to || peerPath.includes(peerEdge.to))
|
|
365
|
+
if (!peerEdge.peer || !peerEdge.to || peerPath.includes(peerEdge.to)) {
|
|
340
366
|
continue
|
|
367
|
+
}
|
|
341
368
|
const peer = peerEdge.to
|
|
342
369
|
// it may be the case that the *initial* dep can be nested, but a peer
|
|
343
370
|
// of that dep needs to be placed shallower, because the target has
|
|
@@ -354,13 +381,15 @@ class CanPlaceDep {
|
|
|
354
381
|
})
|
|
355
382
|
/* istanbul ignore next */
|
|
356
383
|
debug(() => {
|
|
357
|
-
if (this.children.some(c => c.dep === cpp.dep))
|
|
384
|
+
if (this.children.some(c => c.dep === cpp.dep)) {
|
|
358
385
|
throw new Error('checking same dep repeatedly')
|
|
386
|
+
}
|
|
359
387
|
})
|
|
360
388
|
this.children.push(cpp)
|
|
361
389
|
|
|
362
|
-
if (cpp.canPlace === CONFLICT)
|
|
390
|
+
if (cpp.canPlace === CONFLICT) {
|
|
363
391
|
sawConflict = true
|
|
392
|
+
}
|
|
364
393
|
}
|
|
365
394
|
|
|
366
395
|
this._canPlacePeers = sawConflict ? CONFLICT : state
|
|
@@ -10,8 +10,9 @@ module.exports = class Map extends OGMap {
|
|
|
10
10
|
constructor (items = []) {
|
|
11
11
|
super()
|
|
12
12
|
this[_keys] = new OGMap()
|
|
13
|
-
for (const [key, val] of items)
|
|
13
|
+
for (const [key, val] of items) {
|
|
14
14
|
this.set(key, val)
|
|
15
|
+
}
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
[_normKey] (key) {
|
|
@@ -26,8 +27,9 @@ module.exports = class Map extends OGMap {
|
|
|
26
27
|
|
|
27
28
|
set (key, val) {
|
|
28
29
|
const normKey = this[_normKey](key)
|
|
29
|
-
if (this[_keys].has(normKey))
|
|
30
|
+
if (this[_keys].has(normKey)) {
|
|
30
31
|
super.delete(this[_keys].get(normKey))
|
|
32
|
+
}
|
|
31
33
|
this[_keys].set(normKey, key)
|
|
32
34
|
return super.set(key, val)
|
|
33
35
|
}
|
|
@@ -5,8 +5,9 @@
|
|
|
5
5
|
const npa = require('npm-package-arg')
|
|
6
6
|
const relpath = require('./relpath.js')
|
|
7
7
|
const consistentResolve = (resolved, fromPath, toPath, relPaths = false) => {
|
|
8
|
-
if (!resolved)
|
|
8
|
+
if (!resolved) {
|
|
9
9
|
return null
|
|
10
|
+
}
|
|
10
11
|
|
|
11
12
|
try {
|
|
12
13
|
const hostedOpt = { noCommittish: false }
|
|
@@ -5,11 +5,13 @@
|
|
|
5
5
|
const deepestNestingTarget = (start, name) => {
|
|
6
6
|
for (const target of start.ancestry()) {
|
|
7
7
|
// note: this will skip past the first target if edge is peer
|
|
8
|
-
if (target.isProjectRoot || !target.resolveParent || target.globalTop)
|
|
8
|
+
if (target.isProjectRoot || !target.resolveParent || target.globalTop) {
|
|
9
9
|
return target
|
|
10
|
+
}
|
|
10
11
|
const targetEdge = target.edgesOut.get(name)
|
|
11
|
-
if (!targetEdge || !targetEdge.peer)
|
|
12
|
+
if (!targetEdge || !targetEdge.peer) {
|
|
12
13
|
return target
|
|
14
|
+
}
|
|
13
15
|
}
|
|
14
16
|
}
|
|
15
17
|
|
package/lib/dep-valid.js
CHANGED
|
@@ -44,8 +44,9 @@ const depValid = (child, requested, requestor) => {
|
|
|
44
44
|
|
|
45
45
|
switch (requested.type) {
|
|
46
46
|
case 'range':
|
|
47
|
-
if (requested.fetchSpec === '*')
|
|
47
|
+
if (requested.fetchSpec === '*') {
|
|
48
48
|
return true
|
|
49
|
+
}
|
|
49
50
|
// fallthrough
|
|
50
51
|
case 'version':
|
|
51
52
|
// if it's a version or a range other than '*', semver it
|
|
@@ -108,17 +109,20 @@ const depValid = (child, requested, requestor) => {
|
|
|
108
109
|
}
|
|
109
110
|
|
|
110
111
|
const tarballValid = (child, requested, requestor) => {
|
|
111
|
-
if (child.isLink)
|
|
112
|
+
if (child.isLink) {
|
|
112
113
|
return false
|
|
114
|
+
}
|
|
113
115
|
|
|
114
|
-
if (child.resolved)
|
|
116
|
+
if (child.resolved) {
|
|
115
117
|
return child.resolved.replace(/\\/g, '/') === `file:${requested.fetchSpec.replace(/\\/g, '/')}`
|
|
118
|
+
}
|
|
116
119
|
|
|
117
120
|
// if we have a legacy mutated package.json file. we can't be 100%
|
|
118
121
|
// sure that it resolved to the same file, but if it was the same
|
|
119
122
|
// request, that's a pretty good indicator of sameness.
|
|
120
|
-
if (child.package._requested)
|
|
123
|
+
if (child.package._requested) {
|
|
121
124
|
return child.package._requested.saveSpec === requested.saveSpec
|
|
125
|
+
}
|
|
122
126
|
|
|
123
127
|
// ok, we're probably dealing with some legacy cruft here, not much
|
|
124
128
|
// we can do at this point unfortunately.
|