From 86e292ddbfb725bb97501b5e5e58d68335bbcf86 Mon Sep 17 00:00:00 2001 From: Charles Lyding <19598772+clydin@users.noreply.github.com> Date: Thu, 4 Feb 2021 15:38:19 -0500 Subject: [PATCH] fix(@angular/cli): temporarily limit npm to version 6 This change will display an error message if using npm 7 (or versions earlier than 6). This is a temporarily change while npm 7 usability concerns are addressed. --- lib/packages.ts | 2 +- packages/angular/cli/commands/add-impl.ts | 4 +- packages/angular/cli/commands/new-impl.ts | 3 ++ packages/angular/cli/commands/update-impl.ts | 4 +- .../angular/cli/utilities/package-manager.ts | 23 ++++++++++ tests/legacy-cli/e2e/tests/misc/npm-7.ts | 45 +++++++++++++++++++ 6 files changed, 78 insertions(+), 3 deletions(-) create mode 100644 tests/legacy-cli/e2e/tests/misc/npm-7.ts diff --git a/lib/packages.ts b/lib/packages.ts index 37c238b24cb8..4606982e3c2c 100644 --- a/lib/packages.ts +++ b/lib/packages.ts @@ -88,7 +88,7 @@ function loadPackageJson(p: string) { case 'engines': pkg['engines'] = { 'node': '>= 10.13.0', - 'npm': '>= 6.11.0', + 'npm': '^6.11.0', 'yarn': '>= 1.13.0', }; break; diff --git a/packages/angular/cli/commands/add-impl.ts b/packages/angular/cli/commands/add-impl.ts index 79d0c6155cc5..b30ab9fbb7bb 100644 --- a/packages/angular/cli/commands/add-impl.ts +++ b/packages/angular/cli/commands/add-impl.ts @@ -15,7 +15,7 @@ import { Arguments } from '../models/interface'; import { RunSchematicOptions, SchematicCommand } from '../models/schematic-command'; import { colors } from '../utilities/color'; import { installPackage, installTempPackage } from '../utilities/install-package'; -import { getPackageManager } from '../utilities/package-manager'; +import { ensureCompatibleNpm, getPackageManager } from '../utilities/package-manager'; import { NgAddSaveDepedency, PackageManifest, @@ -39,6 +39,8 @@ export class AddCommand extends SchematicCommand { } async run(options: AddCommandSchema & Arguments) { + ensureCompatibleNpm(); + if (!options.collection) { this.logger.fatal( `The "ng add" command requires a name argument to be specified eg. ` + diff --git a/packages/angular/cli/commands/new-impl.ts b/packages/angular/cli/commands/new-impl.ts index b5f0221b3104..33170dcd22db 100644 --- a/packages/angular/cli/commands/new-impl.ts +++ b/packages/angular/cli/commands/new-impl.ts @@ -7,6 +7,7 @@ */ import { Arguments } from '../models/interface'; import { SchematicCommand } from '../models/schematic-command'; +import { ensureCompatibleNpm } from '../utilities/package-manager'; import { Schema as NewCommandSchema } from './new'; @@ -21,6 +22,8 @@ export class NewCommand extends SchematicCommand { } public async run(options: NewCommandSchema & Arguments) { + ensureCompatibleNpm(); + // Register the version of the CLI in the registry. const packageJson = require('../package.json'); const version = packageJson.version; diff --git a/packages/angular/cli/commands/update-impl.ts b/packages/angular/cli/commands/update-impl.ts index 1cfa778000e2..35315c50929e 100644 --- a/packages/angular/cli/commands/update-impl.ts +++ b/packages/angular/cli/commands/update-impl.ts @@ -18,7 +18,7 @@ import { SchematicEngineHost } from '../models/schematic-engine-host'; import { colors } from '../utilities/color'; import { runTempPackageBin } from '../utilities/install-package'; import { writeErrorToLogFile } from '../utilities/log-file'; -import { getPackageManager } from '../utilities/package-manager'; +import { ensureCompatibleNpm, getPackageManager } from '../utilities/package-manager'; import { PackageIdentifier, PackageManifest, @@ -267,6 +267,8 @@ export class UpdateCommand extends Command { // tslint:disable-next-line:no-big-function async run(options: UpdateCommandSchema & Arguments) { + ensureCompatibleNpm(); + // Check if the current installed CLI version is older than the latest version. if (!disableVersionCheck && await this.checkCLILatestVersion(options.verbose, options.next)) { this.logger.warn( diff --git a/packages/angular/cli/utilities/package-manager.ts b/packages/angular/cli/utilities/package-manager.ts index 77d1318750f9..5c21c0348310 100644 --- a/packages/angular/cli/utilities/package-manager.ts +++ b/packages/angular/cli/utilities/package-manager.ts @@ -54,3 +54,26 @@ export async function getPackageManager(root: string): Promise { // Potentially with a prompt to choose and optionally set as the default. return packageManager || PackageManager.Npm; } + +/** + * Checks if the npm version is version 6.x. If not, display a message and exit. + */ +export function ensureCompatibleNpm() { + try { + const version = execSync('npm --version', {encoding: 'utf8', stdio: 'pipe'}).trim(); + const major = Number(version.match(/^(\d+)\./)?.[1]); + if (major === 6) { + return; + } + + // tslint:disable-next-line: no-console + console.error( + `npm version ${version} detected.\n` + + 'The Angular CLI currently requires npm version 6.\n\n' + + 'Please install a compatible version to proceed (`npm install --global npm@6`).\n', + ); + process.exit(3); + } catch { + // npm is not installed + } +} diff --git a/tests/legacy-cli/e2e/tests/misc/npm-7.ts b/tests/legacy-cli/e2e/tests/misc/npm-7.ts new file mode 100644 index 000000000000..4ab3c4db01f5 --- /dev/null +++ b/tests/legacy-cli/e2e/tests/misc/npm-7.ts @@ -0,0 +1,45 @@ +import { getActivePackageManager } from '../../utils/packages'; +import { ng, npm } from '../../utils/process'; +import { expectToFail } from '../../utils/utils'; + +const errorText = 'The Angular CLI currently requires npm version 6.'; + +export default async function() { + // Only relevant with npm as a package manager + if (getActivePackageManager() !== 'npm') { + return; + } + + const currentDirectory = process.cwd(); + try { + // Install version 7.x + await npm('install', '--global', 'npm@7'); + + // Ensure `ng add` exits and shows npm error + const { message: stderrAdd } = await expectToFail(() => ng('add')); + if (!stderrAdd.includes(errorText)) { + throw new Error('ng add expected to show npm version error.'); + } + + // Ensure `ng update` exits and shows npm error + const { message: stderrUpdate } = await expectToFail(() => ng('update')); + if (!stderrUpdate.includes(errorText)) { + throw new Error('ng update expected to show npm version error.'); + } + + // Ensure `ng new` exits and shows npm error + // Must be outside the project for `ng new` + process.chdir('..'); + const { message: stderrNew } = await expectToFail(() => ng('new')); + if (!stderrNew.includes(errorText)) { + throw new Error('ng new expected to show npm version error.'); + } + } finally { + // Change directory back + process.chdir(currentDirectory); + + // Reset version back to 6.x + await npm('install', '--global', 'npm@6'); + } + +}