@npmcli/arborist 2.7.0 → 2.8.2
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/dedupe.js +46 -0
- package/lib/arborist/build-ideal-tree.js +126 -698
- package/lib/arborist/load-actual.js +4 -2
- package/lib/arborist/reify.js +190 -54
- package/lib/can-place-dep.js +402 -0
- package/lib/case-insensitive-map.js +48 -0
- package/lib/deepest-nesting-target.js +16 -0
- package/lib/edge.js +4 -2
- package/lib/node.js +60 -12
- package/lib/peer-entry-sets.js +72 -0
- package/lib/place-dep.js +551 -0
- package/lib/printable.js +10 -0
- package/lib/shrinkwrap.js +7 -3
- package/package.json +7 -5
- package/lib/peer-set.js +0 -25
|
@@ -3,14 +3,23 @@ const rpj = require('read-package-json-fast')
|
|
|
3
3
|
const npa = require('npm-package-arg')
|
|
4
4
|
const pacote = require('pacote')
|
|
5
5
|
const cacache = require('cacache')
|
|
6
|
-
const semver = require('semver')
|
|
7
6
|
const promiseCallLimit = require('promise-call-limit')
|
|
8
|
-
const getPeerSet = require('../peer-set.js')
|
|
9
7
|
const realpath = require('../../lib/realpath.js')
|
|
10
8
|
const { resolve, dirname } = require('path')
|
|
11
9
|
const { promisify } = require('util')
|
|
12
10
|
const treeCheck = require('../tree-check.js')
|
|
13
11
|
const readdir = promisify(require('readdir-scoped-modules'))
|
|
12
|
+
const fs = require('fs')
|
|
13
|
+
const lstat = promisify(fs.lstat)
|
|
14
|
+
const readlink = promisify(fs.readlink)
|
|
15
|
+
const { depth } = require('treeverse')
|
|
16
|
+
|
|
17
|
+
const {
|
|
18
|
+
OK,
|
|
19
|
+
REPLACE,
|
|
20
|
+
CONFLICT,
|
|
21
|
+
} = require('../can-place-dep.js')
|
|
22
|
+
const PlaceDep = require('../place-dep.js')
|
|
14
23
|
|
|
15
24
|
const debug = require('../debug.js')
|
|
16
25
|
const fromPath = require('../from-path.js')
|
|
@@ -19,20 +28,9 @@ const Shrinkwrap = require('../shrinkwrap.js')
|
|
|
19
28
|
const Node = require('../node.js')
|
|
20
29
|
const Link = require('../link.js')
|
|
21
30
|
const addRmPkgDeps = require('../add-rm-pkg-deps.js')
|
|
22
|
-
const gatherDepSet = require('../gather-dep-set.js')
|
|
23
31
|
const optionalSet = require('../optional-set.js')
|
|
24
32
|
const {checkEngine, checkPlatform} = require('npm-install-checks')
|
|
25
33
|
|
|
26
|
-
// enum of return values for canPlaceDep.
|
|
27
|
-
// No, this is a conflict, you may not put that package here
|
|
28
|
-
const CONFLICT = Symbol('CONFLICT')
|
|
29
|
-
// Yes, this is fine, and should not be a problem
|
|
30
|
-
const OK = Symbol('OK')
|
|
31
|
-
// No need, because the package already here is fine
|
|
32
|
-
const KEEP = Symbol('KEEP')
|
|
33
|
-
// Yes, clobber the package that is already here
|
|
34
|
-
const REPLACE = Symbol('REPLACE')
|
|
35
|
-
|
|
36
34
|
const relpath = require('../relpath.js')
|
|
37
35
|
|
|
38
36
|
// note: some of these symbols are shared so we can hit
|
|
@@ -47,7 +45,6 @@ const _flagsSuspect = Symbol.for('flagsSuspect')
|
|
|
47
45
|
const _workspaces = Symbol.for('workspaces')
|
|
48
46
|
const _prune = Symbol('prune')
|
|
49
47
|
const _preferDedupe = Symbol('preferDedupe')
|
|
50
|
-
const _pruneDedupable = Symbol('pruneDedupable')
|
|
51
48
|
const _legacyBundling = Symbol('legacyBundling')
|
|
52
49
|
const _parseSettings = Symbol('parseSettings')
|
|
53
50
|
const _initTree = Symbol('initTree')
|
|
@@ -65,10 +62,6 @@ const _loadWorkspaces = Symbol.for('loadWorkspaces')
|
|
|
65
62
|
const _linkFromSpec = Symbol('linkFromSpec')
|
|
66
63
|
const _loadPeerSet = Symbol('loadPeerSet')
|
|
67
64
|
const _updateNames = Symbol.for('updateNames')
|
|
68
|
-
const _placeDep = Symbol.for('placeDep')
|
|
69
|
-
const _canPlaceDep = Symbol.for('canPlaceDep')
|
|
70
|
-
const _canPlacePeers = Symbol('canPlacePeers')
|
|
71
|
-
const _pruneForReplacement = Symbol('pruneForReplacement')
|
|
72
65
|
const _fixDepFlags = Symbol('fixDepFlags')
|
|
73
66
|
const _resolveLinks = Symbol('resolveLinks')
|
|
74
67
|
const _rootNodeFromPackage = Symbol('rootNodeFromPackage')
|
|
@@ -100,12 +93,8 @@ const _checkPlatform = Symbol('checkPlatform')
|
|
|
100
93
|
const _virtualRoots = Symbol('virtualRoots')
|
|
101
94
|
const _virtualRoot = Symbol('virtualRoot')
|
|
102
95
|
|
|
103
|
-
// used for the ERESOLVE error to show the last peer conflict encountered
|
|
104
|
-
const _peerConflict = Symbol('peerConflict')
|
|
105
|
-
|
|
106
96
|
const _failPeerConflict = Symbol('failPeerConflict')
|
|
107
97
|
const _explainPeerConflict = Symbol('explainPeerConflict')
|
|
108
|
-
const _warnPeerConflict = Symbol('warnPeerConflict')
|
|
109
98
|
const _edgesOverridden = Symbol('edgesOverridden')
|
|
110
99
|
// exposed symbol for unit testing the placeDep method directly
|
|
111
100
|
const _peerSetSource = Symbol.for('peerSetSource')
|
|
@@ -163,7 +152,6 @@ module.exports = cls => class IdealTreeBuilder extends cls {
|
|
|
163
152
|
this[_loadFailures] = new Set()
|
|
164
153
|
this[_linkNodes] = new Set()
|
|
165
154
|
this[_manifests] = new Map()
|
|
166
|
-
this[_peerConflict] = null
|
|
167
155
|
this[_edgesOverridden] = new Set()
|
|
168
156
|
this[_resolvedAdd] = []
|
|
169
157
|
|
|
@@ -227,17 +215,13 @@ module.exports = cls => class IdealTreeBuilder extends cls {
|
|
|
227
215
|
return treeCheck(this.idealTree)
|
|
228
216
|
}
|
|
229
217
|
|
|
230
|
-
[_checkEngineAndPlatform] () {
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
this[_checkPlatform](node)
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
})
|
|
218
|
+
async [_checkEngineAndPlatform] () {
|
|
219
|
+
for (const node of this.idealTree.inventory.values()) {
|
|
220
|
+
if (!node.optional) {
|
|
221
|
+
this[_checkEngine](node)
|
|
222
|
+
this[_checkPlatform](node)
|
|
223
|
+
}
|
|
224
|
+
}
|
|
241
225
|
}
|
|
242
226
|
|
|
243
227
|
[_checkPlatform] (node) {
|
|
@@ -426,7 +410,14 @@ module.exports = cls => class IdealTreeBuilder extends cls {
|
|
|
426
410
|
if (this[_updateAll] || updateName) {
|
|
427
411
|
if (updateName)
|
|
428
412
|
globalExplicitUpdateNames.push(name)
|
|
429
|
-
|
|
413
|
+
const dir = resolve(nm, name)
|
|
414
|
+
const st = await lstat(dir).catch(/* istanbul ignore next */ er => null)
|
|
415
|
+
if (st && st.isSymbolicLink()) {
|
|
416
|
+
const target = await readlink(dir)
|
|
417
|
+
const real = resolve(dirname(dir), target)
|
|
418
|
+
tree.package.dependencies[name] = `file:${real}`
|
|
419
|
+
} else
|
|
420
|
+
tree.package.dependencies[name] = '*'
|
|
430
421
|
}
|
|
431
422
|
}
|
|
432
423
|
}
|
|
@@ -850,7 +841,7 @@ This is a one-time fix-up, please be patient...
|
|
|
850
841
|
const tasks = []
|
|
851
842
|
const peerSource = this[_peerSetSource].get(node) || node
|
|
852
843
|
for (const edge of this[_problemEdges](node)) {
|
|
853
|
-
if (
|
|
844
|
+
if (edge.overridden)
|
|
854
845
|
continue
|
|
855
846
|
|
|
856
847
|
// peerSetSource is only relevant when we have a peerEntryEdge
|
|
@@ -894,34 +885,101 @@ This is a one-time fix-up, please be patient...
|
|
|
894
885
|
tasks.push({edge, dep})
|
|
895
886
|
}
|
|
896
887
|
|
|
897
|
-
const
|
|
888
|
+
const placeDeps = tasks
|
|
898
889
|
.sort((a, b) => a.edge.name.localeCompare(b.edge.name, 'en'))
|
|
899
|
-
.map(({ edge, dep }) =>
|
|
890
|
+
.map(({ edge, dep }) => new PlaceDep({
|
|
891
|
+
edge,
|
|
892
|
+
dep,
|
|
893
|
+
|
|
894
|
+
explicitRequest: this[_explicitRequests].has(edge),
|
|
895
|
+
updateNames: this[_updateNames],
|
|
896
|
+
auditReport: this.auditReport,
|
|
897
|
+
force: this[_force],
|
|
898
|
+
preferDedupe: this[_preferDedupe],
|
|
899
|
+
legacyBundling: this[_legacyBundling],
|
|
900
|
+
strictPeerDeps: this[_strictPeerDeps],
|
|
901
|
+
legacyPeerDeps: this.legacyPeerDeps,
|
|
902
|
+
globalStyle: this[_globalStyle],
|
|
903
|
+
}))
|
|
900
904
|
|
|
901
905
|
const promises = []
|
|
902
|
-
for (const
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
906
|
+
for (const pd of placeDeps) {
|
|
907
|
+
// placing a dep is actually a tree of placing the dep itself
|
|
908
|
+
// and all of its peer group that aren't already met by the tree
|
|
909
|
+
depth({
|
|
910
|
+
tree: pd,
|
|
911
|
+
getChildren: pd => pd.children,
|
|
912
|
+
visit: pd => {
|
|
913
|
+
const { placed, edge, canPlace: cpd } = pd
|
|
914
|
+
// if we didn't place anything, nothing to do here
|
|
915
|
+
if (!placed)
|
|
916
|
+
return
|
|
917
|
+
|
|
918
|
+
// we placed something, that means we changed the tree
|
|
919
|
+
if (placed.errors.length)
|
|
920
|
+
this[_loadFailures].add(placed)
|
|
921
|
+
this[_mutateTree] = true
|
|
922
|
+
if (cpd.canPlaceSelf === OK) {
|
|
923
|
+
for (const edgeIn of placed.edgesIn) {
|
|
924
|
+
if (edgeIn === edge)
|
|
925
|
+
continue
|
|
926
|
+
const { from, valid, overridden } = edgeIn
|
|
927
|
+
if (!overridden && !valid && !this[_depsSeen].has(from)) {
|
|
928
|
+
this.addTracker('idealTree', from.name, from.location)
|
|
929
|
+
this[_depsQueue].push(edgeIn.from)
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
} else {
|
|
933
|
+
/* istanbul ignore else - should be only OK or REPLACE here */
|
|
934
|
+
if (cpd.canPlaceSelf === REPLACE) {
|
|
935
|
+
// this may also create some invalid edges, for example if we're
|
|
936
|
+
// intentionally causing something to get nested which was
|
|
937
|
+
// previously placed in this location.
|
|
938
|
+
for (const edgeIn of placed.edgesIn) {
|
|
939
|
+
if (edgeIn === edge)
|
|
940
|
+
continue
|
|
941
|
+
|
|
942
|
+
const { valid, overridden } = edgeIn
|
|
943
|
+
if (!valid && !overridden) {
|
|
944
|
+
// if it's already been visited, we have to re-visit
|
|
945
|
+
// otherwise, just enqueue normally.
|
|
946
|
+
this[_depsSeen].delete(edgeIn.from)
|
|
947
|
+
this[_depsQueue].push(edgeIn.from)
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
/* istanbul ignore if - should be impossible */
|
|
954
|
+
if (cpd.canPlaceSelf === CONFLICT) {
|
|
955
|
+
debug(() => {
|
|
956
|
+
const er = new Error('placed with canPlaceSelf=CONFLICT')
|
|
957
|
+
throw Object.assign(er, { placeDep: pd })
|
|
958
|
+
})
|
|
959
|
+
return
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
// lastly, also check for the missing deps of the node we placed
|
|
963
|
+
this[_depsQueue].push(placed)
|
|
964
|
+
|
|
965
|
+
// pre-fetch any problem edges, since we'll need these soon
|
|
966
|
+
// if it fails at this point, though, dont' worry because it
|
|
967
|
+
// may well be an optional dep that has gone missing. it'll
|
|
968
|
+
// fail later anyway.
|
|
969
|
+
const from = fromPath(placed)
|
|
970
|
+
promises.push(...this[_problemEdges](placed).map(e =>
|
|
971
|
+
this[_fetchManifest](npa.resolve(e.name, e.spec, from))
|
|
972
|
+
.catch(er => null)))
|
|
973
|
+
},
|
|
974
|
+
})
|
|
917
975
|
}
|
|
918
|
-
await Promise.all(promises)
|
|
919
976
|
|
|
920
977
|
for (const { to } of node.edgesOut.values()) {
|
|
921
978
|
if (to && to.isLink && to.target)
|
|
922
979
|
this[_linkNodes].add(to)
|
|
923
980
|
}
|
|
924
981
|
|
|
982
|
+
await Promise.all(promises)
|
|
925
983
|
return this[_buildDepStep]()
|
|
926
984
|
}
|
|
927
985
|
|
|
@@ -934,7 +992,6 @@ This is a one-time fix-up, please be patient...
|
|
|
934
992
|
// Note that the virtual root will also have virtual copies of the
|
|
935
993
|
// targets of any child Links, so that they resolve appropriately.
|
|
936
994
|
const parent = parent_ || this[_virtualRoot](edge.from)
|
|
937
|
-
const realParent = edge.peer ? edge.from.resolveParent : edge.from
|
|
938
995
|
|
|
939
996
|
const spec = npa.resolve(edge.name, edge.spec, edge.from.path)
|
|
940
997
|
const first = await this[_nodeFromSpec](edge.name, spec, parent, edge)
|
|
@@ -965,19 +1022,18 @@ This is a one-time fix-up, please be patient...
|
|
|
965
1022
|
required.has(secondEdge.from) && secondEdge.type !== 'peerOptional'))
|
|
966
1023
|
required.add(node)
|
|
967
1024
|
|
|
968
|
-
// handle otherwise unresolvable dependency nesting loops by
|
|
969
|
-
// creating a symbolic link
|
|
970
|
-
// a1 -> b1 -> a2 -> b2 -> a1 -> ...
|
|
971
|
-
// instead of nesting forever, when the loop occurs, create
|
|
972
|
-
// a symbolic link to the earlier instance
|
|
973
|
-
for (let p = edge.from.resolveParent; p; p = p.resolveParent) {
|
|
974
|
-
if (p.matches(node) && !p.isTop)
|
|
975
|
-
return new Link({ parent: realParent, target: p })
|
|
976
|
-
}
|
|
977
|
-
|
|
978
1025
|
// keep track of the thing that caused this node to be included.
|
|
979
1026
|
const src = parent.sourceReference
|
|
980
1027
|
this[_peerSetSource].set(node, src)
|
|
1028
|
+
|
|
1029
|
+
// do not load the peers along with the set if this is a global top pkg
|
|
1030
|
+
// otherwise we'll be tempted to put peers as other top-level installed
|
|
1031
|
+
// things, potentially clobbering what's there already, which is not
|
|
1032
|
+
// what we want. the missing edges will be picked up on the next pass.
|
|
1033
|
+
if (this[_global] && edge.from.isProjectRoot)
|
|
1034
|
+
return node
|
|
1035
|
+
|
|
1036
|
+
// otherwise, we have to make sure that our peers can go along with us.
|
|
981
1037
|
return this[_loadPeerSet](node, required)
|
|
982
1038
|
}
|
|
983
1039
|
|
|
@@ -1176,8 +1232,10 @@ This is a one-time fix-up, please be patient...
|
|
|
1176
1232
|
// allow it. either we're overriding, or it's not something
|
|
1177
1233
|
// that will be installed by default anyway, and we'll fail when
|
|
1178
1234
|
// we get to the point where we need to, if we need to.
|
|
1179
|
-
if (conflictOK || !required.has(dep))
|
|
1235
|
+
if (conflictOK || !required.has(dep)) {
|
|
1236
|
+
edge.overridden = true
|
|
1180
1237
|
continue
|
|
1238
|
+
}
|
|
1181
1239
|
|
|
1182
1240
|
// problem
|
|
1183
1241
|
this[_failPeerConflict](edge, parentEdge)
|
|
@@ -1219,9 +1277,7 @@ This is a one-time fix-up, please be patient...
|
|
|
1219
1277
|
[_explainPeerConflict] (edge, currentEdge) {
|
|
1220
1278
|
const node = edge.from
|
|
1221
1279
|
const curNode = node.resolve(edge.name)
|
|
1222
|
-
const
|
|
1223
|
-
const current = curNode ? curNode.explain() : pc.current
|
|
1224
|
-
const peerConflict = pc.peer
|
|
1280
|
+
const current = curNode.explain()
|
|
1225
1281
|
return {
|
|
1226
1282
|
code: 'ERESOLVE',
|
|
1227
1283
|
current,
|
|
@@ -1230,640 +1286,11 @@ This is a one-time fix-up, please be patient...
|
|
|
1230
1286
|
// the tree handling logic.
|
|
1231
1287
|
currentEdge: currentEdge ? currentEdge.explain() : null,
|
|
1232
1288
|
edge: edge.explain(),
|
|
1233
|
-
peerConflict,
|
|
1234
1289
|
strictPeerDeps: this[_strictPeerDeps],
|
|
1235
1290
|
force: this[_force],
|
|
1236
1291
|
}
|
|
1237
1292
|
}
|
|
1238
1293
|
|
|
1239
|
-
[_warnPeerConflict] (edge) {
|
|
1240
|
-
// track that we've overridden this edge, so that we don't keep trying
|
|
1241
|
-
// to re-resolve it in an infinite loop.
|
|
1242
|
-
this[_edgesOverridden].add(edge)
|
|
1243
|
-
const expl = this[_explainPeerConflict](edge)
|
|
1244
|
-
this.log.warn('ERESOLVE', 'overriding peer dependency', expl)
|
|
1245
|
-
}
|
|
1246
|
-
|
|
1247
|
-
// starting from either node, or in the case of non-root peer deps,
|
|
1248
|
-
// the node's parent, walk up the tree until we find the first spot
|
|
1249
|
-
// where this dep cannot be placed, and use the one right before that.
|
|
1250
|
-
// place dep, requested by node, to satisfy edge
|
|
1251
|
-
// XXX split this out into a separate method or mixin? It's quite a lot
|
|
1252
|
-
// of functionality that ought to have its own unit tests more conveniently.
|
|
1253
|
-
[_placeDep] (dep, node, edge, peerEntryEdge = null, peerPath = []) {
|
|
1254
|
-
if (edge.to &&
|
|
1255
|
-
!edge.error &&
|
|
1256
|
-
!this[_explicitRequests].has(edge) &&
|
|
1257
|
-
!this[_updateNames].includes(edge.name) &&
|
|
1258
|
-
!this[_isVulnerable](edge.to))
|
|
1259
|
-
return []
|
|
1260
|
-
|
|
1261
|
-
// top nodes should still get peer deps from their fsParent if possible,
|
|
1262
|
-
// and only install locally if there's no other option, eg for a link
|
|
1263
|
-
// outside of the project root, or for a conflicted dep.
|
|
1264
|
-
const start = edge.peer && !node.isProjectRoot ? node.resolveParent || node
|
|
1265
|
-
: node
|
|
1266
|
-
|
|
1267
|
-
let target
|
|
1268
|
-
let canPlace = null
|
|
1269
|
-
let isSource = false
|
|
1270
|
-
const source = this[_peerSetSource].get(dep)
|
|
1271
|
-
for (let check = start; check; check = check.resolveParent) {
|
|
1272
|
-
// we always give the FIRST place we possibly *can* put this a little
|
|
1273
|
-
// extra prioritization with peer dep overrides and deduping
|
|
1274
|
-
if (check === source)
|
|
1275
|
-
isSource = true
|
|
1276
|
-
|
|
1277
|
-
// if the current location has a peerDep on it, then we can't place here
|
|
1278
|
-
// this is pretty rare to hit, since we always prefer deduping peers.
|
|
1279
|
-
const checkEdge = check.edgesOut.get(edge.name)
|
|
1280
|
-
if (!check.isTop && checkEdge && checkEdge.peer)
|
|
1281
|
-
continue
|
|
1282
|
-
|
|
1283
|
-
const cp = this[_canPlaceDep](dep, check, edge, peerEntryEdge, peerPath, isSource)
|
|
1284
|
-
isSource = false
|
|
1285
|
-
|
|
1286
|
-
// anything other than a conflict is fine to proceed with
|
|
1287
|
-
if (cp !== CONFLICT) {
|
|
1288
|
-
canPlace = cp
|
|
1289
|
-
target = check
|
|
1290
|
-
} else
|
|
1291
|
-
break
|
|
1292
|
-
|
|
1293
|
-
// nest packages like npm v1 and v2
|
|
1294
|
-
// very disk-inefficient
|
|
1295
|
-
if (this[_legacyBundling])
|
|
1296
|
-
break
|
|
1297
|
-
|
|
1298
|
-
// when installing globally, or just in global style, we never place
|
|
1299
|
-
// deps above the first level.
|
|
1300
|
-
const tree = this.idealTree && this.idealTree.target
|
|
1301
|
-
if (this[_globalStyle] && check.resolveParent === tree)
|
|
1302
|
-
break
|
|
1303
|
-
}
|
|
1304
|
-
|
|
1305
|
-
// if we can't find a target, that means that the last placed checked
|
|
1306
|
-
// (and all the places before it) had a copy already. if we're in
|
|
1307
|
-
// --force mode, then the user has explicitly said that they're ok
|
|
1308
|
-
// with conflicts. This can only occur in --force mode in the case
|
|
1309
|
-
// when a node was added to the tree with a peerOptional dep that we
|
|
1310
|
-
// ignored, and then later, that edge became invalid, and we fail to
|
|
1311
|
-
// resolve it. We will warn about it in a moment.
|
|
1312
|
-
if (!target) {
|
|
1313
|
-
if (this[_force]) {
|
|
1314
|
-
// we know that there is a dep (not the root) which is the target
|
|
1315
|
-
// of this edge, or else it wouldn't have been a conflict.
|
|
1316
|
-
target = edge.to.resolveParent
|
|
1317
|
-
canPlace = KEEP
|
|
1318
|
-
} else
|
|
1319
|
-
this[_failPeerConflict](edge)
|
|
1320
|
-
} else {
|
|
1321
|
-
// it worked, so we clearly have no peer conflicts at this point.
|
|
1322
|
-
this[_peerConflict] = null
|
|
1323
|
-
}
|
|
1324
|
-
|
|
1325
|
-
this.log.silly(
|
|
1326
|
-
'placeDep',
|
|
1327
|
-
target.location || 'ROOT',
|
|
1328
|
-
`${dep.name}@${dep.version}`,
|
|
1329
|
-
canPlace.description || /* istanbul ignore next */ canPlace,
|
|
1330
|
-
`for: ${node.package._id || node.location}`,
|
|
1331
|
-
`want: ${edge.spec || '*'}`
|
|
1332
|
-
)
|
|
1333
|
-
|
|
1334
|
-
// Can only get KEEP here if the original edge was valid,
|
|
1335
|
-
// and we're checking for an update but it's already up to date.
|
|
1336
|
-
if (canPlace === KEEP) {
|
|
1337
|
-
if (edge.peer && !target.children.get(edge.name).satisfies(edge)) {
|
|
1338
|
-
// this is an overridden peer dep
|
|
1339
|
-
this[_warnPeerConflict](edge)
|
|
1340
|
-
}
|
|
1341
|
-
|
|
1342
|
-
// if we get a KEEP in a update scenario, then we MAY have something
|
|
1343
|
-
// already duplicating this unnecessarily! For example:
|
|
1344
|
-
// ```
|
|
1345
|
-
// root
|
|
1346
|
-
// +-- x (dep: y@1.x)
|
|
1347
|
-
// | +-- y@1.0.0
|
|
1348
|
-
// +-- y@1.1.0
|
|
1349
|
-
// ```
|
|
1350
|
-
// Now say we do `reify({update:['y']})`, and the latest version is
|
|
1351
|
-
// 1.1.0, which we already have in the root. We'll try to place y@1.1.0
|
|
1352
|
-
// first in x, then in the root, ending with KEEP, because we already
|
|
1353
|
-
// have it. In that case, we ought to REMOVE the nm/x/nm/y node, because
|
|
1354
|
-
// it is an unnecessary duplicate.
|
|
1355
|
-
this[_pruneDedupable](target)
|
|
1356
|
-
return []
|
|
1357
|
-
}
|
|
1358
|
-
|
|
1359
|
-
// figure out which of this node's peer deps will get placed as well
|
|
1360
|
-
const virtualRoot = dep.parent
|
|
1361
|
-
|
|
1362
|
-
const newDep = new dep.constructor({
|
|
1363
|
-
name: dep.name,
|
|
1364
|
-
pkg: dep.package,
|
|
1365
|
-
resolved: dep.resolved,
|
|
1366
|
-
integrity: dep.integrity,
|
|
1367
|
-
legacyPeerDeps: this.legacyPeerDeps,
|
|
1368
|
-
error: dep.errors[0],
|
|
1369
|
-
...(dep.isLink ? { target: dep.target, realpath: dep.target.path } : {}),
|
|
1370
|
-
})
|
|
1371
|
-
if (this[_loadFailures].has(dep))
|
|
1372
|
-
this[_loadFailures].add(newDep)
|
|
1373
|
-
|
|
1374
|
-
const placed = [newDep]
|
|
1375
|
-
const oldChild = target.children.get(edge.name)
|
|
1376
|
-
if (oldChild) {
|
|
1377
|
-
// if we're replacing, we should also remove any nodes for edges that
|
|
1378
|
-
// are now invalid, and where this (or its deps) is the only dependent,
|
|
1379
|
-
// and also recurse on that pruning. Otherwise leaving that dep node
|
|
1380
|
-
// around can result in spurious conflicts pushing nodes deeper into
|
|
1381
|
-
// the tree than needed in the case of cycles that will be removed
|
|
1382
|
-
// later anyway.
|
|
1383
|
-
const oldDeps = []
|
|
1384
|
-
for (const [name, edge] of oldChild.edgesOut.entries()) {
|
|
1385
|
-
if (!newDep.edgesOut.has(name) && edge.to)
|
|
1386
|
-
oldDeps.push(...gatherDepSet([edge.to], e => e.to !== edge.to))
|
|
1387
|
-
}
|
|
1388
|
-
newDep.replace(oldChild)
|
|
1389
|
-
this[_pruneForReplacement](newDep, oldDeps)
|
|
1390
|
-
// this may also create some invalid edges, for example if we're
|
|
1391
|
-
// intentionally causing something to get nested which was previously
|
|
1392
|
-
// placed in this location.
|
|
1393
|
-
for (const edgeIn of newDep.edgesIn) {
|
|
1394
|
-
if (edgeIn.invalid && edgeIn !== edge) {
|
|
1395
|
-
this[_depsQueue].push(edgeIn.from)
|
|
1396
|
-
this[_depsSeen].delete(edgeIn.from)
|
|
1397
|
-
}
|
|
1398
|
-
}
|
|
1399
|
-
} else
|
|
1400
|
-
newDep.parent = target
|
|
1401
|
-
|
|
1402
|
-
if (edge.peer && !newDep.satisfies(edge)) {
|
|
1403
|
-
// this is an overridden peer dep
|
|
1404
|
-
this[_warnPeerConflict](edge)
|
|
1405
|
-
}
|
|
1406
|
-
|
|
1407
|
-
// If the edge is not an error, then we're updating something, and
|
|
1408
|
-
// MAY end up putting a better/identical node further up the tree in
|
|
1409
|
-
// a way that causes an unnecessary duplication. If so, remove the
|
|
1410
|
-
// now-unnecessary node.
|
|
1411
|
-
if (edge.valid && edge.to && edge.to !== newDep)
|
|
1412
|
-
this[_pruneDedupable](edge.to, false)
|
|
1413
|
-
|
|
1414
|
-
// visit any dependents who are upset by this change
|
|
1415
|
-
// if it's an angry overridden peer edge, however, make sure we
|
|
1416
|
-
// skip over it!
|
|
1417
|
-
for (const edgeIn of newDep.edgesIn) {
|
|
1418
|
-
if (edgeIn !== edge && !edgeIn.valid && !this[_depsSeen].has(edge.from)) {
|
|
1419
|
-
this.addTracker('idealTree', edgeIn.from.name, edgeIn.from.location)
|
|
1420
|
-
this[_depsQueue].push(edgeIn.from)
|
|
1421
|
-
}
|
|
1422
|
-
}
|
|
1423
|
-
|
|
1424
|
-
// in case we just made some duplicates that can be removed,
|
|
1425
|
-
// prune anything deeper in the tree that can be replaced by this
|
|
1426
|
-
if (this.idealTree) {
|
|
1427
|
-
for (const node of this.idealTree.inventory.query('name', newDep.name)) {
|
|
1428
|
-
if (!node.isTop && node.isDescendantOf(target))
|
|
1429
|
-
this[_pruneDedupable](node, false)
|
|
1430
|
-
}
|
|
1431
|
-
}
|
|
1432
|
-
|
|
1433
|
-
// also place its unmet or invalid peer deps at this location
|
|
1434
|
-
// note that newDep has now been removed from the virtualRoot set
|
|
1435
|
-
// by virtue of being placed in the target's node_modules.
|
|
1436
|
-
// loop through any peer deps from the thing we just placed, and place
|
|
1437
|
-
// those ones as well. it's safe to do this with the virtual nodes,
|
|
1438
|
-
// because we're copying rather than moving them out of the virtual root,
|
|
1439
|
-
// otherwise they'd be gone and the peer set would change throughout
|
|
1440
|
-
// this loop.
|
|
1441
|
-
for (const peerEdge of newDep.edgesOut.values()) {
|
|
1442
|
-
const peer = virtualRoot.children.get(peerEdge.name)
|
|
1443
|
-
|
|
1444
|
-
// Note: if the virtualRoot *doesn't* have the peer, then that means
|
|
1445
|
-
// it's an optional peer dep. If it's not being properly met (ie,
|
|
1446
|
-
// peerEdge.valid is false), that this is likely heading for an
|
|
1447
|
-
// ERESOLVE error, unless it can walk further up the tree.
|
|
1448
|
-
if (!peerEdge.peer || peerEdge.valid || !peer)
|
|
1449
|
-
continue
|
|
1450
|
-
|
|
1451
|
-
const peerPlaced = this[_placeDep](
|
|
1452
|
-
peer, newDep, peerEdge, peerEntryEdge || edge, peerPath)
|
|
1453
|
-
placed.push(...peerPlaced)
|
|
1454
|
-
}
|
|
1455
|
-
|
|
1456
|
-
// we're done with this now, clean it up.
|
|
1457
|
-
this[_virtualRoots].delete(virtualRoot.sourceReference)
|
|
1458
|
-
|
|
1459
|
-
return placed
|
|
1460
|
-
}
|
|
1461
|
-
|
|
1462
|
-
// prune all the nodes in a branch of the tree that can be safely removed
|
|
1463
|
-
// This is only the most basic duplication detection; it finds if there
|
|
1464
|
-
// is another satisfying node further up the tree, and if so, dedupes.
|
|
1465
|
-
// Even in legacyBundling mode, we do this amount of deduplication.
|
|
1466
|
-
[_pruneDedupable] (node, descend = true) {
|
|
1467
|
-
if (node.canDedupe(this[_preferDedupe])) {
|
|
1468
|
-
node.root = null
|
|
1469
|
-
return
|
|
1470
|
-
}
|
|
1471
|
-
if (descend) {
|
|
1472
|
-
// sort these so that they're deterministically ordered
|
|
1473
|
-
// otherwise, resulting tree shape is dependent on the order
|
|
1474
|
-
// in which they happened to be resolved.
|
|
1475
|
-
const nodeSort = (a, b) => a.location.localeCompare(b.location, 'en')
|
|
1476
|
-
|
|
1477
|
-
const children = [...node.children.values()].sort(nodeSort)
|
|
1478
|
-
const fsChildren = [...node.fsChildren].sort(nodeSort)
|
|
1479
|
-
for (const child of children)
|
|
1480
|
-
this[_pruneDedupable](child)
|
|
1481
|
-
for (const topNode of fsChildren) {
|
|
1482
|
-
const children = [...topNode.children.values()].sort(nodeSort)
|
|
1483
|
-
for (const child of children)
|
|
1484
|
-
this[_pruneDedupable](child)
|
|
1485
|
-
}
|
|
1486
|
-
}
|
|
1487
|
-
}
|
|
1488
|
-
|
|
1489
|
-
[_pruneForReplacement] (node, oldDeps) {
|
|
1490
|
-
// gather up all the invalid edgesOut, and any now-extraneous
|
|
1491
|
-
// deps that the new node doesn't depend on but the old one did.
|
|
1492
|
-
const invalidDeps = new Set([...node.edgesOut.values()]
|
|
1493
|
-
.filter(e => e.to && !e.valid).map(e => e.to))
|
|
1494
|
-
for (const dep of oldDeps) {
|
|
1495
|
-
const set = gatherDepSet([dep], e => e.to !== dep && e.valid)
|
|
1496
|
-
for (const dep of set)
|
|
1497
|
-
invalidDeps.add(dep)
|
|
1498
|
-
}
|
|
1499
|
-
|
|
1500
|
-
// ignore dependency edges from the node being replaced, but
|
|
1501
|
-
// otherwise filter the set down to just the set with no
|
|
1502
|
-
// dependencies from outside the set, except the node in question.
|
|
1503
|
-
const deps = gatherDepSet(invalidDeps, edge =>
|
|
1504
|
-
edge.from !== node && edge.to !== node && edge.valid)
|
|
1505
|
-
|
|
1506
|
-
// now just delete whatever's left, because it's junk
|
|
1507
|
-
for (const dep of deps)
|
|
1508
|
-
dep.parent = null
|
|
1509
|
-
}
|
|
1510
|
-
|
|
1511
|
-
// check if we can place DEP in TARGET to satisfy EDGE
|
|
1512
|
-
// Need to verify:
|
|
1513
|
-
// - no child by that name there already
|
|
1514
|
-
// - target does not have a peer dep on name
|
|
1515
|
-
// - no higher-level pkg by that name and incompatible spec is depended on
|
|
1516
|
-
// by anything lower in the tree.
|
|
1517
|
-
// - node's peer deps and meta-peer deps are siblings in a virtual root at
|
|
1518
|
-
// this point. make sure that the whole family can come along, so apply
|
|
1519
|
-
// the same checks to each of them. They may land higher up in the tree,
|
|
1520
|
-
// but we need to know that they CAN live here.
|
|
1521
|
-
// Responses:
|
|
1522
|
-
// - OK - Yes, because there is nothing there and no conflicts caused
|
|
1523
|
-
// - REPLACE - Yes, and you can clobber what's there
|
|
1524
|
-
// - KEEP - No, but what's there is fine
|
|
1525
|
-
// - CONFLICT - You may not put that there
|
|
1526
|
-
//
|
|
1527
|
-
// Check peers on OK or REPLACE. KEEP and CONFLICT do not require peer
|
|
1528
|
-
// checking, because either we're leaving it alone, or it won't work anyway.
|
|
1529
|
-
// When we check peers, we pass along the peerEntryEdge to track the
|
|
1530
|
-
// original edge that caused us to load the family of peer dependencies.
|
|
1531
|
-
[_canPlaceDep] (dep, target, edge, peerEntryEdge = null, peerPath = [], isSource = false) {
|
|
1532
|
-
/* istanbul ignore next */
|
|
1533
|
-
debug(() => {
|
|
1534
|
-
if (!dep)
|
|
1535
|
-
throw new Error('no dep??')
|
|
1536
|
-
})
|
|
1537
|
-
const entryEdge = peerEntryEdge || edge
|
|
1538
|
-
const source = this[_peerSetSource].get(dep)
|
|
1539
|
-
|
|
1540
|
-
isSource = isSource || target === source
|
|
1541
|
-
// if we're overriding the source, then we care if the *target* is
|
|
1542
|
-
// ours, even if it wasn't actually the original source, since we
|
|
1543
|
-
// are depending on something that has a dep that can't go in its own
|
|
1544
|
-
// folder. for example, a -> b, b -> PEER(a). Even though a is the
|
|
1545
|
-
// source, b has to be installed up a level, and if the root package
|
|
1546
|
-
// depends on a, and it has a conflict, it's our problem. So, the root
|
|
1547
|
-
// (or whatever is bringing in a) becomes the "effective source" for
|
|
1548
|
-
// the purposes of this calculation.
|
|
1549
|
-
const { isProjectRoot, isWorkspace } = isSource ? target : source || {}
|
|
1550
|
-
const isMine = isProjectRoot || isWorkspace
|
|
1551
|
-
|
|
1552
|
-
// Useful testing thingie right here.
|
|
1553
|
-
// peerEntryEdge should *always* be a non-peer dependency, or a peer
|
|
1554
|
-
// dependency from the root node. When we get spurious ERESOLVE errors,
|
|
1555
|
-
// or *don't* get ERESOLVE errors when we should, check to see if this
|
|
1556
|
-
// fails, because it MAY mean we got off track somehow.
|
|
1557
|
-
/* istanbul ignore next - debug check, should be impossible */
|
|
1558
|
-
debug(() => {
|
|
1559
|
-
if (peerEntryEdge && peerEntryEdge.peer && !peerEntryEdge.from.isTop)
|
|
1560
|
-
throw new Error('lost original peerEntryEdge somehow?')
|
|
1561
|
-
})
|
|
1562
|
-
|
|
1563
|
-
if (target.children.has(edge.name)) {
|
|
1564
|
-
const current = target.children.get(edge.name)
|
|
1565
|
-
|
|
1566
|
-
// same thing = keep, UNLESS the current doesn't satisfy and new
|
|
1567
|
-
// one does satisfy. This can happen if it's a link to a matching target
|
|
1568
|
-
// at a different location, which satisfies a version dep, but not a
|
|
1569
|
-
// file: dep. If neither of them satisfy, then we can replace it,
|
|
1570
|
-
// because presumably it's better for a peer or something.
|
|
1571
|
-
if (dep.matches(current)) {
|
|
1572
|
-
if (current.satisfies(edge) || !dep.satisfies(edge))
|
|
1573
|
-
return KEEP
|
|
1574
|
-
}
|
|
1575
|
-
|
|
1576
|
-
const { version: curVer } = current
|
|
1577
|
-
const { version: newVer } = dep
|
|
1578
|
-
const tryReplace = curVer && newVer && semver.gte(newVer, curVer)
|
|
1579
|
-
if (tryReplace && dep.canReplace(current)) {
|
|
1580
|
-
const res = this[_canPlacePeers](dep, target, edge, REPLACE, peerEntryEdge, peerPath, isSource)
|
|
1581
|
-
/* istanbul ignore else - It's extremely rare that a replaceable
|
|
1582
|
-
* node would be a conflict, if the current one wasn't a conflict,
|
|
1583
|
-
* but it is theoretically possible if peer deps are pinned. In
|
|
1584
|
-
* that case we treat it like any other conflict, and keep trying */
|
|
1585
|
-
if (res !== CONFLICT)
|
|
1586
|
-
return res
|
|
1587
|
-
}
|
|
1588
|
-
|
|
1589
|
-
// ok, can't replace the current with new one, but maybe current is ok?
|
|
1590
|
-
// no need to check if it's a peer that's valid to be here, because
|
|
1591
|
-
// peers are always placed along with their entry source
|
|
1592
|
-
if (edge.satisfiedBy(current))
|
|
1593
|
-
return KEEP
|
|
1594
|
-
|
|
1595
|
-
// if we prefer deduping, then try replacing newer with older
|
|
1596
|
-
// we always prefer to dedupe peers, because they are trying
|
|
1597
|
-
// a bit harder to be singletons.
|
|
1598
|
-
const preferDedupe = this[_preferDedupe] || edge.peer
|
|
1599
|
-
if (preferDedupe && !tryReplace && dep.canReplace(current)) {
|
|
1600
|
-
const res = this[_canPlacePeers](dep, target, edge, REPLACE, peerEntryEdge, peerPath, isSource)
|
|
1601
|
-
/* istanbul ignore else - It's extremely rare that a replaceable
|
|
1602
|
-
* node would be a conflict, if the current one wasn't a conflict,
|
|
1603
|
-
* but it is theoretically possible if peer deps are pinned. In
|
|
1604
|
-
* that case we treat it like any other conflict, and keep trying */
|
|
1605
|
-
if (res !== CONFLICT)
|
|
1606
|
-
return res
|
|
1607
|
-
}
|
|
1608
|
-
|
|
1609
|
-
// check for conflict override cases.
|
|
1610
|
-
// first: is this the only place this thing can go? If the target is
|
|
1611
|
-
// the source, then one of these things are true.
|
|
1612
|
-
//
|
|
1613
|
-
// 1. the conflicted dep was deduped up to here from a lower dependency
|
|
1614
|
-
// w -> (x,y)
|
|
1615
|
-
// x -> (z)
|
|
1616
|
-
// y -> PEER(p@1)
|
|
1617
|
-
// z -> (q)
|
|
1618
|
-
// q -> (p@2)
|
|
1619
|
-
//
|
|
1620
|
-
// When building, let's say that x is fully placed, with all of its
|
|
1621
|
-
// deps, and we're _adding_ y. Since the peer on p@1 was not initially
|
|
1622
|
-
// present, it's been deduped up to w, and now needs to be pushed out.
|
|
1623
|
-
// Replace it, and potentially also replace its peer set (though that'll
|
|
1624
|
-
// be accomplished by making the same determination when we call
|
|
1625
|
-
// _canPlacePeers)
|
|
1626
|
-
//
|
|
1627
|
-
// 2. the dep we're TRYING to place here ought to be overridden by the
|
|
1628
|
-
// one that's here now, because current is (a) a direct dep of the
|
|
1629
|
-
// source, or (b) an already-placed peer in a conflicted peer set, or
|
|
1630
|
-
// (c) an already-placed peer in a different peer set at the same level.
|
|
1631
|
-
// If strict or ours, conflict. Otherwise, keep.
|
|
1632
|
-
if (isSource) {
|
|
1633
|
-
// check to see if the current module could go deeper in the tree
|
|
1634
|
-
let canReplace = true
|
|
1635
|
-
// only do this check when we're placing peers. when we're placing
|
|
1636
|
-
// the original in the source, we know that the edge from the source
|
|
1637
|
-
// is the thing we're trying to place, so its peer set will need to be
|
|
1638
|
-
// placed here as well. the virtualRoot already has the appropriate
|
|
1639
|
-
// overrides applied.
|
|
1640
|
-
if (peerEntryEdge) {
|
|
1641
|
-
const currentPeerSet = getPeerSet(current)
|
|
1642
|
-
|
|
1643
|
-
// We are effectively replacing currentPeerSet with newPeerSet
|
|
1644
|
-
// If there are any non-peer deps coming into the currentPeerSet,
|
|
1645
|
-
// which are currently valid, and are from the target, then that
|
|
1646
|
-
// means that we have to ensure that they're not going to be made
|
|
1647
|
-
// invalid by putting the newPeerSet in place.
|
|
1648
|
-
// If the edge comes from somewhere deeper than the target, then
|
|
1649
|
-
// that's fine, because we'll create an invalid edge, detect it,
|
|
1650
|
-
// and duplicate the node further into the tree.
|
|
1651
|
-
// loop through the currentPeerSet checking for valid edges on
|
|
1652
|
-
// the members of the peer set which will be made invalid.
|
|
1653
|
-
const targetEdges = new Set()
|
|
1654
|
-
for (const p of currentPeerSet) {
|
|
1655
|
-
for (const edge of p.edgesIn) {
|
|
1656
|
-
// edge from within the peerSet, ignore
|
|
1657
|
-
if (currentPeerSet.has(edge.from))
|
|
1658
|
-
continue
|
|
1659
|
-
// only care about valid edges from target.
|
|
1660
|
-
// edges from elsewhere can dupe if offended, invalid edges
|
|
1661
|
-
// are already being fixed or will be later.
|
|
1662
|
-
if (edge.from !== target || !edge.valid)
|
|
1663
|
-
continue
|
|
1664
|
-
targetEdges.add(edge)
|
|
1665
|
-
}
|
|
1666
|
-
}
|
|
1667
|
-
|
|
1668
|
-
for (const edge of targetEdges) {
|
|
1669
|
-
// see if we intend to replace this one anyway
|
|
1670
|
-
const rep = dep.parent.children.get(edge.name)
|
|
1671
|
-
const current = edge.to
|
|
1672
|
-
if (!rep) {
|
|
1673
|
-
// this isn't one we're replacing. but it WAS included in the
|
|
1674
|
-
// peerSet for some reason, so make sure that it's still
|
|
1675
|
-
// ok with the replacements in the new peerSet
|
|
1676
|
-
for (const curEdge of current.edgesOut.values()) {
|
|
1677
|
-
const newRepDep = dep.parent.children.get(curEdge.name)
|
|
1678
|
-
if (curEdge.valid && newRepDep && !newRepDep.satisfies(curEdge)) {
|
|
1679
|
-
canReplace = false
|
|
1680
|
-
break
|
|
1681
|
-
}
|
|
1682
|
-
}
|
|
1683
|
-
continue
|
|
1684
|
-
}
|
|
1685
|
-
|
|
1686
|
-
// was this replacement already an override of some sort?
|
|
1687
|
-
const override = [...rep.edgesIn].some(e => !e.valid)
|
|
1688
|
-
// if we have a rep, and it's ok to put in this location, and
|
|
1689
|
-
// it's not already part of an override in the peerSet, then
|
|
1690
|
-
// we can continue with it.
|
|
1691
|
-
if (rep.satisfies(edge) && !override)
|
|
1692
|
-
continue
|
|
1693
|
-
// Otherwise, we cannot replace.
|
|
1694
|
-
canReplace = false
|
|
1695
|
-
break
|
|
1696
|
-
}
|
|
1697
|
-
// if we're going to be replacing the peerSet, we have to remove
|
|
1698
|
-
// and re-resolve any members of the old peerSet that are not
|
|
1699
|
-
// present in the new one, and which will have invalid edges.
|
|
1700
|
-
// We know that they're not depended upon by the target, or else
|
|
1701
|
-
// they would have caused a conflict, so they'll get landed deeper
|
|
1702
|
-
// in the tree, if possible.
|
|
1703
|
-
if (canReplace) {
|
|
1704
|
-
let needNesting = false
|
|
1705
|
-
OUTER: for (const node of currentPeerSet) {
|
|
1706
|
-
const rep = dep.parent.children.get(node.name)
|
|
1707
|
-
// has a replacement, already addressed above
|
|
1708
|
-
if (rep)
|
|
1709
|
-
continue
|
|
1710
|
-
|
|
1711
|
-
// ok, it has been placed here to dedupe, see if it needs to go
|
|
1712
|
-
// back deeper within the tree.
|
|
1713
|
-
for (const edge of node.edgesOut.values()) {
|
|
1714
|
-
const repDep = dep.parent.children.get(edge.name)
|
|
1715
|
-
// not in new peerSet, maybe fine.
|
|
1716
|
-
if (!repDep)
|
|
1717
|
-
continue
|
|
1718
|
-
|
|
1719
|
-
// new thing will be fine, no worries
|
|
1720
|
-
if (repDep.satisfies(edge))
|
|
1721
|
-
continue
|
|
1722
|
-
|
|
1723
|
-
// uhoh, we'll have to nest them.
|
|
1724
|
-
needNesting = true
|
|
1725
|
-
break OUTER
|
|
1726
|
-
}
|
|
1727
|
-
}
|
|
1728
|
-
|
|
1729
|
-
// to nest, just delete everything without a target dep
|
|
1730
|
-
// that's in the current peerSet, and add their dependants
|
|
1731
|
-
// to the _depsQueue for evaluation. Some of these MAY end
|
|
1732
|
-
// up in the same location again, and that's fine.
|
|
1733
|
-
if (needNesting) {
|
|
1734
|
-
// avoid mutating the tree while we're examining it
|
|
1735
|
-
const dependants = new Set()
|
|
1736
|
-
const reresolve = new Set()
|
|
1737
|
-
OUTER: for (const node of currentPeerSet) {
|
|
1738
|
-
const rep = dep.parent.children.get(node.name)
|
|
1739
|
-
if (rep)
|
|
1740
|
-
continue
|
|
1741
|
-
// create a separate set for each one, so we can skip any
|
|
1742
|
-
// that might somehow have an incoming target edge
|
|
1743
|
-
const deps = new Set()
|
|
1744
|
-
for (const edge of node.edgesIn) {
|
|
1745
|
-
// a target dep, skip this dep entirely, already addressed
|
|
1746
|
-
// ignoring for coverage, because it really ought to be
|
|
1747
|
-
// impossible, but I can't prove it yet, so this is here
|
|
1748
|
-
// for safety.
|
|
1749
|
-
/* istanbul ignore if - should be impossible */
|
|
1750
|
-
if (edge.from === target)
|
|
1751
|
-
continue OUTER
|
|
1752
|
-
// ignore this edge, it'll either be replaced or re-resolved
|
|
1753
|
-
if (currentPeerSet.has(edge.from))
|
|
1754
|
-
continue
|
|
1755
|
-
// ok, we care about this one.
|
|
1756
|
-
deps.add(edge.from)
|
|
1757
|
-
}
|
|
1758
|
-
reresolve.add(node)
|
|
1759
|
-
for (const d of deps)
|
|
1760
|
-
dependants.add(d)
|
|
1761
|
-
}
|
|
1762
|
-
for (const dependant of dependants) {
|
|
1763
|
-
this[_depsQueue].push(dependant)
|
|
1764
|
-
this[_depsSeen].delete(dependant)
|
|
1765
|
-
}
|
|
1766
|
-
for (const node of reresolve)
|
|
1767
|
-
node.root = null
|
|
1768
|
-
}
|
|
1769
|
-
}
|
|
1770
|
-
}
|
|
1771
|
-
|
|
1772
|
-
if (canReplace) {
|
|
1773
|
-
const ret = this[_canPlacePeers](dep, target, edge, REPLACE, peerEntryEdge, peerPath, isSource)
|
|
1774
|
-
/* istanbul ignore else - extremely rare that the peer set would
|
|
1775
|
-
* conflict if we can replace the node in question, but theoretically
|
|
1776
|
-
* possible, if peer deps are pinned aggressively. */
|
|
1777
|
-
if (ret !== CONFLICT)
|
|
1778
|
-
return ret
|
|
1779
|
-
}
|
|
1780
|
-
|
|
1781
|
-
// so it's not a deeper dep that's been deduped. That means that the
|
|
1782
|
-
// only way it could have ended up here is if it's a conflicted peer.
|
|
1783
|
-
/* istanbul ignore else - would have already crashed if not forced,
|
|
1784
|
-
* and either mine or strict, when creating the peerSet. Keeping this
|
|
1785
|
-
* check so that we're not only relying on action at a distance. */
|
|
1786
|
-
if (!this[_strictPeerDeps] && !isMine || this[_force]) {
|
|
1787
|
-
this[_warnPeerConflict](edge, dep)
|
|
1788
|
-
return KEEP
|
|
1789
|
-
}
|
|
1790
|
-
}
|
|
1791
|
-
|
|
1792
|
-
// no justification for overriding, and no agreement possible.
|
|
1793
|
-
return CONFLICT
|
|
1794
|
-
}
|
|
1795
|
-
|
|
1796
|
-
// no existing node at this location!
|
|
1797
|
-
// check to see if the target doesn't have a child by that name,
|
|
1798
|
-
// but WANTS one, and won't be happy with this one. if this is the
|
|
1799
|
-
// edge we're looking to resolve, then not relevant, of course.
|
|
1800
|
-
if (target !== entryEdge.from && target.edgesOut.has(dep.name)) {
|
|
1801
|
-
const targetEdge = target.edgesOut.get(dep.name)
|
|
1802
|
-
// It might be that the dep would not be valid here, BUT some other
|
|
1803
|
-
// version would. Could to try to resolve that, but that makes this no
|
|
1804
|
-
// longer a pure synchronous function. ugh.
|
|
1805
|
-
// This is a pretty unlikely scenario in a normal install, because we
|
|
1806
|
-
// resolve the peer dep set against the parent dependencies, and
|
|
1807
|
-
// presumably they all worked together SOMEWHERE to get published in the
|
|
1808
|
-
// first place, and since we resolve shallower deps before deeper ones,
|
|
1809
|
-
// this can only occur by a child having a peer dep that does not satisfy
|
|
1810
|
-
// the parent. It can happen if we're doing a deep update limited by
|
|
1811
|
-
// a specific name, however, or if a dep makes an incompatible change
|
|
1812
|
-
// to its peer dep in a non-semver-major version bump, or if the parent
|
|
1813
|
-
// is unbounded in its dependency list.
|
|
1814
|
-
if (!targetEdge.satisfiedBy(dep))
|
|
1815
|
-
return CONFLICT
|
|
1816
|
-
}
|
|
1817
|
-
|
|
1818
|
-
// check to see what that name resolves to here, and who may depend on
|
|
1819
|
-
// being able to reach it by crawling up past this parent. we know
|
|
1820
|
-
// at this point that it's not the target's direct child node. if it's
|
|
1821
|
-
// a direct dep of the target, we just make the invalid edge and
|
|
1822
|
-
// resolve it later.
|
|
1823
|
-
const current = target !== entryEdge.from && target.resolve(dep.name)
|
|
1824
|
-
if (current) {
|
|
1825
|
-
for (const edge of current.edgesIn.values()) {
|
|
1826
|
-
if (!edge.from.isTop && edge.from.isDescendantOf(target) && edge.valid) {
|
|
1827
|
-
if (!edge.satisfiedBy(dep))
|
|
1828
|
-
return CONFLICT
|
|
1829
|
-
}
|
|
1830
|
-
}
|
|
1831
|
-
}
|
|
1832
|
-
|
|
1833
|
-
// no objections! ok to place here
|
|
1834
|
-
return this[_canPlacePeers](dep, target, edge, OK, peerEntryEdge, peerPath, isSource)
|
|
1835
|
-
}
|
|
1836
|
-
|
|
1837
|
-
// make sure the family of peer deps can live here alongside it.
|
|
1838
|
-
// this doesn't guarantee that THIS solution will be the one we take,
|
|
1839
|
-
// but it does establish that SOME solution exists at this level in
|
|
1840
|
-
// the tree.
|
|
1841
|
-
[_canPlacePeers] (dep, target, edge, ret, peerEntryEdge, peerPath, isSource) {
|
|
1842
|
-
// do not go in cycles when we're resolving a peer group
|
|
1843
|
-
if (!dep.parent || peerEntryEdge && peerPath.includes(dep))
|
|
1844
|
-
return ret
|
|
1845
|
-
|
|
1846
|
-
const entryEdge = peerEntryEdge || edge
|
|
1847
|
-
peerPath = [...peerPath, dep]
|
|
1848
|
-
|
|
1849
|
-
for (const peerEdge of dep.edgesOut.values()) {
|
|
1850
|
-
if (!peerEdge.peer || !peerEdge.to)
|
|
1851
|
-
continue
|
|
1852
|
-
const peer = peerEdge.to
|
|
1853
|
-
const canPlacePeer = this[_canPlaceDep](peer, target, peerEdge, entryEdge, peerPath, isSource)
|
|
1854
|
-
if (canPlacePeer !== CONFLICT)
|
|
1855
|
-
continue
|
|
1856
|
-
|
|
1857
|
-
const current = target.resolve(peer.name)
|
|
1858
|
-
this[_peerConflict] = {
|
|
1859
|
-
peer: peer.explain(peerEdge),
|
|
1860
|
-
current: current && current.explain(),
|
|
1861
|
-
}
|
|
1862
|
-
return CONFLICT
|
|
1863
|
-
}
|
|
1864
|
-
return ret
|
|
1865
|
-
}
|
|
1866
|
-
|
|
1867
1294
|
// go through all the links in the this[_linkNodes] set
|
|
1868
1295
|
// for each one:
|
|
1869
1296
|
// - if outside the root, ignore it, assume it's fine, it's not our problem
|
|
@@ -1945,6 +1372,7 @@ This is a one-time fix-up, please be patient...
|
|
|
1945
1372
|
const needPrune = metaFromDisk && (mutateTree || flagsSuspect)
|
|
1946
1373
|
if (this[_prune] && needPrune)
|
|
1947
1374
|
this[_idealTreePrune]()
|
|
1375
|
+
|
|
1948
1376
|
process.emit('timeEnd', 'idealTree:fixDepFlags')
|
|
1949
1377
|
}
|
|
1950
1378
|
|