@@ -18,7 +18,8 @@ import {
1818 IPackageJson ,
1919 MapExtensions ,
2020 FileSystem ,
21- FileConstants
21+ FileConstants ,
22+ Sort
2223} from '@microsoft/node-core-library' ;
2324
2425import { ApprovedPackagesChecker } from '../logic/ApprovedPackagesChecker' ;
@@ -111,7 +112,8 @@ export class InstallManager {
111112 private _commonTempFolderRecycler : AsyncRecycler ;
112113
113114 /**
114- * Returns a map of all direct dependencies that only have a single semantic version specifier
115+ * Returns a map of all direct dependencies that only have a single semantic version specifier.
116+ * Returns a map: dependency name --> version specifier
115117 */
116118 public static collectImplicitlyPreferredVersions ( rushConfiguration : RushConfiguration ) : Map < string , string > {
117119 // First, collect all the direct dependencies of all local projects, and their versions:
@@ -381,6 +383,7 @@ export class InstallManager {
381383 shrinkwrapIsUpToDate = false ;
382384 }
383385
386+ // dependency name --> version specifier
384387 const allExplicitPreferredVersions : Map < string , string > = this . _rushConfiguration . commonVersions
385388 . getAllPreferredVersions ( ) ;
386389
@@ -429,6 +432,8 @@ export class InstallManager {
429432 // Find the implicitly preferred versions
430433 // These are any first-level dependencies for which we only consume a single version range
431434 // (e.g. every package that depends on react uses an identical specifier)
435+
436+ // dependency name --> version specifier
432437 const allPreferredVersions : Map < string , string > =
433438 InstallManager . collectImplicitlyPreferredVersions ( this . _rushConfiguration ) ;
434439
@@ -445,9 +450,7 @@ export class InstallManager {
445450 // To make the common/package.json file more readable, sort alphabetically
446451 // according to rushProject.tempProjectName instead of packageName.
447452 const sortedRushProjects : RushConfigurationProject [ ] = this . _rushConfiguration . projects . slice ( 0 ) ;
448- sortedRushProjects . sort (
449- ( a : RushConfigurationProject , b : RushConfigurationProject ) => a . tempProjectName . localeCompare ( b . tempProjectName )
450- ) ;
453+ Sort . sortBy ( sortedRushProjects , x => x . tempProjectName ) ;
451454
452455 for ( const rushProject of sortedRushProjects ) {
453456 const packageJson : PackageJsonEditor = rushProject . packageJsonEditor ;
@@ -469,65 +472,66 @@ export class InstallManager {
469472 dependencies : { }
470473 } ;
471474
472- // Collect pairs of (packageName, packageVersion) to be added as temp package dependencies
473- const pairs : { packageName : string , packageVersion : string } [ ] = [ ] ;
475+ // Collect pairs of (packageName, packageVersion) to be added as dependencies of the @rush- temp package.json
476+ const tempDependencies : Map < string , string > = new Map < string , string > ( ) ;
474477
478+ // These can be regular, optional, or peer dependencies (but NOT dev dependencies).
479+ // (A given packageName will never appear more than once in this list.)
475480 for ( const dependency of packageJson . dependencyList ) {
476481
477- // If there are any optional dependencies, copy them over directly
482+ // If there are any optional dependencies, copy directly into the optionalDependencies field.
478483 if ( dependency . dependencyType === DependencyType . Optional ) {
479484 if ( ! tempPackageJson . optionalDependencies ) {
480485 tempPackageJson . optionalDependencies = { } ;
481486 }
482487 tempPackageJson . optionalDependencies [ dependency . name ] = dependency . version ;
483488 } else {
484- pairs . push ( { packageName : dependency . name , packageVersion : dependency . version } ) ;
489+ tempDependencies . set ( dependency . name , dependency . version ) ;
485490 }
486491 }
487492
488493 for ( const dependency of packageJson . devDependencyList ) {
489- // If there are devDependencies, we need to merge them with the regular
490- // dependencies. If the same library appears in both places, then the
491- // regular dependency takes precedence over the devDependency.
492- // It also takes precedence over a duplicate in optionalDependencies,
493- // but NPM will take care of that for us. (Frankly any kind of duplicate
494- // should be an error, but NPM is pretty lax about this.)
495- pairs . push ( { packageName : dependency . name , packageVersion : dependency . version } ) ;
494+ // If there are devDependencies, we need to merge them with the regular dependencies. If the same
495+ // library appears in both places, then the dev dependency wins (because presumably it's saying what you
496+ // want right now for development, not the range that you support for consumers).
497+ tempDependencies . set ( dependency . name , dependency . version ) ;
496498 }
499+ Sort . sortMapKeys ( tempDependencies ) ;
497500
498- for ( const pair of pairs ) {
501+ for ( const [ packageName , packageVersion ] of tempDependencies . entries ( ) ) {
499502 // Is there a locally built Rush project that could satisfy this dependency?
500503 // If so, then we will symlink to the project folder rather than to common/temp/node_modules.
501504 // In this case, we don't want "npm install" to process this package, but we do need
502505 // to record this decision for "rush link" later, so we add it to a special 'rushDependencies' field.
503506 const localProject : RushConfigurationProject | undefined =
504- this . _rushConfiguration . getProjectByName ( pair . packageName ) ;
505- if ( localProject ) {
507+ this . _rushConfiguration . getProjectByName ( packageName ) ;
506508
509+ if ( localProject ) {
507510 // Don't locally link if it's listed in the cyclicDependencyProjects
508- if ( ! rushProject . cyclicDependencyProjects . has ( pair . packageName ) ) {
511+ if ( ! rushProject . cyclicDependencyProjects . has ( packageName ) ) {
509512
510513 // Also, don't locally link if the SemVer doesn't match
511514 const localProjectVersion : string = localProject . packageJsonEditor . version ;
512- if ( semver . satisfies ( localProjectVersion , pair . packageVersion ) ) {
515+ if ( semver . satisfies ( localProjectVersion , packageVersion ) ) {
513516
514- // We will locally link this package
517+ // We will locally link this package, so instead add it to our special "rushDependencies"
518+ // field in the package.json file.
515519 if ( ! tempPackageJson . rushDependencies ) {
516520 tempPackageJson . rushDependencies = { } ;
517521 }
518- tempPackageJson . rushDependencies [ pair . packageName ] = pair . packageVersion ;
522+ tempPackageJson . rushDependencies [ packageName ] = packageVersion ;
519523 continue ;
520524 }
521525 }
522526 }
523527
524528 // We will NOT locally link this package; add it as a regular dependency.
525- tempPackageJson . dependencies ! [ pair . packageName ] = pair . packageVersion ;
529+ tempPackageJson . dependencies ! [ packageName ] = packageVersion ;
526530
527531 if ( shrinkwrapFile ) {
528- if ( ! shrinkwrapFile . tryEnsureCompatibleDependency ( pair . packageName , pair . packageVersion ,
532+ if ( ! shrinkwrapFile . tryEnsureCompatibleDependency ( packageName , packageVersion ,
529533 rushProject . tempProjectName ) ) {
530- shrinkwrapWarnings . push ( `"${ pair . packageName } " (${ pair . packageVersion } ) required by`
534+ shrinkwrapWarnings . push ( `"${ packageName } " (${ packageVersion } ) required by`
531535 + ` "${ rushProject . packageName } "` ) ;
532536 shrinkwrapIsUpToDate = false ;
533537 }
0 commit comments