@@ -5,7 +5,7 @@ import colors from 'colors/safe';
55import * as os from 'os' ;
66import * as path from 'path' ;
77import * as semver from 'semver' ;
8- import { FileSystem , FileConstants , AlreadyReportedError } from '@rushstack/node-core-library' ;
8+ import { FileSystem , FileConstants , AlreadyReportedError , Async } from '@rushstack/node-core-library' ;
99
1010import { BaseInstallManager , IInstallManagerOptions } from '../base/BaseInstallManager' ;
1111import { BaseShrinkwrapFile } from '../../logic/base/BaseShrinkwrapFile' ;
@@ -21,6 +21,7 @@ import { RepoStateFile } from '../RepoStateFile';
2121import { LastLinkFlagFactory } from '../../api/LastLinkFlag' ;
2222import { EnvironmentConfiguration } from '../../api/EnvironmentConfiguration' ;
2323import { ShrinkwrapFileFactory } from '../ShrinkwrapFileFactory' ;
24+ import { BaseProjectShrinkwrapFile } from '../base/BaseProjectShrinkwrapFile' ;
2425
2526/**
2627 * This class implements common logic between "rush install" and "rush update".
@@ -373,19 +374,44 @@ export class WorkspaceInstallManager extends BaseInstallManager {
373374protected async postInstallAsync ( ) :Promise < void > {
374375// Grab the temp shrinkwrap, as this was the most recently completed install. It may also be
375376// more up-to-date than the checked-in shrinkwrap since filtered installs are not written back.
376- const tempShrinkwrapFile :BaseShrinkwrapFile = ShrinkwrapFileFactory . getShrinkwrapFile (
377+ // Note that if there are no projects, or if we're in PNPM workspace mode and there are no
378+ // projects with dependencies, a lockfile won't be generated.
379+ const tempShrinkwrapFile :BaseShrinkwrapFile | undefined = ShrinkwrapFileFactory . getShrinkwrapFile (
377380this . rushConfiguration . packageManager ,
378381this . rushConfiguration . pnpmOptions ,
379382this . rushConfiguration . tempShrinkwrapFilename
380- ) ! ;
381-
382- // Write or delete all project shrinkwraps related to the install
383- await Promise . all (
384- this . rushConfiguration . projects . map ( async ( project ) => {
385- await tempShrinkwrapFile . getProjectShrinkwrap ( project ) ?. updateProjectShrinkwrapAsync ( ) ;
386- } )
387383) ;
388384
385+ if ( tempShrinkwrapFile ) {
386+ // Write or delete all project shrinkwraps related to the install
387+ await Async . forEachAsync (
388+ this . rushConfiguration . projects ,
389+ async ( project ) => {
390+ await tempShrinkwrapFile . getProjectShrinkwrap ( project ) ?. updateProjectShrinkwrapAsync ( ) ;
391+ } ,
392+ { concurrency :10 }
393+ ) ;
394+ } else if (
395+ this . rushConfiguration . packageManager === 'pnpm' &&
396+ this . rushConfiguration . pnpmOptions ?. useWorkspaces
397+ ) {
398+ // If we're in PNPM workspace mode and PNPM didn't create a shrinkwrap file,
399+ // there are no dependencies. Generate empty shrinkwrap files for all projects.
400+ await Async . forEachAsync (
401+ this . rushConfiguration . projects ,
402+ async ( project ) => {
403+ await BaseProjectShrinkwrapFile . saveEmptyProjectShrinkwrapFileAsync ( project ) ;
404+ } ,
405+ { concurrency :10 }
406+ ) ;
407+ } else {
408+ // This is an unexpected case
409+ throw new Error (
410+ 'A shrinkwrap file does not exist after after successful installation. This probably indicates a ' +
411+ 'bug in the package manager.'
412+ ) ;
413+ }
414+
389415// TODO: Remove when "rush link" and "rush unlink" are deprecated
390416LastLinkFlagFactory . getCommonTempFlag ( this . rushConfiguration ) . create ( ) ;
391417}