diff --git a/lib/packages.ts b/lib/packages.ts index fb1f5df02417..61b95b5efde7 100644 --- a/lib/packages.ts +++ b/lib/packages.ts @@ -88,7 +88,7 @@ function loadPackageJson(p: string) { case 'engines': pkg['engines'] = { 'node': '>= 12.13.0', - 'npm': '^6.11.0', + 'npm': '^6.11.0 || ^7.5.6', 'yarn': '>= 1.13.0', }; break; diff --git a/packages/angular/cli/utilities/package-manager.ts b/packages/angular/cli/utilities/package-manager.ts index 56e7cd7b4fee..8c53bdf97d20 100644 --- a/packages/angular/cli/utilities/package-manager.ts +++ b/packages/angular/cli/utilities/package-manager.ts @@ -8,6 +8,7 @@ import { execSync } from 'child_process'; import { existsSync } from 'fs'; import { join } from 'path'; +import { satisfies, valid } from 'semver'; import { PackageManager } from '../lib/config/schema'; import { getConfiguredPackageManager } from './config'; @@ -56,7 +57,7 @@ export async function getPackageManager(root: string): Promise { } /** - * Checks if the npm version is version 6.x. If not, display a message and exit. + * Checks if the npm version is a supported 7.x version. If not, display a warning. */ export async function ensureCompatibleNpm(root: string): Promise { if ((await getPackageManager(root)) !== PackageManager.Npm) { @@ -64,21 +65,19 @@ export async function ensureCompatibleNpm(root: string): Promise { } try { - const version = execSync('npm --version', {encoding: 'utf8', stdio: 'pipe'}).trim(); - const major = Number(version.match(/^(\d+)\./)?.[1]); - if (major <= 6) { + const versionText = execSync('npm --version', {encoding: 'utf8', stdio: 'pipe'}).trim(); + const version = valid(versionText); + if (!version) { return; } - // tslint:disable-next-line: no-console - console.error( - `npm version ${version} detected. The Angular CLI temporarily requires npm version 6 while upstream issues are addressed.\n\n` + - 'Please install a compatible version to proceed (`npm install --global npm@6`).\n' + - 'For additional information and alternative workarounds, please see ' + - 'https://github.com/angular/angular-cli/issues/19957#issuecomment-775407654', - ); - - process.exit(3); + if (satisfies(version, '>=7 <7.5.6')) { + // tslint:disable-next-line: no-console + console.warn( + `npm version ${version} detected.` + + ' When using npm 7 with the Angular CLI, npm version 7.5.6 or higher is recommended.', + ); + } } 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 index 2ecb4a677a8a..41e9051e2458 100644 --- a/tests/legacy-cli/e2e/tests/misc/npm-7.ts +++ b/tests/legacy-cli/e2e/tests/misc/npm-7.ts @@ -3,7 +3,7 @@ import { getActivePackageManager } from '../../utils/packages'; import { ng, npm } from '../../utils/process'; import { expectToFail } from '../../utils/utils'; -const errorText = 'The Angular CLI temporarily requires npm version 6'; +const warningText = 'npm version 7.5.6 or higher is recommended'; export default async function() { // Only relevant with npm as a package manager @@ -18,53 +18,62 @@ export default async function() { const currentDirectory = process.cwd(); try { - // Install version 7.x - await npm('install', '--global', 'npm@7'); + // Install version >=7.5.6 + await npm('install', '--global', 'npm@>=7.5.6'); - // Ensure `ng add` exits and shows npm error + // Ensure `ng update` does not show npm warning + const { stderr: stderrUpdate1 } = await ng('update'); + if (stderrUpdate1.includes(warningText)) { + throw new Error('ng update expected to not show npm version warning.'); + } + + // Install version <7.5.6 + await npm('install', '--global', 'npm@7.4.0'); + + // Ensure `ng add` shows npm warning const { message: stderrAdd } = await expectToFail(() => ng('add')); - if (!stderrAdd.includes(errorText)) { - throw new Error('ng add expected to show npm version error.'); + if (!stderrAdd.includes(warningText)) { + throw new Error('ng add expected to show npm version warning.'); } - // 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 update` shows npm warning + const { stderr: stderrUpdate2 } = await ng('update'); + if (!stderrUpdate2.includes(warningText)) { + throw new Error('ng update expected to show npm version warning.'); } // Ensure `ng build` executes successfully const { stderr: stderrBuild } = await ng('build'); - if (stderrBuild.includes(errorText)) { - throw new Error('ng build expected to not show npm version error.'); + if (stderrBuild.includes(warningText)) { + throw new Error('ng build expected to not show npm version warning.'); } - // Ensure `ng new` exits and shows npm error + // Ensure `ng new` shows npm warning // 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.'); + if (!stderrNew.includes(warningText)) { + throw new Error('ng new expected to show npm version warning.'); } - // Ensure `ng new --package-manager=npm` exits and shows npm error + // Ensure `ng new --package-manager=npm` shows npm warning const { message: stderrNewNpm } = await expectToFail(() => ng('new', '--package-manager=npm')); - if (!stderrNewNpm.includes(errorText)) { - throw new Error('ng new expected to show npm version error.'); + if (!stderrNewNpm.includes(warningText)) { + throw new Error('ng new expected to show npm version warning.'); } // Ensure `ng new --skip-install` executes successfully const { stderr: stderrNewSkipInstall } = await ng('new', 'npm-seven-skip', '--skip-install'); - if (stderrNewSkipInstall.includes(errorText)) { - throw new Error('ng new --skip-install expected to not show npm version error.'); + if (stderrNewSkipInstall.includes(warningText)) { + throw new Error('ng new --skip-install expected to not show npm version warning.'); } // Ensure `ng new --package-manager=yarn` executes successfully // Need an additional npmrc file since yarn does not use the NPM registry environment variable await writeFile('.npmrc', 'registry=http://localhost:4873') const { stderr: stderrNewYarn } = await ng('new', 'npm-seven-yarn', '--package-manager=yarn'); - if (stderrNewYarn.includes(errorText)) { - throw new Error('ng new --package-manager=yarn expected to not show npm version error.'); + if (stderrNewYarn.includes(warningText)) { + throw new Error('ng new --package-manager=yarn expected to not show npm version warning.'); } } finally { // Cleanup extra test projects