@@ -2191,11 +2191,10 @@ function applyVirtualResolutionMutations({
2191
2191
2192
2192
const allIdents = new Map < IdentHash , Ident > ( ) ;
2193
2193
2194
- /** Maps dependency hashes to the first virtual locator encountered with that hash, for deduplication */
2195
- const allVirtualInstances = new Map < string , Locator > ( ) ;
2196
- const allVirtualDependents = new Map < LocatorHash , Set < LocatorHash > > ( ) ;
2197
- /** Maps virtual locators to all (virtual) descriptors that resolve to them, for deduplication */
2198
- const allVirtualResolutions = new Map < LocatorHash , Set < DescriptorHash > > ( ) ;
2194
+ // We'll be keeping track of all virtual descriptors; once they have all
2195
+ // been generated we'll check whether they can be deduplicated into one.
2196
+ const allVirtualInstances = new Map < LocatorHash , Map < string , Descriptor > > ( ) ;
2197
+ const allVirtualDependents = new Map < DescriptorHash , Set < LocatorHash > > ( ) ;
2199
2198
2200
2199
const allPeerRequests = new Map < LocatorHash , Map < IdentHash , PeerRequestNode > > ( ) ;
2201
2200
@@ -2264,7 +2263,7 @@ function applyVirtualResolutionMutations({
2264
2263
if ( ! parentPackage )
2265
2264
throw new Error ( `Assertion failed: The package (${ structUtils . prettyLocator ( project . configuration , parentLocator ) } ) should have been registered` ) ;
2266
2265
2267
- const dedupeCandidates = new Set < LocatorHash > ( ) ;
2266
+ const newVirtualInstances : Array < [ Locator , Descriptor , Package ] > = [ ] ;
2268
2267
const parentPeerRequirements = new Map < IdentHash , PeerRequirementNode > ( ) ;
2269
2268
2270
2269
const firstPass = [ ] ;
@@ -2323,15 +2322,16 @@ function applyVirtualResolutionMutations({
2323
2322
virtualizedDescriptor = structUtils . virtualizeDescriptor ( descriptor , parentLocator . locatorHash ) ;
2324
2323
virtualizedPackage = structUtils . virtualizePackage ( pkg , parentLocator . locatorHash ) ;
2325
2324
2326
- parentPackage . dependencies . set ( descriptor . identHash , virtualizedDescriptor ) ;
2325
+ parentPackage . dependencies . delete ( descriptor . identHash ) ;
2326
+ parentPackage . dependencies . set ( virtualizedDescriptor . identHash , virtualizedDescriptor ) ;
2327
2327
2328
2328
allResolutions . set ( virtualizedDescriptor . descriptorHash , virtualizedPackage . locatorHash ) ;
2329
2329
allDescriptors . set ( virtualizedDescriptor . descriptorHash , virtualizedDescriptor ) ;
2330
2330
2331
2331
allPackages . set ( virtualizedPackage . locatorHash , virtualizedPackage ) ;
2332
2332
2333
- miscUtils . getSetWithDefault ( allVirtualResolutions , virtualizedPackage . locatorHash ) . add ( virtualizedDescriptor . descriptorHash ) ;
2334
- dedupeCandidates . add ( virtualizedPackage . locatorHash ) ;
2333
+ // Keep track of all new virtual packages since we'll want to dedupe them
2334
+ newVirtualInstances . push ( [ pkg , virtualizedDescriptor , virtualizedPackage ] ) ;
2335
2335
} ) ;
2336
2336
2337
2337
// In the second pass we resolve the peer requests to their provision.
@@ -2397,12 +2397,10 @@ function applyVirtualResolutionMutations({
2397
2397
2398
2398
virtualizedPackage . dependencies . set ( peerDescriptor . identHash , peerProvision ) ;
2399
2399
2400
- // Need to keep track when a virtual depends on a sibling virtual so
2401
- // that if and when the latter is deduplicated, we know the former
2402
- // needs to be deduplicated again
2400
+ // Need to track when a virtual descriptor is set as a dependency in case
2401
+ // the descriptor will be deduplicated.
2403
2402
if ( structUtils . isVirtualDescriptor ( peerProvision ) ) {
2404
- const dependentLocatorHash = allResolutions . get ( peerProvision . descriptorHash ) ;
2405
- const dependents = miscUtils . getSetWithDefault ( allVirtualDependents , dependentLocatorHash ) ;
2403
+ const dependents = miscUtils . getSetWithDefault ( allVirtualDependents , peerProvision . descriptorHash ) ;
2406
2404
dependents . add ( virtualizedPackage . locatorHash ) ;
2407
2405
}
2408
2406
@@ -2449,7 +2447,11 @@ function applyVirtualResolutionMutations({
2449
2447
// In the fourth pass, we register information about the peer requirement
2450
2448
// and peer request trees, using the post-deduplication information.
2451
2449
fourthPass . push ( ( ) => {
2452
- const finalResolution = allResolutions . get ( virtualizedDescriptor . descriptorHash ) ! ;
2450
+ const finalDescriptor = parentPackage . dependencies . get ( descriptor . identHash ) ;
2451
+ if ( typeof finalDescriptor === `undefined` )
2452
+ throw new Error ( `Assertion failed: Expected the peer dependency to have been turned into a dependency` ) ;
2453
+
2454
+ const finalResolution = allResolutions . get ( finalDescriptor . descriptorHash ) ! ;
2453
2455
if ( typeof finalResolution === `undefined` )
2454
2456
throw new Error ( `Assertion failed: Expected the descriptor to be registered` ) ;
2455
2457
@@ -2462,10 +2464,10 @@ function applyVirtualResolutionMutations({
2462
2464
if ( ! peerRequest )
2463
2465
continue ;
2464
2466
2465
- peerRequirement . requests . set ( virtualizedDescriptor . descriptorHash , peerRequest ) ;
2467
+ peerRequirement . requests . set ( finalDescriptor . descriptorHash , peerRequest ) ;
2466
2468
peerRequirementNodes . set ( peerRequirement . hash , peerRequirement ) ;
2467
2469
if ( ! peerRequirement . root ) {
2468
- parentPeerRequests . get ( peerRequirement . ident . identHash ) ?. children . set ( virtualizedDescriptor . descriptorHash , peerRequest ) ;
2470
+ parentPeerRequests . get ( peerRequirement . ident . identHash ) ?. children . set ( finalDescriptor . descriptorHash , peerRequest ) ;
2469
2471
}
2470
2472
}
2471
2473
@@ -2481,63 +2483,76 @@ function applyVirtualResolutionMutations({
2481
2483
for ( const fn of [ ...firstPass , ...secondPass ] )
2482
2484
fn ( ) ;
2483
2485
2484
- for ( const locatorHash of dedupeCandidates ) {
2485
- // Remove locatorHash here so that if a dependency is deduped, it will be
2486
- // deduped again when added to the dedupe candidates
2487
- dedupeCandidates . delete ( locatorHash ) ;
2486
+ let stable : boolean ;
2487
+ do {
2488
+ stable = true ;
2488
2489
2489
- const virtualPackage = allPackages . get ( locatorHash ) ! ;
2490
+ for ( const [ physicalLocator , virtualDescriptor , virtualPackage ] of newVirtualInstances ) {
2491
+ const otherVirtualInstances = miscUtils . getMapWithDefault ( allVirtualInstances , physicalLocator . locatorHash ) ;
2490
2492
2491
- // We take all the dependencies from the new virtual instance and
2492
- // generate a hash from it. By checking if this hash is already
2493
- // registered, we know whether we can trim the new version.
2494
- const dependencyHash = hashUtils . makeHash (
2495
- structUtils . devirtualizeLocator ( virtualPackage ) . locatorHash ,
2496
- ...Array . from ( virtualPackage . dependencies . values ( ) , descriptor => {
2497
- const resolution = descriptor . range !== `missing:`
2498
- ? allResolutions . get ( descriptor . descriptorHash )
2499
- : `missing:` ;
2493
+ // We take all the dependencies from the new virtual instance and
2494
+ // generate a hash from it. By checking if this hash is already
2495
+ // registered, we know whether we can trim the new version.
2496
+ const dependencyHash = hashUtils . makeHash (
2497
+ ...[ ...virtualPackage . dependencies . values ( ) ] . map ( descriptor => {
2498
+ const resolution = descriptor . range !== `missing:`
2499
+ ? allResolutions . get ( descriptor . descriptorHash )
2500
+ : `missing:` ;
2500
2501
2501
- if ( typeof resolution === `undefined` )
2502
- throw new Error ( `Assertion failed: Expected the resolution for ${ structUtils . prettyDescriptor ( project . configuration , descriptor ) } to have been registered` ) ;
2502
+ if ( typeof resolution === `undefined` )
2503
+ throw new Error ( `Assertion failed: Expected the resolution for ${ structUtils . prettyDescriptor ( project . configuration , descriptor ) } to have been registered` ) ;
2503
2504
2504
- return resolution === top ? `${ resolution } (top)` : resolution ;
2505
- } ) ,
2506
- ) ;
2505
+ return resolution === top ? `${ resolution } (top)` : resolution ;
2506
+ } ) ,
2507
+ // We use the identHash to disambiguate between virtual descriptors
2508
+ // with different base idents being resolved to the same virtual package.
2509
+ // Note: We don't use the descriptorHash because the whole point of duplicate
2510
+ // virtual descriptors is that they have different `virtual:` ranges.
2511
+ // This causes the virtual descriptors with different base idents
2512
+ // to be preserved, while the virtual package they resolve to gets deduped.
2513
+ virtualDescriptor . identHash ,
2514
+ ) ;
2507
2515
2508
- const masterLocator = allVirtualInstances . get ( dependencyHash ) ;
2509
- if ( typeof masterLocator === `undefined` ) {
2510
- allVirtualInstances . set ( dependencyHash , virtualPackage ) ;
2511
- continue ;
2512
- }
2516
+ const masterDescriptor = otherVirtualInstances . get ( dependencyHash ) ;
2517
+ if ( typeof masterDescriptor === `undefined` ) {
2518
+ otherVirtualInstances . set ( dependencyHash , virtualDescriptor ) ;
2519
+ continue ;
2520
+ }
2513
2521
2514
- // Change every descriptor that is resolving to the virtual package to
2515
- // resolve to the master locator instead, then discard the virtual
2516
- // package
2517
- const masterResolutions = miscUtils . getSetWithDefault ( allVirtualResolutions , masterLocator . locatorHash ) ;
2518
- for ( const descriptorHash of allVirtualResolutions . get ( virtualPackage . locatorHash ) ?? [ ] ) {
2519
- allResolutions . set ( descriptorHash , masterLocator . locatorHash ) ;
2520
- masterResolutions . add ( descriptorHash ) ;
2521
- }
2522
- allPackages . delete ( virtualPackage . locatorHash ) ;
2523
- accessibleLocators . delete ( virtualPackage . locatorHash ) ;
2524
- dedupeCandidates . delete ( virtualPackage . locatorHash ) ;
2525
-
2526
- const dependents = allVirtualDependents . get ( virtualPackage . locatorHash ) ;
2527
- if ( dependents !== undefined ) {
2528
- const masterDependents = miscUtils . getSetWithDefault ( allVirtualDependents , masterLocator . locatorHash ) ;
2529
- for ( const dependent of dependents ) {
2530
- // A dependent of the virtual package is now a dependent of the
2531
- // master package
2532
- masterDependents . add ( dependent ) ;
2533
-
2534
- // Virtual packages that depended on the deduplicated package would
2535
- // get a different dependency hash now, so we need to deduplicate
2536
- // them again
2537
- dedupeCandidates . add ( dependent ) ;
2522
+ // Since we're applying multiple pass, we might have already registered
2523
+ // ourselves as the "master" descriptor in the previous pass.
2524
+ if ( masterDescriptor === virtualDescriptor )
2525
+ continue ;
2526
+
2527
+ allPackages . delete ( virtualPackage . locatorHash ) ;
2528
+ allDescriptors . delete ( virtualDescriptor . descriptorHash ) ;
2529
+ allResolutions . delete ( virtualDescriptor . descriptorHash ) ;
2530
+
2531
+ accessibleLocators . delete ( virtualPackage . locatorHash ) ;
2532
+
2533
+ const dependents = allVirtualDependents . get ( virtualDescriptor . descriptorHash ) || [ ] ;
2534
+ const allDependents = [ parentPackage . locatorHash , ...dependents ] ;
2535
+
2536
+ allVirtualDependents . delete ( virtualDescriptor . descriptorHash ) ;
2537
+
2538
+ for ( const dependent of allDependents ) {
2539
+ const pkg = allPackages . get ( dependent ) ;
2540
+ if ( typeof pkg === `undefined` )
2541
+ continue ;
2542
+
2543
+ if ( pkg . dependencies . get ( virtualDescriptor . identHash ) ! . descriptorHash !== masterDescriptor . descriptorHash )
2544
+ stable = false ;
2545
+
2546
+ pkg . dependencies . set ( virtualDescriptor . identHash , masterDescriptor ) ;
2547
+ }
2548
+
2549
+ for ( const peerRequirement of parentPeerRequirements . values ( ) ) {
2550
+ if ( peerRequirement . provided . descriptorHash === virtualDescriptor . descriptorHash ) {
2551
+ peerRequirement . provided = masterDescriptor ;
2552
+ }
2538
2553
}
2539
2554
}
2540
- }
2555
+ } while ( ! stable ) ;
2541
2556
2542
2557
for ( const fn of [ ...thirdPass , ...fourthPass ] ) {
2543
2558
fn ( ) ;
0 commit comments