protected async installAsync()

in apps/rush-lib/src/logic/installManager/RushInstallManager.ts [445:631]


  protected async installAsync(cleanInstall: boolean): Promise<void> {
    // Since we are actually running npm/pnpm/yarn install, recreate all the temp project tarballs.
    // This ensures that any existing tarballs with older header bits will be regenerated.
    // It is safe to assume that temp project pacakge.jsons already exist.
    for (const rushProject of this.rushConfiguration.projects) {
      this._tempProjectHelper.createTempProjectTarball(rushProject);
    }

    // NOTE: The PNPM store is supposed to be transactionally safe, so we don't delete it automatically.
    // The user must request that via the command line.
    if (cleanInstall) {
      if (this.rushConfiguration.packageManager === 'npm') {
        console.log(`Deleting the "npm-cache" folder`);
        // This is faster and more thorough than "npm cache clean"
        this.installRecycler.moveFolder(this.rushConfiguration.npmCacheFolder);

        console.log(`Deleting the "npm-tmp" folder`);
        this.installRecycler.moveFolder(this.rushConfiguration.npmTmpFolder);
      }
    }

    // Example: "C:\MyRepo\common\temp\npm-local\node_modules\.bin\npm"
    const packageManagerFilename: string = this.rushConfiguration.packageManagerToolFilename;

    const packageManagerEnv: NodeJS.ProcessEnv = InstallHelpers.getPackageManagerEnvironment(
      this.rushConfiguration,
      this.options
    );

    const commonNodeModulesFolder: string = path.join(
      this.rushConfiguration.commonTempFolder,
      RushConstants.nodeModulesFolderName
    );

    // Is there an existing "node_modules" folder to consider?
    if (FileSystem.exists(commonNodeModulesFolder)) {
      // Should we delete the entire "node_modules" folder?
      if (cleanInstall) {
        // YES: Delete "node_modules"

        // Explain to the user why we are hosing their node_modules folder
        console.log('Deleting files from ' + commonNodeModulesFolder);

        this.installRecycler.moveFolder(commonNodeModulesFolder);

        Utilities.createFolderWithRetry(commonNodeModulesFolder);
      } else {
        // NO: Prepare to do an incremental install in the "node_modules" folder

        // note: it is not necessary to run "prune" with pnpm
        if (this.rushConfiguration.packageManager === 'npm') {
          console.log(
            `Running "${this.rushConfiguration.packageManager} prune"` +
              ` in ${this.rushConfiguration.commonTempFolder}`
          );
          const args: string[] = ['prune'];
          this.pushConfigurationArgs(args, this.options);

          Utilities.executeCommandWithRetry(
            {
              command: packageManagerFilename,
              args: args,
              workingDirectory: this.rushConfiguration.commonTempFolder,
              environment: packageManagerEnv
            },
            this.options.maxInstallAttempts
          );

          // Delete the (installed image of) the temp projects, since "npm install" does not
          // detect changes for "file:./" references.
          // We recognize the temp projects by their names, which always start with "rush-".

          // Example: "C:\MyRepo\common\temp\node_modules\@rush-temp"
          const pathToDeleteWithoutStar: string = path.join(
            commonNodeModulesFolder,
            RushConstants.rushTempNpmScope
          );
          console.log(`Deleting ${pathToDeleteWithoutStar}\\*`);
          // Glob can't handle Windows paths
          const normalizedpathToDeleteWithoutStar: string = Text.replaceAll(
            pathToDeleteWithoutStar,
            '\\',
            '/'
          );

          // Example: "C:/MyRepo/common/temp/node_modules/@rush-temp/*"
          for (const tempModulePath of glob.sync(globEscape(normalizedpathToDeleteWithoutStar) + '/*')) {
            // We could potentially use AsyncRecycler here, but in practice these folders tend
            // to be very small
            Utilities.dangerouslyDeletePath(tempModulePath);
          }
        }
      }
    }

    if (this.rushConfiguration.packageManager === 'yarn') {
      // Yarn does not correctly detect changes to a tarball, so we need to forcibly clear its cache
      const yarnRushTempCacheFolder: string = path.join(
        this.rushConfiguration.yarnCacheFolder,
        'v2',
        'npm-@rush-temp'
      );
      if (FileSystem.exists(yarnRushTempCacheFolder)) {
        console.log('Deleting ' + yarnRushTempCacheFolder);
        Utilities.dangerouslyDeletePath(yarnRushTempCacheFolder);
      }
    }

    // Run "npm install" in the common folder
    const installArgs: string[] = ['install'];
    this.pushConfigurationArgs(installArgs, this.options);

    console.log(
      os.EOL +
        colors.bold(
          `Running "${this.rushConfiguration.packageManager} install" in` +
            ` ${this.rushConfiguration.commonTempFolder}`
        ) +
        os.EOL
    );

    // If any diagnostic options were specified, then show the full command-line
    if (this.options.debug || this.options.collectLogFile || this.options.networkConcurrency) {
      console.log(
        os.EOL +
          colors.green('Invoking package manager: ') +
          FileSystem.getRealPath(packageManagerFilename) +
          ' ' +
          installArgs.join(' ') +
          os.EOL
      );
    }

    try {
      Utilities.executeCommandWithRetry(
        {
          command: packageManagerFilename,
          args: installArgs,
          workingDirectory: this.rushConfiguration.commonTempFolder,
          environment: packageManagerEnv,
          suppressOutput: false
        },
        this.options.maxInstallAttempts,
        () => {
          if (this.rushConfiguration.packageManager === 'pnpm') {
            console.log(colors.yellow(`Deleting the "node_modules" folder`));
            this.installRecycler.moveFolder(commonNodeModulesFolder);

            // Leave the pnpm-store as is for the retry. This ensures that packages that have already
            // been downloaded need not be downloaded again, thereby potentially increasing the chances
            // of a subsequent successful install.

            Utilities.createFolderWithRetry(commonNodeModulesFolder);
          }
        }
      );
    } catch (error) {
      // All the install attempts failed.

      if (
        this.rushConfiguration.packageManager === 'pnpm' &&
        this.rushConfiguration.pnpmOptions.pnpmStore === 'local'
      ) {
        // If the installation has failed even after the retries, then pnpm store may
        // have got into a corrupted, irrecoverable state. Delete the store so that a
        // future install can create the store afresh.
        console.log(colors.yellow(`Deleting the "pnpm-store" folder`));
        this.installRecycler.moveFolder(this.rushConfiguration.pnpmOptions.pnpmStorePath);
      }

      throw error;
    }

    if (this.rushConfiguration.packageManager === 'npm') {
      console.log(os.EOL + colors.bold('Running "npm shrinkwrap"...'));
      const npmArgs: string[] = ['shrinkwrap'];
      this.pushConfigurationArgs(npmArgs, this.options);
      Utilities.executeCommand({
        command: this.rushConfiguration.packageManagerToolFilename,
        args: npmArgs,
        workingDirectory: this.rushConfiguration.commonTempFolder
      });
      console.log('"npm shrinkwrap" completed' + os.EOL);

      this._fixupNpm5Regression();
    }
  }