From 0788b9c5bd6c9a444e70220b8ee1fbf13b5a1868 Mon Sep 17 00:00:00 2001 From: Alan Agius <17563226+alan-agius4@users.noreply.github.com> Date: Thu, 26 Feb 2026 16:50:42 +0000 Subject: [PATCH] refactor(@angular/cli): centralize Node.js version checks into a new utility Makes it effort less to update a supported Node.js version --- packages/angular/cli/BUILD.bazel | 1 + packages/angular/cli/bin/ng.js | 15 ++-- packages/angular/cli/lib/cli/index.ts | 12 +-- .../cli/src/commands/version/version-info.ts | 13 +--- .../angular/cli/src/utilities/node-version.ts | 75 +++++++++++++++++++ 5 files changed, 89 insertions(+), 27 deletions(-) create mode 100644 packages/angular/cli/src/utilities/node-version.ts diff --git a/packages/angular/cli/BUILD.bazel b/packages/angular/cli/BUILD.bazel index 0eac4a2cede9..a49e30695138 100644 --- a/packages/angular/cli/BUILD.bazel +++ b/packages/angular/cli/BUILD.bazel @@ -172,6 +172,7 @@ npm_package( ], stamp_files = [ "src/utilities/version.js", + "src/utilities/node-version.js", ], tags = ["release-package"], deps = RUNTIME_ASSETS + [ diff --git a/packages/angular/cli/bin/ng.js b/packages/angular/cli/bin/ng.js index 8487b50a2ae1..7303a5c05632 100755 --- a/packages/angular/cli/bin/ng.js +++ b/packages/angular/cli/bin/ng.js @@ -12,6 +12,7 @@ 'use strict'; const path = require('path'); +const nodeUtils = require('../src/utilities/node-version'); // Error if the external CLI appears to be used inside a google3 context. if (process.cwd().split(path.sep).includes('google3')) { @@ -42,10 +43,10 @@ if (rawCommandName === '--get-yargs-completions' || rawCommandName === 'completi // This node version check ensures that extremely old versions of node are not used. // These may not support ES2015 features such as const/let/async/await/etc. // These would then crash with a hard to diagnose error message. -const [major, minor, patch] = process.versions.node.split('.', 3).map((part) => Number(part)); +const [major] = process.versions.node.split('.', 1).map((part) => Number(part)); if (major % 2 === 1) { - // Allow new odd numbered releases with a warning (currently v17+) + // Allow new odd numbered releases with a warning. console.warn( 'Node.js version ' + process.version + @@ -55,17 +56,15 @@ if (major % 2 === 1) { ); require('./bootstrap'); -} else if ( - major < 22 || - (major === 22 && minor < 22) || - (major === 24 && minor < 13 && patch < 1) -) { +} else if (!nodeUtils.isNodeVersionSupported()) { // Error and exit if less than 22.22 or 24.13.1 console.error( 'Node.js version ' + process.version + ' detected.\n' + - 'The Angular CLI requires a minimum Node.js version of v22.22 or v24.13.1.\n\n' + + 'The Angular CLI requires a minimum Node.js version of ' + + nodeUtils.supportedNodeVersions.map((v) => 'v' + v).join(' or ') + + '.\n\n' + 'Please update your Node.js version or visit https://nodejs.org/ for additional instructions.\n', ); diff --git a/packages/angular/cli/lib/cli/index.ts b/packages/angular/cli/lib/cli/index.ts index a030e692e26b..a497dc0ea243 100644 --- a/packages/angular/cli/lib/cli/index.ts +++ b/packages/angular/cli/lib/cli/index.ts @@ -13,22 +13,18 @@ import { runCommand } from '../../src/command-builder/command-runner'; import { colors, supportColor } from '../../src/utilities/color'; import { ngDebug } from '../../src/utilities/environment-options'; import { writeErrorToLogFile } from '../../src/utilities/log-file'; +import { isNodeVersionMinSupported, supportedNodeVersions } from '../../src/utilities/node-version'; export { VERSION } from '../../src/utilities/version'; -const MIN_NODEJS_VERSION = [22, 22] as const; - /* eslint-disable no-console */ export default async function (options: { cliArgs: string[] }) { // This node version check ensures that the requirements of the project instance of the CLI are met - const [major, minor] = process.versions.node.split('.').map((part) => Number(part)); - if ( - major < MIN_NODEJS_VERSION[0] || - (major === MIN_NODEJS_VERSION[0] && minor < MIN_NODEJS_VERSION[1]) - ) { + + if (!isNodeVersionMinSupported()) { process.stderr.write( `Node.js version ${process.version} detected.\n` + - `The Angular CLI requires a minimum of v${MIN_NODEJS_VERSION[0]}.${MIN_NODEJS_VERSION[1]}.\n\n` + + `The Angular CLI requires a minimum of v${supportedNodeVersions[0]}.\n\n` + 'Please update your Node.js version or visit https://nodejs.org/ for additional instructions.\n', ); diff --git a/packages/angular/cli/src/commands/version/version-info.ts b/packages/angular/cli/src/commands/version/version-info.ts index efe2f8506ea5..128cc8ef5449 100644 --- a/packages/angular/cli/src/commands/version/version-info.ts +++ b/packages/angular/cli/src/commands/version/version-info.ts @@ -8,7 +8,7 @@ import { createRequire } from 'node:module'; import { CommandContext } from '../../command-builder/definitions'; -import { PackageManager } from '../../package-managers'; +import { isNodeVersionSupported } from '../../utilities/node-version'; import { VERSION } from '../../utilities/version'; /** @@ -56,12 +56,6 @@ export interface VersionInfo { packages: Record; } -/** - * Major versions of Node.js that are officially supported by Angular. - * @see https://angular.dev/reference/versions#supported-node-js-versions - */ -const SUPPORTED_NODE_MAJORS = [22, 24]; - /** * A list of regular expression patterns that match package names that should be included in the * version output. @@ -92,9 +86,6 @@ export async function gatherVersionInfo(context: CommandContext): Promise Number(part)); - const unsupportedNodeVersion = !SUPPORTED_NODE_MAJORS.includes(nodeMajor); - const allDependencies = { ...workspacePackage?.dependencies, ...workspacePackage?.devDependencies, @@ -123,7 +114,7 @@ export async function gatherVersionInfo(context: CommandContext): Promise=]/g, '') + .split('||') + .map((v) => v.trim()); + +/** + * Checks if the current Node.js version is supported. + * @returns `true` if the current Node.js version is supported, `false` otherwise. + */ +export function isNodeVersionSupported(): boolean { + if (SUPPORTED_NODE_VERSIONS.charAt(0) === '0') { + // Unlike `pkg_npm`, `ts_library` which is used to run unit tests does not support substitutions. + return true; + } + + const [processMajor, processMinor, processPatch] = process.versions.node + .split('.', 3) + .map((part) => Number(part)); + + for (const version of supportedNodeVersions) { + const [major, minor, patch] = version.split('.', 3).map((part) => Number(part)); + if ( + (major === processMajor && processMinor === minor && processPatch >= patch) || + (major === processMajor && processMinor > minor) + ) { + return true; + } + } + + return false; +} + +/** + * Checks if the current Node.js version is the minimum supported version. + * @returns `true` if the current Node.js version is the minimum supported version, `false` otherwise. + */ +export function isNodeVersionMinSupported(): boolean { + if (SUPPORTED_NODE_VERSIONS.charAt(0) === '0') { + // Unlike `pkg_npm`, `ts_library` which is used to run unit tests does not support substitutions. + return true; + } + + const [processMajor, processMinor, processPatch] = process.versions.node + .split('.', 3) + .map((part) => Number(part)); + const [major, minor, patch] = supportedNodeVersions[0].split('.', 3).map((part) => Number(part)); + + return ( + processMajor > major || + (processMajor === major && processMinor > minor) || + (processMajor === major && processMinor === minor && processPatch >= patch) + ); +}