Skip to content

Commit f524913

Browse files
fix: reset the failure exit code after a successful rebuild in webpack serve
A failed build sets the failure exit code, which `webpack serve` keeps on shutdown. The code stayed set after the error was fixed, so stopping the dev server after a successful rebuild still exited with 1. The stats callback now clears the failure exit code it set when a later build succeeds. Also test the fallback for webpack before 5.112, which doesn't apply `plugins` of multi configurations. Claude-Session: https://claude.ai/code/session_013FhutRFjuQemBzekJMYcqL
1 parent 5954947 commit f524913

5 files changed

Lines changed: 86 additions & 1 deletion

File tree

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3944,6 +3944,9 @@ class WebpackCLI {
39443944
({ Readable } = await import("node:stream"));
39453945
}
39463946

3947+
// Whether the failure exit code comes from a previous build, so a successful rebuild resets it
3948+
let isFailedBuild = false;
3949+
39473950
return (error, stats): void => {
39483951
if (error) {
39493952
this.logger.error(error);
@@ -3952,6 +3955,10 @@ class WebpackCLI {
39523955

39533956
if (stats && (stats.hasErrors() || (options.failOnWarnings && stats.hasWarnings()))) {
39543957
process.exitCode = 1;
3958+
isFailedBuild = true;
3959+
} else if (stats && isFailedBuild) {
3960+
process.exitCode = undefined;
3961+
isFailedBuild = false;
39553962
}
39563963

39573964
if (!stats) {
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
module.exports = (env) => ({
2+
mode: "development",
3+
entry: env.entry,
4+
});

‎test/serve/error-handling/serve-error-handling.test.js‎

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
/* eslint-disable jest/require-top-level-describe -- Version-gated describe */
44

5+
const fs = require("node:fs");
6+
const os = require("node:os");
7+
const path = require("node:path");
58
const [devServerVersion] = require("webpack-dev-server/package.json").version;
69
const { run, runWatch } = require("../../utils/test-utils");
710

@@ -32,6 +35,52 @@ describeDevServer6("serve error handling", () => {
3235
expect(exitCode).toBe(1);
3336
});
3437

38+
test("should reset the failure exit code after a successful rebuild", async () => {
39+
const directory = fs.mkdtempSync(path.join(os.tmpdir(), "webpack-cli-serve-recover-"));
40+
const entry = path.join(directory, "index.js");
41+
42+
fs.writeFileSync(entry, "const broken = {;");
43+
44+
try {
45+
const { exitCode, stdout } = await runWatch(
46+
__dirname,
47+
[
48+
"serve",
49+
"--config",
50+
"recover.config.js",
51+
"--env",
52+
`entry=${entry}`,
53+
"--watch-options-stdin",
54+
"--port",
55+
port,
56+
],
57+
{
58+
handler: (proc) => {
59+
let output = "";
60+
let fixed = false;
61+
62+
proc.stdout.on("data", (chunk) => {
63+
output += chunk.toString();
64+
65+
if (!fixed && output.includes("ERROR in")) {
66+
fixed = true;
67+
fs.writeFileSync(entry, "console.log('fixed');");
68+
} else if (fixed && output.includes("compiled successfully")) {
69+
proc.stdin.end();
70+
}
71+
});
72+
},
73+
},
74+
);
75+
76+
expect(stdout).toContain("ERROR in");
77+
expect(stdout).toContain("compiled successfully");
78+
expect(exitCode).toBe(0);
79+
} finally {
80+
fs.rmSync(directory, { recursive: true, force: true });
81+
}
82+
});
83+
3584
test("should print the stats with warnings using the '--fail-on-warnings' option", async () => {
3685
const { exitCode, stderr, stdout } = await runWatch(
3786
__dirname,
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
"use strict";
2+
3+
// Loaded via `WEBPACK_PACKAGE`: webpack before 5.112 doesn't apply `plugins` of multi configurations
4+
const webpack = require("webpack");
5+
6+
module.exports = new Proxy(webpack, {
7+
get: (target, property) => (property === "version" ? "5.111.0" : Reflect.get(target, property)),
8+
});

‎test/serve/per-compiler/serve-per-compiler.test.js‎

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
/* eslint-disable jest/require-top-level-describe -- Version-gated describe */
44

5+
const path = require("node:path");
56
const [devServerVersion] = require("webpack-dev-server/package.json").version;
67
const { runWatch } = require("../../utils/test-utils");
78

@@ -11,9 +12,10 @@ const getGetPort = () => import("get-port");
1112
* Starts `webpack serve` and requests every bundle from every dev server once they listen.
1213
* @param {string[]} args extra CLI arguments
1314
* @param {string=} config configuration file
15+
* @param {Record<string, string>=} env environment variables
1416
* @returns {Promise<{ exitCode: number, stdout: string, stderr: string, responses: Record<string, number> }>} result
1517
*/
16-
const serve = async (args, config = "webpack.config.js") => {
18+
const serve = async (args, config = "webpack.config.js", env = {}) => {
1719
const { default: getPort } = await getGetPort();
1820
const appPort = await getPort();
1921
const adminPort = await getPort({ exclude: [appPort] });
@@ -35,6 +37,7 @@ const serve = async (args, config = "webpack.config.js") => {
3537
...args,
3638
],
3739
{
40+
env,
3841
handler: (proc) => {
3942
let output = "";
4043
let requesting = false;
@@ -86,6 +89,20 @@ describeDevServer6("serve with a dev server per compiler", () => {
8689
expect(stdout.match(/admin\.js/g)).toHaveLength(1);
8790
});
8891

92+
test("should serve all compilers by every dev server with webpack before 5.112", async () => {
93+
const { exitCode, responses } = await serve([], "webpack.config.js", {
94+
WEBPACK_PACKAGE: path.resolve(__dirname, "./old-webpack.js"),
95+
});
96+
97+
expect(exitCode).toBe(0);
98+
expect(responses).toEqual({
99+
"app:app": 200,
100+
"app:admin": 200,
101+
"admin:app": 200,
102+
"admin:admin": 200,
103+
});
104+
});
105+
89106
test("should serve each compiler only by its own dev server using the '--dev-server-per-compiler' option", async () => {
90107
const { exitCode, responses, stdout } = await serve(["--dev-server-per-compiler"]);
91108

0 commit comments

Comments
 (0)