From 91cfd0131a320a14002368ff1e5caf37ad9940d5 Mon Sep 17 00:00:00 2001 From: keha07 Date: Tue, 10 Mar 2026 14:53:08 +0530 Subject: [PATCH 1/6] fix(client): prevent overlay from closing immediately on runtime error at initial load --- client-src/index.js | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/client-src/index.js b/client-src/index.js index 60527ef029..7c49f698f0 100644 --- a/client-src/index.js +++ b/client-src/index.js @@ -84,8 +84,15 @@ const decodeOverlayOptions = (overlayOptions) => { const status = { isUnloading: false, currentHash: __webpack_hash__, + hasRuntimeError: false, }; +window.addEventListener("error", () => { + status.hasRuntimeError = true; +}); +window.addEventListener("unhandledrejection", () => { + status.hasRuntimeError = true; +}); /** * @returns {string} current script source */ @@ -403,8 +410,7 @@ const onSocketMessage = { invalid() { log.info("App updated. Recompiling..."); - // Fixes #1042. overlay doesn't clear if errors are fixed but warnings remain. - if (options.overlay) { + if (options.overlay && !status.hasRuntimeError) { overlay.send({ type: "DISMISS" }); } @@ -473,7 +479,7 @@ const onSocketMessage = { "still-ok": function stillOk() { log.info("Nothing changed."); - if (options.overlay) { + if (options.overlay && !status.hasRuntimeError) { overlay.send({ type: "DISMISS" }); } @@ -482,6 +488,8 @@ const onSocketMessage = { ok() { sendMessage("Ok"); + status.hasRuntimeError = false; + if (options.overlay) { overlay.send({ type: "DISMISS" }); } From 45bb7787aedb2de5c58f62f163ee47a2cded0eb3 Mon Sep 17 00:00:00 2001 From: keha07 Date: Tue, 10 Mar 2026 16:41:57 +0530 Subject: [PATCH 2/6] test: add test for overlay runtime error at initial load --- client-src/index.js | 1 + .../e2e/overlay-initial-runtime-error.test.js | 49 +++++++++++++++++++ test/fixtures/overlay-runtime-error.js | 1 + 3 files changed, 51 insertions(+) create mode 100644 test/e2e/overlay-initial-runtime-error.test.js create mode 100644 test/fixtures/overlay-runtime-error.js diff --git a/client-src/index.js b/client-src/index.js index 7c49f698f0..5634d07019 100644 --- a/client-src/index.js +++ b/client-src/index.js @@ -45,6 +45,7 @@ import sendMessage from "./utils/sendMessage.js"; * @property {boolean} isUnloading true when unloaded, otherwise false * @property {string} currentHash current hash * @property {string=} previousHash previous hash + * @property {boolean} hasRuntimeError true when a runtime error occurred */ /** diff --git a/test/e2e/overlay-initial-runtime-error.test.js b/test/e2e/overlay-initial-runtime-error.test.js new file mode 100644 index 0000000000..df022e023e --- /dev/null +++ b/test/e2e/overlay-initial-runtime-error.test.js @@ -0,0 +1,49 @@ +"use strict"; + +const path = require("node:path"); +const puppeteer = require("puppeteer"); +const webpack = require("webpack"); +const WebpackDevServer = require("../../lib/Server"); + +describe("overlay runtime error", () => { + let browser; + let page; + let server; + + beforeAll(async () => { + const config = { + mode: "development", + entry: path.resolve(__dirname, "../fixtures/overlay-runtime-error.js"), + }; + + const compiler = webpack(config); + + server = new WebpackDevServer( + { + port: 8081, + client: { + overlay: true, + }, + }, + compiler, + ); + + await server.start(); + + browser = await puppeteer.launch(); + page = await browser.newPage(); + }); + + afterAll(async () => { + await browser.close(); + await server.stop(); + }); + + it("should keep overlay visible on runtime error during initial load", async () => { + await page.goto("http://localhost:8081"); + + const overlay = await page.$("webpack-dev-server-client-overlay"); + + expect(overlay).not.toBeNull(); + }); +}); diff --git a/test/fixtures/overlay-runtime-error.js b/test/fixtures/overlay-runtime-error.js new file mode 100644 index 0000000000..e2891e4a51 --- /dev/null +++ b/test/fixtures/overlay-runtime-error.js @@ -0,0 +1 @@ +throw new Error("Initial runtime error"); From 6f04d9480cf5e0eee675edf183700e5bcb8a82bc Mon Sep 17 00:00:00 2001 From: keha07 Date: Tue, 10 Mar 2026 17:01:27 +0530 Subject: [PATCH 3/6] test(e2e): add runtime error overlay test for initial load --- .../e2e/overlay-initial-runtime-error.test.js | 49 ------------------- test/e2e/overlay.test.js | 36 ++++++++++++++ 2 files changed, 36 insertions(+), 49 deletions(-) delete mode 100644 test/e2e/overlay-initial-runtime-error.test.js diff --git a/test/e2e/overlay-initial-runtime-error.test.js b/test/e2e/overlay-initial-runtime-error.test.js deleted file mode 100644 index df022e023e..0000000000 --- a/test/e2e/overlay-initial-runtime-error.test.js +++ /dev/null @@ -1,49 +0,0 @@ -"use strict"; - -const path = require("node:path"); -const puppeteer = require("puppeteer"); -const webpack = require("webpack"); -const WebpackDevServer = require("../../lib/Server"); - -describe("overlay runtime error", () => { - let browser; - let page; - let server; - - beforeAll(async () => { - const config = { - mode: "development", - entry: path.resolve(__dirname, "../fixtures/overlay-runtime-error.js"), - }; - - const compiler = webpack(config); - - server = new WebpackDevServer( - { - port: 8081, - client: { - overlay: true, - }, - }, - compiler, - ); - - await server.start(); - - browser = await puppeteer.launch(); - page = await browser.newPage(); - }); - - afterAll(async () => { - await browser.close(); - await server.stop(); - }); - - it("should keep overlay visible on runtime error during initial load", async () => { - await page.goto("http://localhost:8081"); - - const overlay = await page.$("webpack-dev-server-client-overlay"); - - expect(overlay).not.toBeNull(); - }); -}); diff --git a/test/e2e/overlay.test.js b/test/e2e/overlay.test.js index be3c059233..114c413aaa 100644 --- a/test/e2e/overlay.test.js +++ b/test/e2e/overlay.test.js @@ -1976,4 +1976,40 @@ describe("overlay", () => { await server.stop(); } }); + + it("should keep overlay visible for runtime error on initial load", async () => { + const compiler = webpack(config); + + const server = new Server( + { + port, + }, + compiler, + ); + + await server.start(); + + const { page, browser } = await runBrowser(); + + try { + await page.goto(`http://localhost:${port}/`, { + waitUntil: "networkidle0", + }); + + await page.addScriptTag({ + content: ` + throw new Error("Initial runtime error"); + `, + }); + + await delay(1000); + + const overlayHandle = await page.$("#webpack-dev-server-client-overlay"); + + expect(overlayHandle).not.toBeNull(); + } finally { + await browser.close(); + await server.stop(); + } + }); }); From 6110319d92e7f9cf8e0af15c56f1d94a26f98d19 Mon Sep 17 00:00:00 2001 From: keha07 Date: Fri, 13 Mar 2026 11:03:42 +0530 Subject: [PATCH 4/6] fix(client): keep overlay visible for runtime error during initial load and add e2e test --- client-src/index.js | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/client-src/index.js b/client-src/index.js index 5634d07019..f4c77e874c 100644 --- a/client-src/index.js +++ b/client-src/index.js @@ -87,13 +87,15 @@ const status = { currentHash: __webpack_hash__, hasRuntimeError: false, }; -window.addEventListener("error", () => { - status.hasRuntimeError = true; -}); +if (typeof window !== "undefined") { + window.addEventListener("error", () => { + status.hasRuntimeError = true; + }); -window.addEventListener("unhandledrejection", () => { - status.hasRuntimeError = true; -}); + window.addEventListener("unhandledrejection", () => { + status.hasRuntimeError = true; + }); +} /** * @returns {string} current script source */ From 653c0340aa4e583941ac1d34bd997a07a75b361c Mon Sep 17 00:00:00 2001 From: keha07 Date: Fri, 13 Mar 2026 11:05:12 +0530 Subject: [PATCH 5/6] fix(client): keep overlay visible for runtime error during initial load and add e2e test --- client-src/index.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client-src/index.js b/client-src/index.js index f4c77e874c..56d7e2e9b9 100644 --- a/client-src/index.js +++ b/client-src/index.js @@ -87,12 +87,12 @@ const status = { currentHash: __webpack_hash__, hasRuntimeError: false, }; -if (typeof window !== "undefined") { - window.addEventListener("error", () => { +if (typeof self !== "undefined" && self.addEventListener) { + self.addEventListener("error", () => { status.hasRuntimeError = true; }); - window.addEventListener("unhandledrejection", () => { + self.addEventListener("unhandledrejection", () => { status.hasRuntimeError = true; }); } From fa89a055536cc434cf79b26d50dce9bc27043e3f Mon Sep 17 00:00:00 2001 From: keha07 Date: Fri, 13 Mar 2026 15:58:53 +0530 Subject: [PATCH 6/6] fix(client): handle runtime error detection only in browser context --- client-src/index.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client-src/index.js b/client-src/index.js index 56d7e2e9b9..3aa955bc50 100644 --- a/client-src/index.js +++ b/client-src/index.js @@ -87,12 +87,12 @@ const status = { currentHash: __webpack_hash__, hasRuntimeError: false, }; -if (typeof self !== "undefined" && self.addEventListener) { - self.addEventListener("error", () => { +if (typeof window !== "undefined" && window.addEventListener) { + window.addEventListener("error", () => { status.hasRuntimeError = true; }); - self.addEventListener("unhandledrejection", () => { + window.addEventListener("unhandledrejection", () => { status.hasRuntimeError = true; }); }