From 419383ed81790093867ae2c61de9596617234583 Mon Sep 17 00:00:00 2001 From: alexander-akait Date: Tue, 13 Jan 2026 16:10:41 +0300 Subject: [PATCH 1/4] fix: use graceful shutdown when file system cache is enabled --- packages/webpack-cli/src/webpack-cli.ts | 23 +++++++--- test/build/cache/cache.test.js | 44 ++++++++++++++++++- .../cache/graceful-exit.webpack.config.js | 36 +++++++++++++++ 3 files changed, 95 insertions(+), 8 deletions(-) create mode 100644 test/build/cache/graceful-exit.webpack.config.js diff --git a/packages/webpack-cli/src/webpack-cli.ts b/packages/webpack-cli/src/webpack-cli.ts index a288269fadf..ae26dff3317 100644 --- a/packages/webpack-cli/src/webpack-cli.ts +++ b/packages/webpack-cli/src/webpack-cli.ts @@ -2490,14 +2490,19 @@ class WebpackCLI implements IWebpackCLI { return; } - const isWatch = (compiler: WebpackCompiler): boolean => + const needGracefulShutdown = (compiler: WebpackCompiler): boolean => Boolean( this.isMultipleCompiler(compiler) - ? compiler.compilers.some((compiler) => compiler.options.watch) - : compiler.options.watch, + ? compiler.compilers.some( + (compiler) => + compiler.options.watch || + (compiler.options.cache && compiler.options.cache.type === "filesystem"), + ) + : compiler.options.watch || + (compiler.options.cache && compiler.options.cache.type === "filesystem"), ); - if (isWatch(compiler)) { + if (needGracefulShutdown(compiler)) { let needForceShutdown = false; for (const signal of EXIT_SIGNALS) { @@ -2507,13 +2512,17 @@ class WebpackCLI implements IWebpackCLI { process.exit(0); } - this.logger.info( - "Gracefully shutting down. To force exit, press ^C again. Please wait...", - ); + // Output message after delay to avoid extra logging + const timeout = setTimeout(() => { + this.logger.info( + "Gracefully shutting down. To force exit, press ^C again. Please wait...", + ); + }, 2000); needForceShutdown = true; compiler.close(() => { + clearTimeout(timeout); process.exit(0); }); }; diff --git a/test/build/cache/cache.test.js b/test/build/cache/cache.test.js index 1101f59930b..704c1fbfa77 100644 --- a/test/build/cache/cache.test.js +++ b/test/build/cache/cache.test.js @@ -2,7 +2,7 @@ const fs = require("node:fs"); const path = require("node:path"); -const { run } = require("../../utils/test-utils"); +const { processKill, run, runWatch } = require("../../utils/test-utils"); describe("cache", () => { it("should work", async () => { @@ -218,4 +218,46 @@ describe("cache", () => { expect(stderr).toBeTruthy(); expect(stdout).toBeTruthy(); }); + + it("should graceful shutdown", async () => { + fs.rmSync( + path.join( + __dirname, + "../../../node_modules/.cache/webpack/cache-graceful-shutdown-development", + ), + { + recursive: true, + force: true, + }, + ); + + let stdout = ""; + let stderr = ""; + + await runWatch(__dirname, ["--config", "./graceful-exit.webpack.config.js", "--watch"], { + handler: (proc) => { + proc.stdout.on("data", (chunk) => { + const data = chunk.toString(); + + stdout += data; + + if (data.includes("app.bundle.js")) { + processKill(proc); + } + }); + + proc.stderr.on("data", (chunk) => { + const data = chunk.toString(); + + stderr += data; + }); + }, + }); + + expect(stderr.match(/No pack exists at/g)).toHaveLength(1); + expect(stderr.match(/Stored pack/g)).toHaveLength(1); + expect(stdout).toContain( + "Gracefully shutting down. To force exit, press ^C again. Please wait...", + ); + }); }); diff --git a/test/build/cache/graceful-exit.webpack.config.js b/test/build/cache/graceful-exit.webpack.config.js new file mode 100644 index 00000000000..e87e5f62f6c --- /dev/null +++ b/test/build/cache/graceful-exit.webpack.config.js @@ -0,0 +1,36 @@ +const path = require("node:path"); + +class InfiniteWaitPlugin { + apply(compiler) { + compiler.hooks.shutdown.tapPromise("Graceful Exit Test", async () => { + await new Promise((resolve) => { + setTimeout(() => { + resolve(); + }, 3000); + }); + }); + } +} + +module.exports = { + mode: "development", + name: "cache-graceful-shutdown", + cache: { + type: "filesystem", + buildDependencies: { + config: [__filename], + }, + }, + infrastructureLogging: { + debug: /cache/, + }, + entry: { + app: "./src/main.js", + }, + output: { + filename: "[name].bundle.js", + chunkFilename: "[name].bundle.js", + path: path.resolve(__dirname, "dist"), + }, + plugins: [new InfiniteWaitPlugin()], +}; From 99e67f6481c47d293e9295bddeb2887bbe6e3463 Mon Sep 17 00:00:00 2001 From: alexander-akait Date: Tue, 13 Jan 2026 16:44:23 +0300 Subject: [PATCH 2/4] test: fix --- test/build/cache/cache.test.js | 9 --------- 1 file changed, 9 deletions(-) diff --git a/test/build/cache/cache.test.js b/test/build/cache/cache.test.js index 704c1fbfa77..0e39728b41d 100644 --- a/test/build/cache/cache.test.js +++ b/test/build/cache/cache.test.js @@ -232,7 +232,6 @@ describe("cache", () => { ); let stdout = ""; - let stderr = ""; await runWatch(__dirname, ["--config", "./graceful-exit.webpack.config.js", "--watch"], { handler: (proc) => { @@ -245,17 +244,9 @@ describe("cache", () => { processKill(proc); } }); - - proc.stderr.on("data", (chunk) => { - const data = chunk.toString(); - - stderr += data; - }); }, }); - expect(stderr.match(/No pack exists at/g)).toHaveLength(1); - expect(stderr.match(/Stored pack/g)).toHaveLength(1); expect(stdout).toContain( "Gracefully shutting down. To force exit, press ^C again. Please wait...", ); From f63de45e45edba7ecea7fb356c0244d27d09d971 Mon Sep 17 00:00:00 2001 From: alexander-akait Date: Tue, 13 Jan 2026 17:31:55 +0300 Subject: [PATCH 3/4] test: debug --- test/build/cache/cache.test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/build/cache/cache.test.js b/test/build/cache/cache.test.js index 0e39728b41d..bcfe9ce7790 100644 --- a/test/build/cache/cache.test.js +++ b/test/build/cache/cache.test.js @@ -2,7 +2,7 @@ const fs = require("node:fs"); const path = require("node:path"); -const { processKill, run, runWatch } = require("../../utils/test-utils"); +const { run, runWatch } = require("../../utils/test-utils"); describe("cache", () => { it("should work", async () => { @@ -241,7 +241,7 @@ describe("cache", () => { stdout += data; if (data.includes("app.bundle.js")) { - processKill(proc); + proc.kill(); } }); }, From 367a2bdfe9d7b61c5c3f696a110225e36e79a13c Mon Sep 17 00:00:00 2001 From: alexander-akait Date: Tue, 13 Jan 2026 18:10:28 +0300 Subject: [PATCH 4/4] test: fix --- test/build/cache/cache.test.js | 66 +++++++++++++++++----------------- 1 file changed, 34 insertions(+), 32 deletions(-) diff --git a/test/build/cache/cache.test.js b/test/build/cache/cache.test.js index bcfe9ce7790..94af6a45448 100644 --- a/test/build/cache/cache.test.js +++ b/test/build/cache/cache.test.js @@ -2,7 +2,7 @@ const fs = require("node:fs"); const path = require("node:path"); -const { run, runWatch } = require("../../utils/test-utils"); +const { isWindows, processKill, run, runWatch } = require("../../utils/test-utils"); describe("cache", () => { it("should work", async () => { @@ -219,36 +219,38 @@ describe("cache", () => { expect(stdout).toBeTruthy(); }); - it("should graceful shutdown", async () => { - fs.rmSync( - path.join( - __dirname, - "../../../node_modules/.cache/webpack/cache-graceful-shutdown-development", - ), - { - recursive: true, - force: true, - }, - ); - - let stdout = ""; - - await runWatch(__dirname, ["--config", "./graceful-exit.webpack.config.js", "--watch"], { - handler: (proc) => { - proc.stdout.on("data", (chunk) => { - const data = chunk.toString(); - - stdout += data; - - if (data.includes("app.bundle.js")) { - proc.kill(); - } - }); - }, + if (!isWindows) { + it("should graceful shutdown", async () => { + fs.rmSync( + path.join( + __dirname, + "../../../node_modules/.cache/webpack/cache-graceful-shutdown-development", + ), + { + recursive: true, + force: true, + }, + ); + + let stdout = ""; + + await runWatch(__dirname, ["--config", "./graceful-exit.webpack.config.js", "--watch"], { + handler: (proc) => { + proc.stdout.on("data", (chunk) => { + const data = chunk.toString(); + + stdout += data; + + if (data.includes("app.bundle.js")) { + processKill(proc); + } + }); + }, + }); + + expect(stdout).toContain( + "Gracefully shutting down. To force exit, press ^C again. Please wait...", + ); }); - - expect(stdout).toContain( - "Gracefully shutting down. To force exit, press ^C again. Please wait...", - ); - }); + } });