1- import { Project , ResolveOptions , ThrowReport , Resolver , miscUtils , Descriptor , Package , Report , Cache } from '@yarnpkg/core' ;
2- import { formatUtils , structUtils , IdentHash , LocatorHash , MessageName , Fetcher , FetchOptions } from '@yarnpkg/core' ;
3- import micromatch from 'micromatch' ;
1+ import { Project , ResolveOptions , ThrowReport , Resolver , miscUtils , Descriptor , Package , Report , Cache , DescriptorHash } from '@yarnpkg/core' ;
2+ import { formatUtils , structUtils , IdentHash , LocatorHash , MessageName , Fetcher , FetchOptions } from '@yarnpkg/core' ;
3+ import micromatch from 'micromatch' ;
4+
5+ export type PackageUpdate = {
6+ descriptor : Descriptor ;
7+ currentPackage : Package ;
8+ updatedPackage : Package ;
9+ resolvedPackage : Package ;
10+ } ;
411
512export type Algorithm = ( project : Project , patterns : Array < string > , opts : {
613 resolver : Resolver ;
714 resolveOptions : ResolveOptions ;
815 fetcher : Fetcher ;
916 fetchOptions : FetchOptions ;
10- } ) => Promise < Array < Promise < {
11- descriptor : Descriptor ;
12- currentPackage : Package ;
13- updatedPackage : Package ;
14- } | null > > > ;
17+ } ) => Promise < Array < Promise < PackageUpdate > > > ;
1518
1619export enum Strategy {
1720 /**
@@ -37,57 +40,100 @@ const DEDUPE_ALGORITHMS: Record<Strategy, Algorithm> = {
3740 miscUtils . getSetWithDefault ( locatorsByIdent , descriptor . identHash ) . add ( locatorHash ) ;
3841 }
3942
40- return Array . from ( project . storedDescriptors . values ( ) , async descriptor => {
41- if ( patterns . length && ! micromatch . isMatch ( structUtils . stringifyIdent ( descriptor ) , patterns ) )
42- return null ;
43+ const deferredMap = new Map < DescriptorHash , miscUtils . Deferred < PackageUpdate > > (
44+ miscUtils . mapAndFilter ( project . storedDescriptors . values ( ) , descriptor => {
45+ // We only care about resolutions that are stored in the lockfile
46+ // (we shouldn't accidentally try deduping virtual packages)
47+ if ( structUtils . isVirtualDescriptor ( descriptor ) )
48+ return miscUtils . mapAndFilter . skip ;
49+
50+ return [ descriptor . descriptorHash , miscUtils . makeDeferred ( ) ] ;
51+ } ) ,
52+ ) ;
53+
54+ for ( const descriptor of project . storedDescriptors . values ( ) ) {
55+ const deferred = deferredMap . get ( descriptor . descriptorHash ) ;
56+ if ( typeof deferred === `undefined` )
57+ throw new Error ( `Assertion failed: The descriptor (${ descriptor . descriptorHash } ) should have been registered` ) ;
4358
4459 const currentResolution = project . storedResolutions . get ( descriptor . descriptorHash ) ;
4560 if ( typeof currentResolution === `undefined` )
4661 throw new Error ( `Assertion failed: The resolution (${ descriptor . descriptorHash } ) should have been registered` ) ;
4762
48- // We only care about resolutions that are stored in the lockfile
49- // (we shouldn't accidentally try deduping virtual packages)
5063 const currentPackage = project . originalPackages . get ( currentResolution ) ;
5164 if ( typeof currentPackage === `undefined` )
52- return null ;
53-
54- // No need to try deduping packages that are not persisted,
55- // they will be resolved again anyways
56- if ( ! resolver . shouldPersistResolution ( currentPackage , resolveOptions ) )
57- return null ;
58-
59- const locators = locatorsByIdent . get ( descriptor . identHash ) ;
60- if ( typeof locators === `undefined` )
61- throw new Error ( `Assertion failed: The resolutions (${ descriptor . identHash } ) should have been registered` ) ;
62-
63- // No need to choose when there's only one possibility
64- if ( locators . size === 1 )
65- return null ;
66-
67- const references = [ ...locators ] . map ( locatorHash => {
68- const pkg = project . originalPackages . get ( locatorHash ) ;
69- if ( typeof pkg === `undefined` )
70- throw new Error ( `Assertion failed: The package (${ locatorHash } ) should have been registered` ) ;
71-
72- return pkg . reference ;
65+ throw new Error ( `Assertion failed: The package (${ currentResolution } ) should have been registered` ) ;
66+
67+ Promise . resolve ( ) . then ( async ( ) => {
68+ const dependencies = resolver . getResolutionDependencies ( descriptor , resolveOptions ) ;
69+
70+ const resolvedDependencies = Object . fromEntries (
71+ await miscUtils . allSettledSafe (
72+ Object . entries ( dependencies ) . map ( async ( [ dependencyName , dependency ] ) => {
73+ const dependencyDeferred = deferredMap . get ( dependency . descriptorHash ) ;
74+ if ( typeof dependencyDeferred === `undefined` )
75+ throw new Error ( `Assertion failed: The descriptor (${ dependency . descriptorHash } ) should have been registered` ) ;
76+
77+ const dedupeResult = await dependencyDeferred . promise ;
78+ if ( ! dedupeResult )
79+ throw new Error ( `Assertion failed: Expected the dependency to have been through the dedupe process itself` ) ;
80+
81+ return [ dependencyName , dedupeResult . updatedPackage ] ;
82+ } ) ,
83+ ) ,
84+ ) ;
85+
86+ if ( patterns . length && ! micromatch . isMatch ( structUtils . stringifyIdent ( descriptor ) , patterns ) )
87+ return currentPackage ;
88+
89+ // No need to try deduping packages that are not persisted,
90+ // they will be resolved again anyways
91+ if ( ! resolver . shouldPersistResolution ( currentPackage , resolveOptions ) )
92+ return currentPackage ;
93+
94+ const candidateHashes = locatorsByIdent . get ( descriptor . identHash ) ;
95+ if ( typeof candidateHashes === `undefined` )
96+ throw new Error ( `Assertion failed: The resolutions (${ descriptor . identHash } ) should have been registered` ) ;
97+
98+ // No need to choose when there's only one possibility
99+ if ( candidateHashes . size === 1 )
100+ return currentPackage ;
101+
102+ const candidates = [ ...candidateHashes ] . map ( locatorHash => {
103+ const pkg = project . originalPackages . get ( locatorHash ) ;
104+ if ( typeof pkg === `undefined` )
105+ throw new Error ( `Assertion failed: The package (${ locatorHash } ) should have been registered` ) ;
106+
107+ return pkg ;
108+ } ) ;
109+
110+ const satisfying = await resolver . getSatisfying ( descriptor , resolvedDependencies , candidates , resolveOptions ) ;
111+
112+ const bestLocator = satisfying . locators ?. [ 0 ] ;
113+ if ( typeof bestLocator === `undefined` || ! satisfying . sorted )
114+ return currentPackage ;
115+
116+ const updatedPackage = project . originalPackages . get ( bestLocator . locatorHash ) ;
117+ if ( typeof updatedPackage === `undefined` )
118+ throw new Error ( `Assertion failed: The package (${ bestLocator . locatorHash } ) should have been registered` ) ;
119+
120+ return updatedPackage ;
121+ } ) . then ( async updatedPackage => {
122+ const resolvedPackage = await project . preparePackage ( updatedPackage , { resolver, resolveOptions} ) ;
123+
124+ deferred . resolve ( {
125+ descriptor,
126+ currentPackage,
127+ updatedPackage,
128+ resolvedPackage,
129+ } ) ;
130+ } ) . catch ( error => {
131+ deferred . reject ( error ) ;
73132 } ) ;
133+ }
74134
75- const candidates = await resolver . getSatisfying ( descriptor , references , resolveOptions ) ;
76-
77- const bestCandidate = candidates ?. [ 0 ] ;
78- if ( typeof bestCandidate === `undefined` )
79- return null ;
80-
81- const updatedResolution = bestCandidate . locatorHash ;
82-
83- const updatedPackage = project . originalPackages . get ( updatedResolution ) ;
84- if ( typeof updatedPackage === `undefined` )
85- throw new Error ( `Assertion failed: The package (${ updatedResolution } ) should have been registered` ) ;
86-
87- if ( updatedResolution === currentResolution )
88- return null ;
89-
90- return { descriptor, currentPackage, updatedPackage} ;
135+ return [ ...deferredMap . values ( ) ] . map ( deferred => {
136+ return deferred . promise ;
91137 } ) ;
92138 } ,
93139} ;
@@ -137,7 +183,7 @@ export async function dedupe(project: Project, {strategy, patterns, cache, repor
137183 dedupePromises . map ( dedupePromise =>
138184 dedupePromise
139185 . then ( dedupe => {
140- if ( dedupe === null )
186+ if ( dedupe === null || dedupe . currentPackage . locatorHash === dedupe . updatedPackage . locatorHash )
141187 return ;
142188
143189 dedupedPackageCount ++ ;
0 commit comments