@@ -33,7 +33,7 @@ import { ExperimentsConfiguration } from './ExperimentsConfiguration';
33
33
import { PackageNameParsers } from './PackageNameParsers' ;
34
34
import { RepoStateFile } from '../logic/RepoStateFile' ;
35
35
import { LookupByPath } from '../logic/LookupByPath' ;
36
- import { PackageJsonDependency } from './PackageJsonEditor' ;
36
+ import { DependencyType , PackageJsonDependency } from './PackageJsonEditor' ;
37
37
import { RushPluginsConfiguration } from './RushPluginsConfiguration' ;
38
38
39
39
const MINIMUM_SUPPORTED_RUSH_JSON_VERSION : string = '0.0.0' ;
@@ -502,6 +502,8 @@ export class RushConfiguration {
502
502
private _commonVersionsConfigurations : Map < string , CommonVersionsConfiguration > | undefined ;
503
503
// variant -> map of package name -> implicitly preferred version
504
504
private _implicitlyPreferredVersions : Map < string , Map < string , string > > | undefined ;
505
+ // variant -> map of package name to a set of specified dependency versions
506
+ private _allDependencyVersions : Map < string , Map < string , Set < string > > > | undefined ;
505
507
506
508
private _versionPolicyConfiguration : VersionPolicyConfiguration ;
507
509
private _versionPolicyConfigurationFilePath : string ;
@@ -1586,15 +1588,11 @@ export class RushConfiguration {
1586
1588
}
1587
1589
1588
1590
// Use an empty string as the key when no variant provided. Anything else would possibly conflict
1589
- // with a varient created by the user
1591
+ // with a variant created by the user
1590
1592
const variantKey : string = variant || '' ;
1591
1593
let implicitlyPreferredVersions : Map < string , string > | undefined =
1592
1594
this . _implicitlyPreferredVersions . get ( variantKey ) ;
1593
1595
if ( ! implicitlyPreferredVersions ) {
1594
- // First, collect all the direct dependencies of all local projects, and their versions:
1595
A3E2
- // direct dependency name --> set of version specifiers
1596
- const versionsForDependencies : Map < string , Set < string > > = new Map < string , Set < string > > ( ) ;
1597
-
1598
1596
// Only generate implicitly preferred versions for variants that request it
1599
1597
const commonVersionsConfiguration : CommonVersionsConfiguration = this . getCommonVersions ( variant ) ;
1600
1598
const useImplicitlyPreferredVersions : boolean =
@@ -1603,14 +1601,9 @@ export class RushConfiguration {
1603
1601
: true ;
1604
1602
1605
1603
if ( useImplicitlyPreferredVersions ) {
1606
- for ( const project of this . projects ) {
1607
- this . _collectVersionsForDependencies (
1608
- versionsForDependencies ,
1609
- [ ...project . packageJsonEditor . dependencyList , ...project . packageJsonEditor . devDependencyList ] ,
1610
- project . cyclicDependencyProjects ,
1611
- variant
1612
- ) ;
1613
- }
1604
+ // First, collect all the direct dependencies of all local projects, and their versions:
1605
+ // direct dependency name --> set of version specifiers
1606
+ const versionsForDependencies : Map < string , Set < string > > = this . _getAllDependencyVersions ( variant ) ;
1614
1607
1615
1608
// If any dependency has more than one version, then filter it out (since we don't know which version
1616
1609
// should be preferred). What remains will be the list of preferred dependencies.
@@ -1803,59 +1796,83 @@ export class RushConfiguration {
1803
1796
return undefined ;
1804
1797
}
1805
1798
1806
- private _collectVersionsForDependencies (
1807
- versionsForDependencies : Map < string , Set < string > > ,
1808
- dependencies : ReadonlyArray < PackageJsonDependency > ,
1809
- cyclicDependencies : Set < string > ,
1810
- variant : string | undefined
1811
- ) : void {
1812
- const commonVersions : CommonVersionsConfiguration = this . getCommonVersions ( variant ) ;
1813
- const allowedAlternativeVersions : Map <
1814
- string ,
1815
- ReadonlyArray < string >
1816
- > = commonVersions . allowedAlternativeVersions ;
1817
-
1818
- for ( const dependency of dependencies ) {
1819
- const alternativesForThisDependency : ReadonlyArray < string > =
1820
- allowedAlternativeVersions . get ( dependency . name ) || [ ] ;
1821
-
1822
- // For each dependency, collectImplicitlyPreferredVersions() is collecting the set of all version specifiers
1823
- // that appear across the repo. If there is only one version specifier, then that's the "preferred" one.
1824
- // However, there are a few cases where additional version specifiers can be safely ignored.
1825
- let ignoreVersion : boolean = false ;
1826
-
1827
- // 1. If the version specifier was listed in "allowedAlternativeVersions", then it's never a candidate.
1828
- // (Even if it's the only version specifier anywhere in the repo, we still ignore it, because
1829
- // otherwise the rule would be difficult to explain.)
1830
- if ( alternativesForThisDependency . indexOf ( dependency . version ) > 0 ) {
1831
- ignoreVersion = true ;
1832
- } else {
1833
- // Is it a local project?
1834
- const localProject : RushConfigurationProject | undefined = this . getProjectByName ( dependency . name ) ;
1835
- if ( localProject ) {
1836
- // 2. If it's a symlinked local project, then it's not a candidate, because the package manager will
1837
- // never even see it.
1838
- // However there are two ways that a local project can NOT be symlinked:
1839
- // - if the local project doesn't satisfy the referenced semver specifier; OR
1840
- // - if the local project was specified in "cyclicDependencyProjects" in rush.json
1841
- if (
1842
- ! cyclicDependencies . has ( dependency . name ) &&
1843
- semver . satisfies ( localProject . packageJsonEditor . version , dependency . version )
1844
- ) {
1845
- ignoreVersion = true ;
1799
+ /**
1800
+ * @internal
1801
+ */
1802
+ public _getAllDependencyVersions ( variant ?: string | undefined ) : Map < string , Set < string > > {
1803
+ if ( ! this . _allDependencyVersions ) {
1804
+ this . _allDependencyVersions = new Map ( ) ;
1805
+ }
1806
+
1807
+ const variantKey : string = variant || '' ;
1808
+ let allDependencyVersions : Map < string , Set < string > > | undefined =
1809
+ this . _allDependencyVersions . get ( variantKey ) ;
1810
+ if ( ! allDependencyVersions ) {
1811
+ allDependencyVersions = new Map ( ) ;
1812
+
1813
+ const commonVersions : CommonVersionsConfiguration = this . getCommonVersions ( variant ) ;
1814
+ const allowedAlternativeVersions : Map <
1815
+ string ,
1816
+ ReadonlyArray < string >
1817
+ > = commonVersions . allowedAlternativeVersions ;
1818
+ for ( const project of this . projects ) {
1819
+ const dependencies : PackageJsonDependency [ ] = [
1820
+ ...project . packageJsonEditor . dependencyList ,
1821
+ ...project . packageJsonEditor . devDependencyList
1822
+ ] ;
1823
+ for ( const { name : dependencyName , version : dependencyVersion , dependencyType } of dependencies ) {
1824
+ if ( dependencyType === DependencyType . Peer ) {
1825
+ // If this is a peer dependency, it isn't a real dependency in this context, so it shouldn't
1826
+ // be included in the list of dependency versions.
1827
+ continue ;
1846
1828
}
1847
- }
1848
1829
1849
- if ( ! ignoreVersion ) {
1850
- let versionForDependency : Set < string > | undefined = versionsForDependencies . get ( dependency . name ) ;
1851
- if ( ! versionForDependency ) {
1852
- versionForDependency = new Set < string > ( ) ;
1853
- versionsForDependencies . set ( dependency . name , versionForDependency ) ;
1830
+ const alternativesForThisDependency : ReadonlyArray < string > =
1831
+ allowedAlternativeVersions . get ( dependencyName ) || [ ] ;
1832
+
1833
+ // For each dependency, collectImplicitlyPreferredVersions() is collecting the set of all version specifiers
1834
+ // that appear across the repo. If there is only one version specifier, then that's the "preferred" one.
1835
+ // However, there are a few cases where additional version specifiers can be safely ignored.
1836
+ let ignoreVersion : boolean = false ;
1837
+
1838
+ // 1. If the version specifier was listed in "allowedAlternativeVersions", then it's never a candidate.
1839
+ // (Even if it's the only version specifier anywhere in the repo, we still ignore it, because
1840
+ // otherwise the rule would be difficult to explain.)
1841
+ if ( alternativesForThisDependency . indexOf ( dependencyVersion ) > 0 ) {
1842
+ ignoreVersion = true ;
1843
+ } else {
1844
+ // Is it a local project?
1845
+ const localProject : RushConfigurationProject | undefined = this . getProjectByName ( dependencyName ) ;
1846
+ if ( localProject ) {
1847
+ // 2. If it's a symlinked local project, then it's not a candidate, because the package manager will
1848
+ // never even see it.
1849
+ // However there are two ways that a local project can NOT be symlinked:
1850
+ // - if the local project doesn't satisfy the referenced semver specifier; OR
1851
+ // - if the local project was specified in "cyclicDependencyProjects" in rush.json
1852
+ if (
1853
+ ! project . cyclicDependencyProjects . has ( dependencyName ) &&
1854
+ semver . satisfies ( localProject . packageJsonEditor . version , dependencyVersion )
1855
+ ) {
1856
+ ignoreVersion = true ;
1857
+ }
1858
+ }
1859
+
1860
+ if ( ! ignoreVersion ) {
1861
+ let versionForDependency : Set < string > | undefined = allDependencyVersions . get ( dependencyName ) ;
1862
+ if ( ! versionForDependency ) {
1863
+ versionForDependency = new Set < string > ( ) ;
1864
+ allDependencyVersions . set ( dependencyName , versionForDependency ) ;
1865
+ }
1866
+ versionForDependency . add ( dependencyVersion ) ;
1867
+ }
1854
1868
}
1855
- versionForDependency . add ( dependency . version ) ;
1856
1869
}
1857
1870
}
1871
+
1872
+ this . _allDependencyVersions . set ( variantKey, allDependencyVersions ) ;
1858
1873
}
1874
+
1875
+ return allDependencyVersions ;
1859
1876
}
1860
1877
1861
1878
private _getVariantConfigFolderPath ( variant ?: string | undefined ) : string {
0 commit comments