Skip to content

Commit d06ff3f

Browse files
committed
fix(@angular/cli): update min Node.js support to 20.19, 22.12, and 24.0
Bumps the minimum Node.js support for `@angular/cli`. This update ensures compatibility with a growing number of upstream dependencies, such as Undici and Pacote, that are adopting newer Node.js versions. These versions where selected as `require(esm)` is enabled by default. This change also prepares us for future package releases that will likely rely on this behavior.
1 parent 87f4121 commit d06ff3f

File tree

9 files changed

+77
-66
lines changed

9 files changed

+77
-66
lines changed

WORKSPACE

Lines changed: 36 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
workspace(name = "angular_cli")
22

3-
DEFAULT_NODE_VERSION = "20.11.1"
4-
53
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive", "http_file")
64

75
http_archive(
@@ -61,65 +59,65 @@ rules_pkg_dependencies()
6159
# Setup the Node.js toolchain
6260
load("@rules_nodejs//nodejs:repositories.bzl", "nodejs_register_toolchains")
6361

64-
NODE_20_REPO = {
65-
"20.11.1-darwin_arm64": ("node-v20.11.1-darwin-arm64.tar.gz", "node-v20.11.1-darwin-arm64", "e0065c61f340e85106a99c4b54746c5cee09d59b08c5712f67f99e92aa44995d"),
66-
"20.11.1-darwin_amd64": ("node-v20.11.1-darwin-x64.tar.gz", "node-v20.11.1-darwin-x64", "c52e7fb0709dbe63a4cbe08ac8af3479188692937a7bd8e776e0eedfa33bb848"),
67-
"20.11.1-linux_arm64": ("node-v20.11.1-linux-arm64.tar.xz", "node-v20.11.1-linux-arm64", "c957f29eb4e341903520caf362534f0acd1db7be79c502ae8e283994eed07fe1"),
68-
"20.11.1-linux_ppc64le": ("node-v20.11.1-linux-ppc64le.tar.xz", "node-v20.11.1-linux-ppc64le", "51343cacf5cdf5c4b5e93e919d19dd373d6ef43d5f2c666eae299f26e31d08b5"),
69-
"20.11.1-linux_s390x": ("node-v20.11.1-linux-s390x.tar.xz", "node-v20.11.1-linux-s390x", "b32616b705cd0ddbb230b95c693e3d7a37becc2ced9bcadea8dc824cceed6be0"),
70-
"20.11.1-linux_amd64": ("node-v20.11.1-linux-x64.tar.xz", "node-v20.11.1-linux-x64", "d8dab549b09672b03356aa2257699f3de3b58c96e74eb26a8b495fbdc9cf6fbe"),
71-
"20.11.1-windows_amd64": ("node-v20.11.1-win-x64.zip", "node-v20.11.1-win-x64", "bc032628d77d206ffa7f133518a6225a9c5d6d9210ead30d67e294ff37044bda"),
62+
# Set the default nodejs toolchain to the latest supported major version
63+
64+
NODE_24_VERSION = "24.0.0"
65+
66+
NODE_24_REPO = {
67+
"24.0.0-darwin_arm64": ("node-v24.0.0-darwin-arm64.tar.gz", "node-v24.0.0-darwin-arm64", "194e2f3dd3ec8c2adcaa713ed40f44c5ca38467880e160974ceac1659be60121"),
68+
"24.0.0-darwin_amd64": ("node-v24.0.0-darwin-x64.tar.gz", "node-v24.0.0-darwin-x64", "f716b3ce14a7e37a6cbf97c9de10d444d7da07ef833cd8da81dd944d111e6a4a"),
69+
"24.0.0-linux_arm64": ("node-v24.0.0-linux-arm64.tar.xz", "node-v24.0.0-linux-arm64", "d40ec7ffe0b82b02dce94208c84351424099bd70fa3a42b65c46d95322305040"),
70+
"24.0.0-linux_ppc64le": ("node-v24.0.0-linux-ppc64le.tar.xz", "node-v24.0.0-linux-ppc64le", "cfa0e8d51a2f9a446f1bfb81cdf4c7e95336ad622e2aa230e3fa1d093c63d77d"),
71+
"24.0.0-linux_s390x": ("node-v24.0.0-linux-s390x.tar.xz", "node-v24.0.0-linux-s390x", "e37a04c7ee05416ec1234fd3255e05b6b81287eb0424a57441c8b69f0a155021"),
72+
"24.0.0-linux_amd64": ("node-v24.0.0-linux-x64.tar.xz", "node-v24.0.0-linux-x64", "59b8af617dccd7f9f68cc8451b2aee1e86d6bd5cb92cd51dd6216a31b707efd7"),
73+
"24.0.0-windows_amd64": ("node-v24.0.0-win-x64.zip", "node-v24.0.0-win-x64", "3d0fff80c87bb9a8d7f49f2f27832aa34a1477d137af46f5b14df5498be81304"),
7274
}
7375

74-
# Set the default nodejs toolchain to the latest supported major version
7576
nodejs_register_toolchains(
7677
name = "nodejs",
77-
# The below can be removed once @rules_nodejs/nodejs is updated to latest which contains https://github.com/bazelbuild/rules_nodejs/pull/3701
78-
node_repositories = NODE_20_REPO,
79-
node_version = DEFAULT_NODE_VERSION,
78+
node_repositories = NODE_24_REPO,
79+
node_version = NODE_24_VERSION,
8080
)
8181

8282
nodejs_register_toolchains(
8383
name = "node20",
84-
# The below can be removed once @rules_nodejs/nodejs is updated to latest which contains https://github.com/bazelbuild/rules_nodejs/pull/3701
85-
node_repositories = NODE_20_REPO,
86-
node_version = "20.11.1",
84+
node_repositories = {
85+
"20.19.0-darwin_arm64": ("node-v20.19.0-darwin-arm64.tar.gz", "node-v20.19.0-darwin-arm64", "c016cd1975a264a29dc1b07c6fbe60d5df0a0c2beb4113c0450e3d998d1a0d9c"),
86+
"20.19.0-darwin_amd64": ("node-v20.19.0-darwin-x64.tar.gz", "node-v20.19.0-darwin-x64", "a8554af97d6491fdbdabe63d3a1cfb9571228d25a3ad9aed2df856facb131b20"),
87+
"20.19.0-linux_arm64": ("node-v20.19.0-linux-arm64.tar.xz", "node-v20.19.0-linux-arm64", "dbe339e55eb393955a213e6b872066880bb9feceaa494f4d44c7aac205ec2ab9"),
88+
"20.19.0-linux_ppc64le": ("node-v20.19.0-linux-ppc64le.tar.xz", "node-v20.19.0-linux-ppc64le", "84937108f005679e60b486ed8e801cebfe923f02b76d8e710463d32f82181f65"),
89+
"20.19.0-linux_s390x": ("node-v20.19.0-linux-s390x.tar.xz", "node-v20.19.0-linux-s390x", "11f8ee99d792a83bba7b29911e0229dd6cd5e88987d7416346067db1cc76d89a"),
90+
"20.19.0-linux_amd64": ("node-v20.19.0-linux-x64.tar.xz", "node-v20.19.0-linux-x64", "b4e336584d62abefad31baecff7af167268be9bb7dd11f1297112e6eed3ca0d5"),
91+
"20.19.0-windows_amd64": ("node-v20.19.0-win-x64.zip", "node-v20.19.0-win-x64", "be72284c7bc62de07d5a9fd0ae196879842c085f11f7f2b60bf8864c0c9d6a4f"),
92+
},
93+
node_version = "20.19.0",
8794
)
8895

8996
nodejs_register_toolchains(
9097
name = "node22",
91-
# The below can be removed once @rules_nodejs/nodejs is updated to latest which contains https://github.com/bazelbuild/rules_nodejs/pull/3701
9298
node_repositories = {
93-
"22.11.0-darwin_arm64": ("node-v22.11.0-darwin-arm64.tar.gz", "node-v22.11.0-darwin-arm64", "2e89afe6f4e3aa6c7e21c560d8a0453d84807e97850bbb819b998531a22bdfde"),
94-
"22.11.0-darwin_amd64": ("node-v22.11.0-darwin-x64.tar.gz", "node-v22.11.0-darwin-x64", "668d30b9512137b5f5baeef6c1bb4c46efff9a761ba990a034fb6b28b9da2465"),
95-
"22.11.0-linux_arm64": ("node-v22.11.0-linux-arm64.tar.xz", "node-v22.11.0-linux-arm64", "6031d04b98f59ff0f7cb98566f65b115ecd893d3b7870821171708cdbaf7ae6e"),
96-
"22.11.0-linux_ppc64le": ("node-v22.11.0-linux-ppc64le.tar.xz", "node-v22.11.0-linux-ppc64le", "d1d49d7d611b104b6d616e18ac439479d8296aa20e3741432de0e85f4735a81e"),
97-
"22.11.0-linux_s390x": ("node-v22.11.0-linux-s390x.tar.xz", "node-v22.11.0-linux-s390x", "f474ed77d6b13d66d07589aee1c2b9175be4c1b165483e608ac1674643064a99"),
98-
"22.11.0-linux_amd64": ("node-v22.11.0-linux-x64.tar.xz", "node-v22.11.0-linux-x64", "83bf07dd343002a26211cf1fcd46a9d9534219aad42ee02847816940bf610a72"),
99-
"22.11.0-windows_amd64": ("node-v22.11.0-win-x64.zip", "node-v22.11.0-win-x64", "905373a059aecaf7f48c1ce10ffbd5334457ca00f678747f19db5ea7d256c236"),
99+
"22.12.0-darwin_arm64": ("node-v22.12.0-darwin-arm64.tar.gz", "node-v22.12.0-darwin-arm64", "293dcc6c2408da21562d135b0412525e381bb6fe150d688edb58fe850d0f3e13"),
100+
"22.12.0-darwin_amd64": ("node-v22.12.0-darwin-x64.tar.gz", "node-v22.12.0-darwin-x64", "52bc25dd026db7247c3c00439afdb83e95087248267f02d6c1a7250d1f896173"),
101+
"22.12.0-linux_arm64": ("node-v22.12.0-linux-arm64.tar.xz", "node-v22.12.0-linux-arm64", "8cfd5a8b9afae5a2e0bd86b0148ca31d2589c0ea669c2d0b11c132e35d90ed68"),
102+
"22.12.0-linux_ppc64le": ("node-v22.12.0-linux-ppc64le.tar.xz", "node-v22.12.0-linux-ppc64le", "199a606ba1ee86cce6d6b369c71f9d00873d2836a6662592afc3b6a5923e2004"),
103+
"22.12.0-linux_s390x": ("node-v22.12.0-linux-s390x.tar.xz", "node-v22.12.0-linux-s390x", "9b517f8006eb4b451d40c461cbe64f93c6455566dbe2613387ab02412bc06d35"),
104+
"22.12.0-linux_amd64": ("node-v22.12.0-linux-x64.tar.xz", "node-v22.12.0-linux-x64", "22982235e1b71fa8850f82edd09cdae7e3f32df1764a9ec298c72d25ef2c164f"),
105+
"22.12.0-windows_amd64": ("node-v22.12.0-win-x64.zip", "node-v22.12.0-win-x64", "2b8f2256382f97ad51e29ff71f702961af466c4616393f767455501e6aece9b8"),
100106
},
101-
node_version = "22.11.0",
107+
node_version = "22.12.0",
102108
)
103109

104110
nodejs_register_toolchains(
105111
name = "node24",
106-
node_repositories = {
107-
"24.0.0-darwin_arm64": ("node-v24.0.0-darwin-arm64.tar.gz", "node-v24.0.0-darwin-arm64", "194e2f3dd3ec8c2adcaa713ed40f44c5ca38467880e160974ceac1659be60121"),
108-
"24.0.0-darwin_amd64": ("node-v24.0.0-darwin-x64.tar.gz", "node-v24.0.0-darwin-x64", "f716b3ce14a7e37a6cbf97c9de10d444d7da07ef833cd8da81dd944d111e6a4a"),
109-
"24.0.0-linux_arm64": ("node-v24.0.0-linux-arm64.tar.xz", "node-v24.0.0-linux-arm64", "d40ec7ffe0b82b02dce94208c84351424099bd70fa3a42b65c46d95322305040"),
110-
"24.0.0-linux_ppc64le": ("node-v24.0.0-linux-ppc64le.tar.xz", "node-v24.0.0-linux-ppc64le", "cfa0e8d51a2f9a446f1bfb81cdf4c7e95336ad622e2aa230e3fa1d093c63d77d"),
111-
"24.0.0-linux_s390x": ("node-v24.0.0-linux-s390x.tar.xz", "node-v24.0.0-linux-s390x", "e37a04c7ee05416ec1234fd3255e05b6b81287eb0424a57441c8b69f0a155021"),
112-
"24.0.0-linux_amd64": ("node-v24.0.0-linux-x64.tar.xz", "node-v24.0.0-linux-x64", "59b8af617dccd7f9f68cc8451b2aee1e86d6bd5cb92cd51dd6216a31b707efd7"),
113-
"24.0.0-windows_amd64": ("node-v24.0.0-win-x64.zip", "node-v24.0.0-win-x64", "3d0fff80c87bb9a8d7f49f2f27832aa34a1477d137af46f5b14df5498be81304"),
114-
},
115-
node_version = "24.0.0",
112+
node_repositories = NODE_24_REPO,
113+
node_version = NODE_24_VERSION,
116114
)
117115

118116
load("@aspect_rules_js//js:toolchains.bzl", "rules_js_register_toolchains")
119117

120118
rules_js_register_toolchains(
121-
node_repositories = NODE_20_REPO,
122-
node_version = DEFAULT_NODE_VERSION,
119+
node_repositories = NODE_24_REPO,
120+
node_version = NODE_24_VERSION,
123121
)
124122

125123
http_archive(

constants.bzl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Engine versions to stamp in a release package.json
2-
RELEASE_ENGINES_NODE = "^20.11.1 || ^22.11.0 || >=24.0.0"
2+
RELEASE_ENGINES_NODE = "^20.19.0 || ^22.12.0 || >=24.0.0"
33
RELEASE_ENGINES_NPM = "^6.11.0 || ^7.5.6 || >=8.0.0"
44
RELEASE_ENGINES_YARN = ">= 1.13.0"
55

docs/DEVELOPER.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ To get started locally, follow these instructions:
77
1. If you haven't done it already, [make a fork of this repo](https://github.com/angular/angular-cli/fork).
88
2. If you are on Windows, see [the extra steps needed for contributing on Windows](#windows)
99
3. Clone to your local computer using `git`.
10-
4. Make sure that you have Node `v20.18.1` or higher installed. See instructions [here](https://nodejs.org/en/download/).
10+
4. Make sure that you have Node `v20.19.0` or higher installed. See instructions [here](https://nodejs.org/en/download/).
1111
5. Install `pnpm`.
1212
- You can install pnpm by running `npm i -g pnpm@9`.
1313
- See detailed instructions [here](https://pnpm.io/installation).

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
},
3535
"packageManager": "[email protected]",
3636
"engines": {
37-
"node": "^20.11.1 || >=22.11.0",
37+
"node": "^20.19.0 || ^22.12.0 || >=24.0.0",
3838
"npm": "Please use pnpm instead of NPM to install dependencies",
3939
"yarn": "Please use pnpm instead of Yarn to install dependencies",
4040
"pnpm": "^9.15.6"

packages/angular/build/src/utils/load-proxy-config.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,7 @@ export async function loadProxyConfiguration(
5353
// Load the ESM configuration file using the TypeScript dynamic import workaround.
5454
// Once TypeScript provides support for keeping the dynamic import this workaround can be
5555
// changed to a direct dynamic import.
56-
proxyConfiguration = (await loadEsmModule<{ default: unknown }>(pathToFileURL(proxyPath)))
57-
.default;
56+
proxyConfiguration = await loadEsmModule<{ default: unknown }>(pathToFileURL(proxyPath));
5857
break;
5958
case '.cjs':
6059
proxyConfiguration = require(proxyPath);
@@ -71,15 +70,18 @@ export async function loadProxyConfiguration(
7170
// Load the ESM configuration file using the TypeScript dynamic import workaround.
7271
// Once TypeScript provides support for keeping the dynamic import this workaround can be
7372
// changed to a direct dynamic import.
74-
proxyConfiguration = (await loadEsmModule<{ default: unknown }>(pathToFileURL(proxyPath)))
75-
.default;
73+
proxyConfiguration = await loadEsmModule<{ default: unknown }>(pathToFileURL(proxyPath));
7674
break;
7775
}
7876

7977
throw e;
8078
}
8179
}
8280

81+
if ('default' in proxyConfiguration) {
82+
proxyConfiguration = proxyConfiguration.default;
83+
}
84+
8385
return normalizeProxyConfiguration(proxyConfiguration);
8486
}
8587

packages/angular/cli/lib/cli/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import { writeErrorToLogFile } from '../../src/utilities/log-file';
1616

1717
export { VERSION } from '../../src/utilities/version';
1818

19-
const MIN_NODEJS_VERSION = [20, 11] as const;
19+
const MIN_NODEJS_VERSION = [20, 19] as const;
2020

2121
/* eslint-disable no-console */
2222
export default async function (options: { cliArgs: string[] }) {

packages/angular_devkit/architect/node/node-modules-architect-host.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -310,19 +310,22 @@ export function loadEsmModule<T>(modulePath: string | URL): Promise<T> {
310310

311311
// eslint-disable-next-line @typescript-eslint/no-explicit-any
312312
async function getBuilder(builderPath: string): Promise<any> {
313+
let builder;
313314
switch (path.extname(builderPath)) {
314315
case '.mjs':
315316
// Load the ESM configuration file using the TypeScript dynamic import workaround.
316317
// Once TypeScript provides support for keeping the dynamic import this workaround can be
317318
// changed to a direct dynamic import.
318-
return (await loadEsmModule<{ default: unknown }>(pathToFileURL(builderPath))).default;
319+
builder = (await loadEsmModule<{ default: unknown }>(pathToFileURL(builderPath))).default;
320+
break;
319321
case '.cjs':
320-
return localRequire(builderPath);
322+
builder = localRequire(builderPath);
323+
break;
321324
default:
322325
// The file could be either CommonJS or ESM.
323326
// CommonJS is tried first then ESM if loading fails.
324327
try {
325-
return localRequire(builderPath);
328+
builder = localRequire(builderPath);
326329
} catch (e) {
327330
if (
328331
(e as NodeJS.ErrnoException).code === 'ERR_REQUIRE_ESM' ||
@@ -331,10 +334,13 @@ async function getBuilder(builderPath: string): Promise<any> {
331334
// Load the ESM configuration file using the TypeScript dynamic import workaround.
332335
// Once TypeScript provides support for keeping the dynamic import this workaround can be
333336
// changed to a direct dynamic import.
334-
return (await loadEsmModule<{ default: unknown }>(pathToFileURL(builderPath))).default;
337+
builder = await loadEsmModule<{ default: unknown }>(pathToFileURL(builderPath));
335338
}
336339

337340
throw e;
338341
}
342+
break;
339343
}
344+
345+
return 'default' in builder ? builder.default : builder;
340346
}

packages/angular_devkit/build_angular/src/tools/webpack/configs/dev-server.ts

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ async function addProxyConfig(
169169
throw new Error(`Proxy configuration file ${proxyPath} does not exist.`);
170170
}
171171

172-
let proxyConfiguration: Record<string, object> | object[];
172+
let proxyConfiguration;
173173

174174
switch (extname(proxyPath)) {
175175
case '.json': {
@@ -194,11 +194,9 @@ async function addProxyConfig(
194194
// Load the ESM configuration file using the TypeScript dynamic import workaround.
195195
// Once TypeScript provides support for keeping the dynamic import this workaround can be
196196
// changed to a direct dynamic import.
197-
proxyConfiguration = (
198-
await loadEsmModule<{ default: Record<string, object> | object[] }>(
199-
pathToFileURL(proxyPath),
200-
)
201-
).default;
197+
proxyConfiguration = await loadEsmModule<{ default: Record<string, object> | object[] }>(
198+
pathToFileURL(proxyPath),
199+
);
202200
break;
203201
case '.cjs':
204202
proxyConfiguration = require(proxyPath);
@@ -217,14 +215,16 @@ async function addProxyConfig(
217215
// Load the ESM configuration file using the TypeScript dynamic import workaround.
218216
// Once TypeScript provides support for keeping the dynamic import this workaround can be
219217
// changed to a direct dynamic import.
220-
proxyConfiguration = (
221-
await loadEsmModule<{ default: Record<string, object> | object[] }>(
222-
pathToFileURL(proxyPath),
223-
)
224-
).default;
218+
proxyConfiguration = await loadEsmModule<{ default: Record<string, object> | object[] }>(
219+
pathToFileURL(proxyPath),
220+
);
225221
}
226222
}
227223

224+
if ('default' in proxyConfiguration) {
225+
proxyConfiguration = proxyConfiguration.default;
226+
}
227+
228228
return normalizeProxyConfiguration(proxyConfiguration);
229229
}
230230

packages/angular_devkit/build_webpack/src/utils.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -76,19 +76,23 @@ export async function getWebpackConfig(configPath: string): Promise<Configuratio
7676
throw new Error(`Webpack configuration file ${configPath} does not exist.`);
7777
}
7878

79+
let config;
7980
switch (path.extname(configPath)) {
8081
case '.mjs':
8182
// Load the ESM configuration file using the TypeScript dynamic import workaround.
8283
// Once TypeScript provides support for keeping the dynamic import this workaround can be
8384
// changed to a direct dynamic import.
84-
return (await loadEsmModule<{ default: Configuration }>(pathToFileURL(configPath))).default;
85+
config = await loadEsmModule<{ default: Configuration }>(pathToFileURL(configPath));
86+
break;
8587
case '.cjs':
86-
return require(configPath);
88+
config = require(configPath);
89+
break;
8790
default:
8891
// The file could be either CommonJS or ESM.
8992
// CommonJS is tried first then ESM if loading fails.
9093
try {
91-
return require(configPath);
94+
config = require(configPath);
95+
break;
9296
} catch (e) {
9397
if (
9498
(e as NodeJS.ErrnoException).code === 'ERR_REQUIRE_ESM' ||
@@ -97,11 +101,12 @@ export async function getWebpackConfig(configPath: string): Promise<Configuratio
97101
// Load the ESM configuration file using the TypeScript dynamic import workaround.
98102
// Once TypeScript provides support for keeping the dynamic import this workaround can be
99103
// changed to a direct dynamic import.
100-
return (await loadEsmModule<{ default: Configuration }>(pathToFileURL(configPath)))
101-
.default;
104+
config = await loadEsmModule<{ default: Configuration }>(pathToFileURL(configPath));
102105
}
103106

104107
throw e;
105108
}
106109
}
110+
111+
return 'default' in config ? config.default : config;
107112
}

0 commit comments

Comments
 (0)