Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/angular/cli/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ npm_package(
],
stamp_files = [
"src/utilities/version.js",
"src/utilities/node-version.js",
],
tags = ["release-package"],
deps = RUNTIME_ASSETS + [
Expand Down
15 changes: 7 additions & 8 deletions packages/angular/cli/bin/ng.js
Original file line number Diff line number Diff line change
Expand Up @@ -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')) {
Expand Down Expand Up @@ -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 +
Expand All @@ -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',
);

Expand Down
12 changes: 4 additions & 8 deletions packages/angular/cli/lib/cli/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
);

Expand Down
13 changes: 2 additions & 11 deletions packages/angular/cli/src/commands/version/version-info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

/**
Expand Down Expand Up @@ -56,12 +56,6 @@ export interface VersionInfo {
packages: Record<string, PackageVersionInfo>;
}

/**
* 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.
Expand Down Expand Up @@ -92,9 +86,6 @@ export async function gatherVersionInfo(context: CommandContext): Promise<Versio
workspacePackage = workspaceRequire('./package.json');
} catch {}

const [nodeMajor] = process.versions.node.split('.').map((part) => Number(part));
const unsupportedNodeVersion = !SUPPORTED_NODE_MAJORS.includes(nodeMajor);

const allDependencies = {
...workspacePackage?.dependencies,
...workspacePackage?.devDependencies,
Expand Down Expand Up @@ -123,7 +114,7 @@ export async function gatherVersionInfo(context: CommandContext): Promise<Versio
system: {
node: {
version: process.versions.node,
unsupported: unsupportedNodeVersion,
unsupported: !isNodeVersionSupported(),
},
os: {
platform: process.platform,
Expand Down
75 changes: 75 additions & 0 deletions packages/angular/cli/src/utilities/node-version.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.dev/license
*/

/**
* @fileoverview This file contains the supported Node.js version for the Angular CLI.
* @important This file must not import any other modules.
*/

/**
* The supported Node.js version for the Angular CLI.
* @type {[string, number]} The supported Node.js version.
*/

const SUPPORTED_NODE_VERSIONS = '0.0.0-ENGINES-NODE';

/**
* The supported Node.js versions.
*/
export const supportedNodeVersions = SUPPORTED_NODE_VERSIONS.replace(/[\^~<>=]/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)
);
}