Skip to content

Commit 8eb805c

Browse files
refactor!: update minimum required NodeJS version (#4668)
1 parent 635b907 commit 8eb805c

12 files changed

Lines changed: 93 additions & 73 deletions

File tree

.github/workflows/nodejs.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ jobs:
6161
fail-fast: true
6262
matrix:
6363
os: [ubuntu-latest, windows-latest, macos-latest]
64-
node-version: [18.x, 20.x, 22.x, 24.x]
64+
node-version: [20.x, 22.x, 24.x, 25.x]
6565
webpack-version: [latest]
6666
dev-server-version: [latest]
6767

package-lock.json

Lines changed: 24 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@
5959
"concat-stream": "^2.0.0",
6060
"cspell": "^9.4.0",
6161
"css-loader": "^7.1.2",
62-
"del-cli": "^6.0.0",
62+
"del-cli": "^7.0.0",
6363
"eslint": "^9.29.0",
6464
"eslint-config-webpack": "^4.5.0",
6565
"execa": "^9.6.1",
@@ -87,6 +87,6 @@
8787
"webpack": "5.x.x"
8888
},
8989
"engines": {
90-
"node": ">=18.12.0"
90+
"node": ">=20.9.0"
9191
}
9292
}

packages/create-webpack-app/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,6 @@
5252
"@types/ejs": "^3.1.5"
5353
},
5454
"engines": {
55-
"node": ">=18.12.0"
55+
"node": ">=20.9.0"
5656
}
5757
}

packages/create-webpack-app/tsconfig.json

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,9 @@
33
"exclude": ["src/utils/__tests__"],
44
"compilerOptions": {
55
"outDir": "lib",
6-
"rootDir": "src",
7-
"module": "esnext"
6+
"rootDir": "src"
87
},
9-
"include": ["src"],
8+
"include": ["./src"],
109
"references": [
1110
{
1211
"path": "../webpack-cli"

packages/webpack-cli/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@
6262
}
6363
},
6464
"engines": {
65-
"node": ">=18.12.0"
65+
"node": ">=20.9.0"
6666
},
6767
"gitHead": "fb50f766851f500ca12867a2aa9de81fa6e368f9"
6868
}

packages/webpack-cli/src/plugins/cli-plugin.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ export default class CLIPlugin {
1111
}
1212

1313
async setupBundleAnalyzerPlugin(compiler: Compiler) {
14-
const { BundleAnalyzerPlugin } = require("webpack-bundle-analyzer");
14+
// @ts-expect-error No types right now
15+
const { BundleAnalyzerPlugin } = (await import("webpack-bundle-analyzer")).default;
1516

1617
const bundleAnalyzerPlugin = compiler.options.plugins.some(
1718
(plugin) => plugin instanceof BundleAnalyzerPlugin,
@@ -25,7 +26,7 @@ export default class CLIPlugin {
2526
static #progressStates: [number, ...string[]][] = [];
2627

2728
setupProgressPlugin(compiler: Compiler) {
28-
const { ProgressPlugin } = compiler.webpack || require("webpack");
29+
const { ProgressPlugin } = compiler.webpack;
2930
const progressPlugin = compiler.options.plugins.some(
3031
(plugin) => plugin instanceof ProgressPlugin,
3132
);

packages/webpack-cli/src/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ interface IWebpackCLI {
4747
capitalizeFirstLetter: StringFormatter;
4848
checkPackageExists(packageName: string): boolean;
4949
getAvailablePackageManagers(): PackageManager[];
50-
getDefaultPackageManager(): PackageManager | undefined;
50+
getDefaultPackageManager(): Promise<PackageManager | undefined>;
5151
doInstall(packageName: string, options?: PackageInstallOptions): Promise<string>;
5252
loadJSONFile<T = unknown>(path: Path, handleError: boolean): Promise<T>;
5353
tryRequireThenImport<T = unknown>(module: ModuleName, handleError: boolean): Promise<T>;

packages/webpack-cli/src/webpack-cli.ts

Lines changed: 29 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,7 @@ class WebpackCLI implements IWebpackCLI {
226226
return false;
227227
}
228228

229+
// TODO remove me
229230
getAvailablePackageManagers(): PackageManager[] {
230231
const { sync } = require("cross-spawn");
231232

@@ -252,9 +253,10 @@ class WebpackCLI implements IWebpackCLI {
252253
return availableInstallers;
253254
}
254255

255-
getDefaultPackageManager(): PackageManager | undefined {
256-
const { sync } = require("cross-spawn");
256+
async getDefaultPackageManager(): Promise<PackageManager | undefined> {
257+
const { sync } = await import("cross-spawn");
257258

259+
// TODO use async methods
258260
const hasLocalNpm = fs.existsSync(path.resolve(process.cwd(), "package-lock.json"));
259261

260262
if (hasLocalNpm) {
@@ -307,7 +309,7 @@ class WebpackCLI implements IWebpackCLI {
307309
}
308310

309311
async doInstall(packageName: string, options: PackageInstallOptions = {}): Promise<string> {
310-
const packageManager = this.getDefaultPackageManager();
312+
const packageManager = await this.getDefaultPackageManager();
311313

312314
if (!packageManager) {
313315
this.logger.error("Can't find package manager");
@@ -319,10 +321,10 @@ class WebpackCLI implements IWebpackCLI {
319321
options.preMessage();
320322
}
321323

322-
const prompt = ({ message, defaultResponse, stream }: PromptOptions) => {
323-
const readline = require("node:readline");
324+
const { createInterface } = await import("node:readline");
324325

325-
const rl = readline.createInterface({
326+
const prompt = ({ message, defaultResponse, stream }: PromptOptions) => {
327+
const rl = createInterface({
326328
input: process.stdin,
327329
output: stream,
328330
});
@@ -470,6 +472,7 @@ class WebpackCLI implements IWebpackCLI {
470472
return result || {};
471473
}
472474

475+
// TODO remove me
473476
loadJSONFile<T = unknown>(pathToFile: Path, handleError = true): T {
474477
let result;
475478

@@ -559,7 +562,7 @@ class WebpackCLI implements IWebpackCLI {
559562

560563
defaultInformation.npmPackages = `{${defaultPackages.map((item) => `*${item}*`).join(",")}}`;
561564

562-
const envinfo = await import("envinfo");
565+
const envinfo = (await import("envinfo")).default;
563566

564567
let info = await envinfo.run(defaultInformation, envinfoConfig);
565568

@@ -1231,8 +1234,8 @@ class WebpackCLI implements IWebpackCLI {
12311234
},
12321235
);
12331236
} else if (this.#isCommand(commandName, WebpackCLI.#commands.serve)) {
1234-
const loadDevServerOptions = () => {
1235-
const devServer = require(WEBPACK_DEV_SERVER_PACKAGE);
1237+
const loadDevServerOptions = async () => {
1238+
const devServer = (await import(WEBPACK_DEV_SERVER_PACKAGE)).default;
12361239

12371240
const options: Record<string, WebpackCLIBuiltInOption> = this.webpack.cli.getArguments(
12381241
devServer.schema,
@@ -1253,7 +1256,7 @@ class WebpackCLI implements IWebpackCLI {
12531256
let devServerOptions = [];
12541257

12551258
try {
1256-
devServerOptions = loadDevServerOptions();
1259+
devServerOptions = await loadDevServerOptions();
12571260
} catch (error) {
12581261
this.logger.error(
12591262
`You need to install 'webpack-dev-server' for running 'webpack serve'.\n${error}`,
@@ -1270,7 +1273,7 @@ class WebpackCLI implements IWebpackCLI {
12701273
let devServerFlags: WebpackCLIBuiltInOption[] = [];
12711274

12721275
try {
1273-
devServerFlags = loadDevServerOptions();
1276+
devServerFlags = await loadDevServerOptions();
12741277
} catch {
12751278
// Nothing, to prevent future updates
12761279
}
@@ -1312,7 +1315,19 @@ class WebpackCLI implements IWebpackCLI {
13121315
return;
13131316
}
13141317

1315-
const servers: (typeof DevServer)[] = [];
1318+
type DevServerConstructor = typeof import("webpack-dev-server");
1319+
let DevServer: DevServerConstructor;
1320+
1321+
try {
1322+
DevServer = (await import(WEBPACK_DEV_SERVER_PACKAGE)).default;
1323+
} catch (err) {
1324+
this.logger.error(
1325+
`You need to install 'webpack-dev-server' for running 'webpack serve'.\n${err}`,
1326+
);
1327+
process.exit(2);
1328+
}
1329+
1330+
const servers: InstanceType<DevServerConstructor>[] = [];
13161331

13171332
if (this.needWatchStdin(compiler)) {
13181333
process.stdin.on("end", () => {
@@ -1323,18 +1338,6 @@ class WebpackCLI implements IWebpackCLI {
13231338
process.stdin.resume();
13241339
}
13251340

1326-
const DevServer = require(WEBPACK_DEV_SERVER_PACKAGE);
1327-
1328-
try {
1329-
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
1330-
require(`${WEBPACK_DEV_SERVER_PACKAGE}/package.json`).version;
1331-
} catch (err) {
1332-
this.logger.error(
1333-
`You need to install 'webpack-dev-server' for running 'webpack serve'.\n${err}`,
1334-
);
1335-
process.exit(2);
1336-
}
1337-
13381341
const compilers = this.isMultipleCompiler(compiler) ? compiler.compilers : [compiler];
13391342
const possibleCompilers = compilers.filter((compiler) => compiler.options.devServer);
13401343
const compilersForDevServer =
@@ -1426,7 +1429,7 @@ class WebpackCLI implements IWebpackCLI {
14261429

14271430
await server.start();
14281431

1429-
servers.push(server);
1432+
servers.push(server as unknown as InstanceType<DevServerConstructor>);
14301433
} catch (error) {
14311434
if (this.isValidationError(error as Error)) {
14321435
this.logger.error((error as Error).message);
@@ -2483,7 +2486,7 @@ class WebpackCLI implements IWebpackCLI {
24832486
process.exit(2);
24842487
}
24852488

2486-
const CLIPlugin = (await import("./plugins/cli-plugin.js")).default;
2489+
const { default: CLIPlugin } = (await import("./plugins/cli-plugin.js")).default;
24872490

24882491
const internalBuildConfig = (item: Configuration) => {
24892492
const originalWatchValue = item.watch;

test/api/do-install.test.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ const CLI = require("../../packages/webpack-cli/lib/webpack-cli");
55

66
const readlineQuestionMock = jest.fn();
77

8-
jest.mock("node:readline", () => ({
8+
jest.unstable_mockModule("node:readline", () => ({
99
createInterface: jest.fn().mockReturnValue({
1010
question: readlineQuestionMock,
1111
close: jest.fn().mockReturnValue(undefined),
@@ -34,7 +34,7 @@ describe("doInstall", () => {
3434

3535
it("should prompt to install using npm if npm is package manager", async () => {
3636
readlineQuestionMock.mockImplementation((_questionTest, cb) => cb("y"));
37-
getDefaultPackageManagerSpy.mockReturnValue("npm");
37+
getDefaultPackageManagerSpy.mockResolvedValue("npm");
3838

3939
const installResult = await cli.doInstall("test-package");
4040

0 commit comments

Comments
 (0)