@npmcli/arborist 3.0.0 → 4.0.3
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/shrinkwrap.js +1 -1
- package/lib/arborist/build-ideal-tree.js +25 -12
- package/lib/arborist/index.js +20 -0
- package/lib/arborist/load-actual.js +4 -5
- package/lib/arborist/load-virtual.js +4 -1
- package/lib/arborist/pruner.js +15 -0
- package/lib/arborist/rebuild.js +9 -2
- package/lib/arborist/reify.js +23 -12
- package/lib/can-place-dep.js +10 -5
- package/lib/edge.js +2 -2
- package/lib/peer-entry-sets.js +1 -1
- package/lib/place-dep.js +8 -8
- package/lib/printable.js +4 -4
- package/lib/shrinkwrap.js +60 -15
- package/package.json +7 -8
package/bin/shrinkwrap.js
CHANGED
|
@@ -5,7 +5,7 @@ require('./lib/timers.js')
|
|
|
5
5
|
|
|
6
6
|
const { quiet } = options
|
|
7
7
|
Shrinkwrap.load(options)
|
|
8
|
-
.then(s => quiet || console.log(JSON.stringify(s.
|
|
8
|
+
.then(s => quiet || console.log(JSON.stringify(s.commit(), 0, 2)))
|
|
9
9
|
.catch(er => {
|
|
10
10
|
console.error('shrinkwrap load failure', er)
|
|
11
11
|
process.exit(1)
|
|
@@ -26,6 +26,7 @@ const debug = require('../debug.js')
|
|
|
26
26
|
const fromPath = require('../from-path.js')
|
|
27
27
|
const calcDepFlags = require('../calc-dep-flags.js')
|
|
28
28
|
const Shrinkwrap = require('../shrinkwrap.js')
|
|
29
|
+
const { defaultLockfileVersion } = Shrinkwrap
|
|
29
30
|
const Node = require('../node.js')
|
|
30
31
|
const Link = require('../link.js')
|
|
31
32
|
const addRmPkgDeps = require('../add-rm-pkg-deps.js')
|
|
@@ -307,8 +308,10 @@ module.exports = cls => class IdealTreeBuilder extends cls {
|
|
|
307
308
|
// reconstructing it anyway.
|
|
308
309
|
.then(root => this[_global] ? root
|
|
309
310
|
: !this[_usePackageLock] || this[_updateAll]
|
|
310
|
-
? Shrinkwrap.reset({
|
|
311
|
-
|
|
311
|
+
? Shrinkwrap.reset({
|
|
312
|
+
path: this.path,
|
|
313
|
+
lockfileVersion: this.options.lockfileVersion,
|
|
314
|
+
}).then(meta => Object.assign(root, {meta}))
|
|
312
315
|
: this.loadVirtual({ root }))
|
|
313
316
|
|
|
314
317
|
// if we don't have a lockfile to go from, then start with the
|
|
@@ -326,6 +329,9 @@ module.exports = cls => class IdealTreeBuilder extends cls {
|
|
|
326
329
|
// dep flags before assuming that any mutations were reflected.
|
|
327
330
|
if (tree.children.size) {
|
|
328
331
|
root.meta.loadedFromDisk = true
|
|
332
|
+
// set these so that we don't try to ancient lockfile reload it
|
|
333
|
+
root.meta.originalLockfileVersion = defaultLockfileVersion
|
|
334
|
+
root.meta.lockfileVersion = defaultLockfileVersion
|
|
329
335
|
}
|
|
330
336
|
}
|
|
331
337
|
return root
|
|
@@ -345,7 +351,10 @@ module.exports = cls => class IdealTreeBuilder extends cls {
|
|
|
345
351
|
// this is a gross kludge to handle the fact that we don't save
|
|
346
352
|
// metadata on the root node in global installs, because the "root"
|
|
347
353
|
// node is something like /usr/local/lib.
|
|
348
|
-
const meta = new Shrinkwrap({
|
|
354
|
+
const meta = new Shrinkwrap({
|
|
355
|
+
path: this.path,
|
|
356
|
+
lockfileVersion: this.options.lockfileVersion,
|
|
357
|
+
})
|
|
349
358
|
meta.reset()
|
|
350
359
|
root.meta = meta
|
|
351
360
|
return root
|
|
@@ -696,14 +705,18 @@ module.exports = cls => class IdealTreeBuilder extends cls {
|
|
|
696
705
|
// least it's just a one-time hit.
|
|
697
706
|
process.emit('time', 'idealTree:inflate')
|
|
698
707
|
|
|
708
|
+
// don't warn if we're not gonna actually write it back anyway.
|
|
699
709
|
const heading = ancient ? 'ancient lockfile' : 'old lockfile'
|
|
700
|
-
this.
|
|
701
|
-
|
|
710
|
+
if (ancient || !this.options.lockfileVersion ||
|
|
711
|
+
this.options.lockfileVersion >= defaultLockfileVersion) {
|
|
712
|
+
this.log.warn(heading,
|
|
713
|
+
`
|
|
702
714
|
The ${meta.type} file was created with an old version of npm,
|
|
703
715
|
so supplemental metadata must be fetched from the registry.
|
|
704
716
|
|
|
705
717
|
This is a one-time fix-up, please be patient...
|
|
706
718
|
`)
|
|
719
|
+
}
|
|
707
720
|
|
|
708
721
|
this.addTracker('idealTree:inflate')
|
|
709
722
|
const queue = []
|
|
@@ -748,7 +761,7 @@ This is a one-time fix-up, please be patient...
|
|
|
748
761
|
// yes, yes, this isn't the "original" version, but now that it's been
|
|
749
762
|
// upgraded, we need to make sure we don't do the work to upgrade it
|
|
750
763
|
// again, since it's now as new as can be.
|
|
751
|
-
meta.originalLockfileVersion =
|
|
764
|
+
meta.originalLockfileVersion = defaultLockfileVersion
|
|
752
765
|
this.finishTracker('idealTree:inflate')
|
|
753
766
|
process.emit('timeEnd', 'idealTree:inflate')
|
|
754
767
|
}
|
|
@@ -880,7 +893,7 @@ This is a one-time fix-up, please be patient...
|
|
|
880
893
|
const tasks = []
|
|
881
894
|
const peerSource = this[_peerSetSource].get(node) || node
|
|
882
895
|
for (const edge of this[_problemEdges](node)) {
|
|
883
|
-
if (edge.
|
|
896
|
+
if (edge.peerConflicted) {
|
|
884
897
|
continue
|
|
885
898
|
}
|
|
886
899
|
|
|
@@ -967,8 +980,8 @@ This is a one-time fix-up, please be patient...
|
|
|
967
980
|
if (edgeIn === edge) {
|
|
968
981
|
continue
|
|
969
982
|
}
|
|
970
|
-
const { from, valid,
|
|
971
|
-
if (!
|
|
983
|
+
const { from, valid, peerConflicted } = edgeIn
|
|
984
|
+
if (!peerConflicted && !valid && !this[_depsSeen].has(from)) {
|
|
972
985
|
this.addTracker('idealTree', from.name, from.location)
|
|
973
986
|
this[_depsQueue].push(edgeIn.from)
|
|
974
987
|
}
|
|
@@ -984,8 +997,8 @@ This is a one-time fix-up, please be patient...
|
|
|
984
997
|
continue
|
|
985
998
|
}
|
|
986
999
|
|
|
987
|
-
const { valid,
|
|
988
|
-
if (!valid && !
|
|
1000
|
+
const { valid, peerConflicted } = edgeIn
|
|
1001
|
+
if (!valid && !peerConflicted) {
|
|
989
1002
|
// if it's already been visited, we have to re-visit
|
|
990
1003
|
// otherwise, just enqueue normally.
|
|
991
1004
|
this[_depsSeen].delete(edgeIn.from)
|
|
@@ -1301,7 +1314,7 @@ This is a one-time fix-up, please be patient...
|
|
|
1301
1314
|
// that will be installed by default anyway, and we'll fail when
|
|
1302
1315
|
// we get to the point where we need to, if we need to.
|
|
1303
1316
|
if (conflictOK || !required.has(dep)) {
|
|
1304
|
-
edge.
|
|
1317
|
+
edge.peerConflicted = true
|
|
1305
1318
|
continue
|
|
1306
1319
|
}
|
|
1307
1320
|
|
package/lib/arborist/index.js
CHANGED
|
@@ -44,9 +44,25 @@ const mixins = [
|
|
|
44
44
|
require('./reify.js'),
|
|
45
45
|
]
|
|
46
46
|
|
|
47
|
+
const _workspacesEnabled = Symbol.for('workspacesEnabled')
|
|
47
48
|
const Base = mixins.reduce((a, b) => b(a), require('events'))
|
|
48
49
|
const getWorkspaceNodes = require('../get-workspace-nodes.js')
|
|
49
50
|
|
|
51
|
+
// if it's 1, 2, or 3, set it explicitly that.
|
|
52
|
+
// if undefined or null, set it null
|
|
53
|
+
// otherwise, throw.
|
|
54
|
+
const lockfileVersion = lfv => {
|
|
55
|
+
if (lfv === 1 || lfv === 2 || lfv === 3) {
|
|
56
|
+
return lfv
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (lfv === undefined || lfv === null) {
|
|
60
|
+
return null
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
throw new TypeError('Invalid lockfileVersion config: ' + lfv)
|
|
64
|
+
}
|
|
65
|
+
|
|
50
66
|
class Arborist extends Base {
|
|
51
67
|
constructor (options = {}) {
|
|
52
68
|
process.emit('time', 'arborist:ctor')
|
|
@@ -59,7 +75,11 @@ class Arborist extends Base {
|
|
|
59
75
|
packumentCache: options.packumentCache || new Map(),
|
|
60
76
|
log: options.log || procLog,
|
|
61
77
|
workspacesEnabled: options.workspacesEnabled !== false,
|
|
78
|
+
lockfileVersion: lockfileVersion(options.lockfileVersion),
|
|
62
79
|
}
|
|
80
|
+
|
|
81
|
+
this[_workspacesEnabled] = this.options.workspacesEnabled
|
|
82
|
+
|
|
63
83
|
if (options.saveType && !saveTypeMap.get(options.saveType)) {
|
|
64
84
|
throw new Error(`Invalid saveType ${options.saveType}`)
|
|
65
85
|
}
|
|
@@ -137,11 +137,9 @@ module.exports = cls => class ActualLoader extends cls {
|
|
|
137
137
|
real: await realpath(this.path, this[_rpcache], this[_stcache]),
|
|
138
138
|
})
|
|
139
139
|
|
|
140
|
-
//
|
|
141
|
-
//
|
|
142
|
-
//
|
|
143
|
-
// user switches back and forth between Arborist and another way of
|
|
144
|
-
// mutating the node_modules folder.
|
|
140
|
+
// Note: hidden lockfile will be rejected if it's not the latest thing
|
|
141
|
+
// in the folder, or if any of the entries in the hidden lockfile are
|
|
142
|
+
// missing.
|
|
145
143
|
const meta = await Shrinkwrap.load({
|
|
146
144
|
path: this[_actualTree].path,
|
|
147
145
|
hiddenLockfile: true,
|
|
@@ -152,6 +150,7 @@ module.exports = cls => class ActualLoader extends cls {
|
|
|
152
150
|
} else {
|
|
153
151
|
const meta = await Shrinkwrap.load({
|
|
154
152
|
path: this[_actualTree].path,
|
|
153
|
+
lockfileVersion: this.options.lockfileVersion,
|
|
155
154
|
})
|
|
156
155
|
this[_actualTree].meta = meta
|
|
157
156
|
return this[_loadActualActually]({ root, ignoreMissing })
|
|
@@ -54,7 +54,10 @@ module.exports = cls => class VirtualLoader extends cls {
|
|
|
54
54
|
return treeCheck(this.virtualTree)
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
const s = await Shrinkwrap.load({
|
|
57
|
+
const s = await Shrinkwrap.load({
|
|
58
|
+
path: this.path,
|
|
59
|
+
lockfileVersion: this.options.lockfileVersion,
|
|
60
|
+
})
|
|
58
61
|
if (!s.loadedFromDisk && !options.root) {
|
|
59
62
|
const er = new Error('loadVirtual requires existing shrinkwrap file')
|
|
60
63
|
throw Object.assign(er, { code: 'ENOLOCK' })
|
package/lib/arborist/pruner.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
const _idealTreePrune = Symbol.for('idealTreePrune')
|
|
2
|
+
const _workspacesEnabled = Symbol.for('workspacesEnabled')
|
|
3
|
+
const _addNodeToTrashList = Symbol.for('addNodeToTrashList')
|
|
2
4
|
|
|
3
5
|
module.exports = cls => class Pruner extends cls {
|
|
4
6
|
async prune (options = {}) {
|
|
@@ -10,6 +12,19 @@ module.exports = cls => class Pruner extends cls {
|
|
|
10
12
|
|
|
11
13
|
this[_idealTreePrune]()
|
|
12
14
|
|
|
15
|
+
if (!this[_workspacesEnabled]) {
|
|
16
|
+
const excludeNodes = this.excludeWorkspacesDependencySet(this.idealTree)
|
|
17
|
+
for (const node of this.idealTree.inventory.values()) {
|
|
18
|
+
if (
|
|
19
|
+
node.parent !== null
|
|
20
|
+
&& !node.isProjectRoot
|
|
21
|
+
&& !excludeNodes.has(node)
|
|
22
|
+
) {
|
|
23
|
+
this[_addNodeToTrashList](node)
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
13
28
|
return this.reify(options)
|
|
14
29
|
}
|
|
15
30
|
}
|
package/lib/arborist/rebuild.js
CHANGED
|
@@ -35,6 +35,7 @@ const _checkBins = Symbol.for('checkBins')
|
|
|
35
35
|
const _queues = Symbol('queues')
|
|
36
36
|
const _scriptShell = Symbol('scriptShell')
|
|
37
37
|
const _includeWorkspaceRoot = Symbol.for('includeWorkspaceRoot')
|
|
38
|
+
const _workspacesEnabled = Symbol.for('workspacesEnabled')
|
|
38
39
|
|
|
39
40
|
const _force = Symbol.for('force')
|
|
40
41
|
|
|
@@ -77,8 +78,14 @@ module.exports = cls => class Builder extends cls {
|
|
|
77
78
|
// the actual tree on disk.
|
|
78
79
|
if (!nodes) {
|
|
79
80
|
const tree = await this.loadActual()
|
|
80
|
-
|
|
81
|
-
|
|
81
|
+
let filterSet
|
|
82
|
+
if (!this[_workspacesEnabled]) {
|
|
83
|
+
filterSet = this.excludeWorkspacesDependencySet(tree)
|
|
84
|
+
nodes = tree.inventory.filter(node =>
|
|
85
|
+
filterSet.has(node) || node.isProjectRoot
|
|
86
|
+
)
|
|
87
|
+
} else if (this[_workspaces] && this[_workspaces].length) {
|
|
88
|
+
filterSet = this.workspaceDependencySet(
|
|
82
89
|
tree,
|
|
83
90
|
this[_workspaces],
|
|
84
91
|
this[_includeWorkspaceRoot]
|
package/lib/arborist/reify.js
CHANGED
|
@@ -40,8 +40,9 @@ const _savePrefix = Symbol('savePrefix')
|
|
|
40
40
|
const _retireShallowNodes = Symbol.for('retireShallowNodes')
|
|
41
41
|
const _getBundlesByDepth = Symbol('getBundlesByDepth')
|
|
42
42
|
const _registryResolved = Symbol('registryResolved')
|
|
43
|
-
const _addNodeToTrashList = Symbol('addNodeToTrashList')
|
|
43
|
+
const _addNodeToTrashList = Symbol.for('addNodeToTrashList')
|
|
44
44
|
const _workspaces = Symbol.for('workspaces')
|
|
45
|
+
const _workspacesEnabled = Symbol.for('workspacesEnabled')
|
|
45
46
|
|
|
46
47
|
// shared by rebuild mixin
|
|
47
48
|
const _trashList = Symbol.for('trashList')
|
|
@@ -314,6 +315,10 @@ module.exports = cls => class Reifier extends cls {
|
|
|
314
315
|
// to just invalidate the parts that changed, but avoid walking the
|
|
315
316
|
// whole tree again.
|
|
316
317
|
|
|
318
|
+
const includeWorkspaces = this[_workspacesEnabled]
|
|
319
|
+
const includeRootDeps = !this[_workspacesEnabled]
|
|
320
|
+
|| this[_includeWorkspaceRoot] && this[_workspaces].length > 0
|
|
321
|
+
|
|
317
322
|
const filterNodes = []
|
|
318
323
|
if (this[_global] && this.explicitRequests.size) {
|
|
319
324
|
const idealTree = this.idealTree.target
|
|
@@ -331,17 +336,21 @@ module.exports = cls => class Reifier extends cls {
|
|
|
331
336
|
}
|
|
332
337
|
}
|
|
333
338
|
} else {
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
339
|
+
if (includeWorkspaces) {
|
|
340
|
+
// add all ws nodes to filterNodes
|
|
341
|
+
for (const ws of this[_workspaces]) {
|
|
342
|
+
const ideal = this.idealTree.children.get(ws)
|
|
343
|
+
if (ideal) {
|
|
344
|
+
filterNodes.push(ideal)
|
|
345
|
+
}
|
|
346
|
+
const actual = this.actualTree.children.get(ws)
|
|
347
|
+
if (actual) {
|
|
348
|
+
filterNodes.push(actual)
|
|
349
|
+
}
|
|
342
350
|
}
|
|
343
351
|
}
|
|
344
|
-
if (
|
|
352
|
+
if (includeRootDeps) {
|
|
353
|
+
// add all non-workspace nodes to filterNodes
|
|
345
354
|
for (const tree of [this.idealTree, this.actualTree]) {
|
|
346
355
|
for (const {type, to} of tree.edgesOut.values()) {
|
|
347
356
|
if (type !== 'workspace' && to) {
|
|
@@ -451,10 +460,12 @@ module.exports = cls => class Reifier extends cls {
|
|
|
451
460
|
|
|
452
461
|
const filter = node =>
|
|
453
462
|
node.top.isProjectRoot &&
|
|
454
|
-
(
|
|
463
|
+
(
|
|
464
|
+
node.peer && this[_omitPeer] ||
|
|
455
465
|
node.dev && this[_omitDev] ||
|
|
456
466
|
node.optional && this[_omitOptional] ||
|
|
457
|
-
node.devOptional && this[_omitOptional] && this[_omitDev]
|
|
467
|
+
node.devOptional && this[_omitOptional] && this[_omitDev]
|
|
468
|
+
)
|
|
458
469
|
|
|
459
470
|
for (const node of this.idealTree.inventory.filter(filter)) {
|
|
460
471
|
this[_addNodeToTrashList](node)
|
package/lib/can-place-dep.js
CHANGED
|
@@ -145,7 +145,12 @@ class CanPlaceDep {
|
|
|
145
145
|
return CONFLICT
|
|
146
146
|
}
|
|
147
147
|
|
|
148
|
-
if
|
|
148
|
+
// skip this test if there's a current node, because we might be able
|
|
149
|
+
// to dedupe against it anyway
|
|
150
|
+
if (!current &&
|
|
151
|
+
targetEdge &&
|
|
152
|
+
!dep.satisfies(targetEdge) &&
|
|
153
|
+
targetEdge !== this.edge) {
|
|
149
154
|
return CONFLICT
|
|
150
155
|
}
|
|
151
156
|
|
|
@@ -167,10 +172,10 @@ class CanPlaceDep {
|
|
|
167
172
|
const { version: newVer } = dep
|
|
168
173
|
const tryReplace = curVer && newVer && semver.gte(newVer, curVer)
|
|
169
174
|
if (tryReplace && dep.canReplace(current)) {
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
175
|
+
// It's extremely rare that a replaceable node would be a conflict, if
|
|
176
|
+
// the current one wasn't a conflict, but it is theoretically possible
|
|
177
|
+
// if peer deps are pinned. In that case we treat it like any other
|
|
178
|
+
// conflict, and keep trying.
|
|
174
179
|
const cpp = this.canPlacePeers(REPLACE)
|
|
175
180
|
if (cpp !== CONFLICT) {
|
|
176
181
|
return cpp
|
package/lib/edge.js
CHANGED
|
@@ -37,7 +37,7 @@ const printableEdge = (edge) => {
|
|
|
37
37
|
...(edgeFrom != null ? { from: edgeFrom } : {}),
|
|
38
38
|
...(edgeTo ? { to: edgeTo } : {}),
|
|
39
39
|
...(edge.error ? { error: edge.error } : {}),
|
|
40
|
-
...(edge.
|
|
40
|
+
...(edge.peerConflicted ? { peerConflicted: true } : {}),
|
|
41
41
|
})
|
|
42
42
|
}
|
|
43
43
|
|
|
@@ -78,7 +78,7 @@ class Edge {
|
|
|
78
78
|
}
|
|
79
79
|
this[_setFrom](from)
|
|
80
80
|
this[_error] = this[_loadError]()
|
|
81
|
-
this.
|
|
81
|
+
this.peerConflicted = false
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
satisfiedBy (node) {
|
package/lib/peer-entry-sets.js
CHANGED
|
@@ -29,7 +29,7 @@ const peerEntrySets = node => {
|
|
|
29
29
|
for (const peer of unionSet) {
|
|
30
30
|
for (const edge of peer.edgesIn) {
|
|
31
31
|
// if not valid, it doesn't matter anyway. either it's been previously
|
|
32
|
-
//
|
|
32
|
+
// peerConflicted, or it's the thing we're interested in replacing.
|
|
33
33
|
if (!edge.valid) {
|
|
34
34
|
continue
|
|
35
35
|
}
|
package/lib/place-dep.js
CHANGED
|
@@ -247,7 +247,7 @@ class PlaceDep {
|
|
|
247
247
|
// if we're placing in the tree with --force, we can get here even though
|
|
248
248
|
// it's a conflict. Treat it as a KEEP, but warn and move on.
|
|
249
249
|
if (placementType === KEEP) {
|
|
250
|
-
// this was
|
|
250
|
+
// this was a peerConflicted peer dep
|
|
251
251
|
if (edge.peer && !edge.valid) {
|
|
252
252
|
this.warnPeerConflict()
|
|
253
253
|
}
|
|
@@ -305,7 +305,7 @@ class PlaceDep {
|
|
|
305
305
|
this.placed.parent = target
|
|
306
306
|
}
|
|
307
307
|
|
|
308
|
-
// if it's
|
|
308
|
+
// if it's a peerConflicted peer dep, warn about it
|
|
309
309
|
if (edge.peer && !this.placed.satisfies(edge)) {
|
|
310
310
|
this.warnPeerConflict()
|
|
311
311
|
}
|
|
@@ -339,7 +339,7 @@ class PlaceDep {
|
|
|
339
339
|
// otherwise they'd be gone and the peer set would change throughout
|
|
340
340
|
// this loop.
|
|
341
341
|
for (const peerEdge of this.placed.edgesOut.values()) {
|
|
342
|
-
if (peerEdge.valid || !peerEdge.peer || peerEdge.
|
|
342
|
+
if (peerEdge.valid || !peerEdge.peer || peerEdge.peerConflicted) {
|
|
343
343
|
continue
|
|
344
344
|
}
|
|
345
345
|
|
|
@@ -353,7 +353,7 @@ class PlaceDep {
|
|
|
353
353
|
continue
|
|
354
354
|
}
|
|
355
355
|
|
|
356
|
-
//
|
|
356
|
+
// peerConflicted peerEdge, just accept what's there already
|
|
357
357
|
if (!peer.satisfies(peerEdge)) {
|
|
358
358
|
continue
|
|
359
359
|
}
|
|
@@ -398,7 +398,7 @@ class PlaceDep {
|
|
|
398
398
|
if (this.placed.satisfies(edge) ||
|
|
399
399
|
!edge.peer ||
|
|
400
400
|
edge.from.parent !== target ||
|
|
401
|
-
edge.
|
|
401
|
+
edge.peerConflicted) {
|
|
402
402
|
// not a peer dep, not invalid, or not from this level, so it's fine
|
|
403
403
|
// to just let it re-evaluate as a problemEdge later, or let it be
|
|
404
404
|
// satisfied by the new dep being placed.
|
|
@@ -406,14 +406,14 @@ class PlaceDep {
|
|
|
406
406
|
}
|
|
407
407
|
for (const entryEdge of peerEntrySets(edge.from).keys()) {
|
|
408
408
|
// either this one needs to be pruned and re-evaluated, or marked
|
|
409
|
-
// as
|
|
409
|
+
// as peerConflicted and warned about. If the entryEdge comes in from
|
|
410
410
|
// the root, then we have to leave it alone, and in that case, it
|
|
411
411
|
// will have already warned or crashed by getting to this point.
|
|
412
412
|
const entryNode = entryEdge.to
|
|
413
413
|
const deepestTarget = deepestNestingTarget(entryNode)
|
|
414
414
|
if (deepestTarget !== target && !entryEdge.from.isRoot) {
|
|
415
415
|
prunePeerSets.push(...gatherDepSet([entryNode], e => {
|
|
416
|
-
return e.to !== entryNode && !e.
|
|
416
|
+
return e.to !== entryNode && !e.peerConflicted
|
|
417
417
|
}))
|
|
418
418
|
} else {
|
|
419
419
|
this.warnPeerConflict(edge, this.dep)
|
|
@@ -532,7 +532,7 @@ class PlaceDep {
|
|
|
532
532
|
warnPeerConflict (edge, dep) {
|
|
533
533
|
edge = edge || this.edge
|
|
534
534
|
dep = dep || this.dep
|
|
535
|
-
edge.
|
|
535
|
+
edge.peerConflicted = true
|
|
536
536
|
const expl = this.explainPeerConflict(edge, dep)
|
|
537
537
|
log.warn('ERESOLVE', 'overriding peer dependency', expl)
|
|
538
538
|
}
|
package/lib/printable.js
CHANGED
|
@@ -130,8 +130,8 @@ class Edge {
|
|
|
130
130
|
if (edge.error) {
|
|
131
131
|
this.error = edge.error
|
|
132
132
|
}
|
|
133
|
-
if (edge.
|
|
134
|
-
this.
|
|
133
|
+
if (edge.peerConflicted) {
|
|
134
|
+
this.peerConflicted = edge.peerConflicted
|
|
135
135
|
}
|
|
136
136
|
}
|
|
137
137
|
}
|
|
@@ -149,7 +149,7 @@ class EdgeOut extends Edge {
|
|
|
149
149
|
}${
|
|
150
150
|
this.error ? ' ' + this.error : ''
|
|
151
151
|
}${
|
|
152
|
-
this.
|
|
152
|
+
this.peerConflicted ? ' peerConflicted' : ''
|
|
153
153
|
} }`
|
|
154
154
|
}
|
|
155
155
|
}
|
|
@@ -165,7 +165,7 @@ class EdgeIn extends Edge {
|
|
|
165
165
|
return `{ ${this.from || '""'} ${this.type} ${this.name}@${this.spec}${
|
|
166
166
|
this.error ? ' ' + this.error : ''
|
|
167
167
|
}${
|
|
168
|
-
this.
|
|
168
|
+
this.peerConflicted ? ' peerConflicted' : ''
|
|
169
169
|
} }`
|
|
170
170
|
}
|
|
171
171
|
}
|
package/lib/shrinkwrap.js
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
// definitely not before npm v8.
|
|
11
11
|
|
|
12
12
|
const localeCompare = require('@isaacs/string-locale-compare')('en')
|
|
13
|
-
const
|
|
13
|
+
const defaultLockfileVersion = 2
|
|
14
14
|
|
|
15
15
|
// for comparing nodes to yarn.lock entries
|
|
16
16
|
const mismatch = (a, b) => a && b && a !== b
|
|
@@ -226,6 +226,10 @@ const _filenameSet = Symbol('_filenameSet')
|
|
|
226
226
|
const _maybeRead = Symbol('_maybeRead')
|
|
227
227
|
const _maybeStat = Symbol('_maybeStat')
|
|
228
228
|
class Shrinkwrap {
|
|
229
|
+
static get defaultLockfileVersion () {
|
|
230
|
+
return defaultLockfileVersion
|
|
231
|
+
}
|
|
232
|
+
|
|
229
233
|
static load (options) {
|
|
230
234
|
return new Shrinkwrap(options).load()
|
|
231
235
|
}
|
|
@@ -234,21 +238,31 @@ class Shrinkwrap {
|
|
|
234
238
|
return swKeyOrder
|
|
235
239
|
}
|
|
236
240
|
|
|
237
|
-
static reset (options) {
|
|
241
|
+
static async reset (options) {
|
|
238
242
|
// still need to know if it was loaded from the disk, but don't
|
|
239
243
|
// bother reading it if we're gonna just throw it away.
|
|
240
244
|
const s = new Shrinkwrap(options)
|
|
241
245
|
s.reset()
|
|
242
246
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
247
|
+
const [sw, lock] = await s[_maybeStat]()
|
|
248
|
+
|
|
249
|
+
s.filename = resolve(s.path,
|
|
250
|
+
(s.hiddenLockfile ? 'node_modules/.package-lock'
|
|
251
|
+
: s.shrinkwrapOnly || sw ? 'npm-shrinkwrap'
|
|
252
|
+
: 'package-lock') + '.json')
|
|
253
|
+
s.loadedFromDisk = !!(sw || lock)
|
|
254
|
+
s.type = basename(s.filename)
|
|
255
|
+
|
|
256
|
+
try {
|
|
257
|
+
if (s.loadedFromDisk && !s.lockfileVersion) {
|
|
258
|
+
const json = parseJSON(await maybeReadFile(s.filename))
|
|
259
|
+
if (json.lockfileVersion > defaultLockfileVersion) {
|
|
260
|
+
s.lockfileVersion = json.lockfileVersion
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
} catch (e) {}
|
|
264
|
+
|
|
265
|
+
return s
|
|
252
266
|
}
|
|
253
267
|
|
|
254
268
|
static metaFromNode (node, path) {
|
|
@@ -316,8 +330,12 @@ class Shrinkwrap {
|
|
|
316
330
|
shrinkwrapOnly = false,
|
|
317
331
|
hiddenLockfile = false,
|
|
318
332
|
log = procLog,
|
|
333
|
+
lockfileVersion,
|
|
319
334
|
} = options
|
|
320
335
|
|
|
336
|
+
this.lockfileVersion = hiddenLockfile ? 3
|
|
337
|
+
: lockfileVersion ? parseInt(lockfileVersion, 10)
|
|
338
|
+
: null
|
|
321
339
|
this.log = log
|
|
322
340
|
this[_awaitingUpdate] = new Map()
|
|
323
341
|
this.tree = null
|
|
@@ -372,6 +390,7 @@ class Shrinkwrap {
|
|
|
372
390
|
reset () {
|
|
373
391
|
this.tree = null
|
|
374
392
|
this[_awaitingUpdate] = new Map()
|
|
393
|
+
const lockfileVersion = this.lockfileVersion || defaultLockfileVersion
|
|
375
394
|
this.originalLockfileVersion = lockfileVersion
|
|
376
395
|
this.data = {
|
|
377
396
|
lockfileVersion,
|
|
@@ -460,14 +479,23 @@ class Shrinkwrap {
|
|
|
460
479
|
this.ancientLockfile = false
|
|
461
480
|
return {}
|
|
462
481
|
}).then(lock => {
|
|
482
|
+
const lockfileVersion = this.lockfileVersion ? this.lockfileVersion
|
|
483
|
+
: Math.max(lock.lockfileVersion || 0, defaultLockfileVersion)
|
|
463
484
|
this.data = {
|
|
464
485
|
...lock,
|
|
465
|
-
lockfileVersion,
|
|
486
|
+
lockfileVersion: lockfileVersion,
|
|
466
487
|
requires: true,
|
|
467
488
|
packages: lock.packages || {},
|
|
468
|
-
|
|
489
|
+
dependencies: lock.dependencies || {},
|
|
469
490
|
}
|
|
491
|
+
|
|
470
492
|
this.originalLockfileVersion = lock.lockfileVersion
|
|
493
|
+
// use default if it wasn't explicitly set, and the current file is
|
|
494
|
+
// less than our default. otherwise, keep whatever is in the file,
|
|
495
|
+
// unless we had an explicit setting already.
|
|
496
|
+
if (!this.lockfileVersion) {
|
|
497
|
+
this.lockfileVersion = this.data.lockfileVersion = lockfileVersion
|
|
498
|
+
}
|
|
471
499
|
this.ancientLockfile = this.loadedFromDisk &&
|
|
472
500
|
!(lock.lockfileVersion >= 2) && !lock.requires
|
|
473
501
|
|
|
@@ -878,15 +906,32 @@ class Shrinkwrap {
|
|
|
878
906
|
}
|
|
879
907
|
}
|
|
880
908
|
|
|
909
|
+
// if we haven't set it by now, use the default
|
|
910
|
+
if (!this.lockfileVersion) {
|
|
911
|
+
this.lockfileVersion = defaultLockfileVersion
|
|
912
|
+
}
|
|
913
|
+
this.data.lockfileVersion = this.lockfileVersion
|
|
914
|
+
|
|
881
915
|
// hidden lockfiles don't include legacy metadata or a root entry
|
|
882
916
|
if (this.hiddenLockfile) {
|
|
883
917
|
delete this.data.packages['']
|
|
884
918
|
delete this.data.dependencies
|
|
885
|
-
} else if (this.tree) {
|
|
919
|
+
} else if (this.tree && this.lockfileVersion <= 3) {
|
|
886
920
|
this[_buildLegacyLockfile](this.tree, this.data)
|
|
887
921
|
}
|
|
888
922
|
|
|
889
|
-
|
|
923
|
+
// lf version 1 = dependencies only
|
|
924
|
+
// lf version 2 = dependencies and packages
|
|
925
|
+
// lf version 3 = packages only
|
|
926
|
+
if (this.lockfileVersion >= 3) {
|
|
927
|
+
const { dependencies, ...data } = this.data
|
|
928
|
+
return data
|
|
929
|
+
} else if (this.lockfileVersion < 2) {
|
|
930
|
+
const { packages, ...data } = this.data
|
|
931
|
+
return data
|
|
932
|
+
} else {
|
|
933
|
+
return { ...this.data }
|
|
934
|
+
}
|
|
890
935
|
}
|
|
891
936
|
|
|
892
937
|
[_buildLegacyLockfile] (node, lock, path = []) {
|
package/package.json
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@npmcli/arborist",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.3",
|
|
4
4
|
"description": "Manage node_modules trees",
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"@isaacs/string-locale-compare": "^1.0.1",
|
|
7
7
|
"@npmcli/installed-package-contents": "^1.0.7",
|
|
8
|
-
"@npmcli/map-workspaces": "^
|
|
8
|
+
"@npmcli/map-workspaces": "^2.0.0",
|
|
9
9
|
"@npmcli/metavuln-calculator": "^2.0.0",
|
|
10
10
|
"@npmcli/move-file": "^1.1.0",
|
|
11
11
|
"@npmcli/name-from-folder": "^1.0.1",
|
|
12
12
|
"@npmcli/node-gyp": "^1.0.1",
|
|
13
13
|
"@npmcli/package-json": "^1.0.1",
|
|
14
14
|
"@npmcli/run-script": "^2.0.0",
|
|
15
|
-
"bin-links": "^2.
|
|
15
|
+
"bin-links": "^2.3.0",
|
|
16
16
|
"cacache": "^15.0.3",
|
|
17
17
|
"common-ancestor-path": "^1.0.1",
|
|
18
18
|
"json-parse-even-better-errors": "^2.3.1",
|
|
@@ -45,11 +45,10 @@
|
|
|
45
45
|
"tcompare": "^5.0.6"
|
|
46
46
|
},
|
|
47
47
|
"scripts": {
|
|
48
|
-
"test": "
|
|
49
|
-
"
|
|
50
|
-
"posttest": "npm run lint --",
|
|
48
|
+
"test": "tap",
|
|
49
|
+
"posttest": "npm run lint",
|
|
51
50
|
"snap": "tap",
|
|
52
|
-
"postsnap": "npm run lintfix
|
|
51
|
+
"postsnap": "npm run lintfix",
|
|
53
52
|
"test-proxy": "ARBORIST_TEST_PROXY=1 tap --snapshot",
|
|
54
53
|
"preversion": "npm test",
|
|
55
54
|
"postversion": "npm publish",
|
|
@@ -88,7 +87,7 @@
|
|
|
88
87
|
"--no-warnings",
|
|
89
88
|
"--no-deprecation"
|
|
90
89
|
],
|
|
91
|
-
"timeout": "
|
|
90
|
+
"timeout": "360"
|
|
92
91
|
},
|
|
93
92
|
"engines": {
|
|
94
93
|
"node": "^12.13.0 || ^14.15.0 || >=16"
|