From b602aeb144cada697143eb38047c559597daf8b4 Mon Sep 17 00:00:00 2001 From: Sebastian Beltran Date: Tue, 20 Jan 2026 10:11:04 -0500 Subject: [PATCH 1/3] feat!: drop support for node <20.9 --- .github/workflows/nodejs.yml | 13 +---- babel.config.js | 2 +- migration-v6.md | 4 ++ package-lock.json | 2 +- package.json | 2 +- scripts/prepare-test-for-old-node.js | 32 ------------ test/e2e/overlay.test.js | 73 +--------------------------- test/server/open-option.test.js | 26 ++-------- 8 files changed, 14 insertions(+), 140 deletions(-) delete mode 100644 scripts/prepare-test-for-old-node.js diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml index 74945cdeb6..601903e675 100644 --- a/.github/workflows/nodejs.yml +++ b/.github/workflows/nodejs.yml @@ -69,7 +69,7 @@ jobs: strategy: matrix: os: [ubuntu-latest, windows-latest, macos-latest] - node-version: [18.x, 20.x, 22.x, 24.x] + node-version: [20.x, 22.x, 24.x, 25.x] shard: ["1/4", "2/4", "3/4", "4/4"] webpack-version: [latest] @@ -91,12 +91,6 @@ jobs: - name: Install dependencies run: npm ci - - name: Install dependencies for Node.js@18 - run: | - npm i p-retry@^4.5.0 open@^8.0.9 - node ./scripts/prepare-test-for-old-node.js - if: matrix.node-version == '18.x' - - name: Setup firefox if: matrix.os != 'windows-latest' uses: browser-actions/setup-firefox@latest @@ -111,13 +105,8 @@ jobs: rm -r client cp -R tmp-client client - - name: Run tests for webpack version ${{ matrix.webpack-version }} - run: node_modules/.bin/jest --coverage --ci --shard=${{ matrix.shard }} - if: matrix.node-version == '18.x' - - name: Run tests for webpack version ${{ matrix.webpack-version }} run: npm run test:coverage -- --ci --shard=${{ matrix.shard }} - if: matrix.node-version != '18.x' - name: Submit coverage data to codecov uses: codecov/codecov-action@v5 diff --git a/babel.config.js b/babel.config.js index 419046888f..a34db39f39 100644 --- a/babel.config.js +++ b/babel.config.js @@ -24,7 +24,7 @@ module.exports = (api) => { "@babel/preset-env", { targets: { - node: "18.12.0", + node: "20.9.0", }, }, ], diff --git a/migration-v6.md b/migration-v6.md index 2a44ec147a..9a4d78722d 100644 --- a/migration-v6.md +++ b/migration-v6.md @@ -2,6 +2,10 @@ This document serves as a migration guide for `webpack-dev-server@6.0.0`. +## ⚠ BREAKING CHANGES + +- Minimum supported `Node.js` version is `20.9.0`. + ## Deprecations - The static methods `internalIP` and `internalIPSync` were removed. Use `findIp` instead. diff --git a/package-lock.json b/package-lock.json index 9680dbf5e1..b76fbf7d16 100644 --- a/package-lock.json +++ b/package-lock.json @@ -105,7 +105,7 @@ "webpack-merge": "^6.0.1" }, "engines": { - "node": ">= 18.12.0" + "node": ">= 20.9.0" }, "funding": { "type": "opencollective", diff --git a/package.json b/package.json index 84d2d5b407..0cc2dc22d7 100644 --- a/package.json +++ b/package.json @@ -148,6 +148,6 @@ } }, "engines": { - "node": ">= 18.12.0" + "node": ">= 20.9.0" } } diff --git a/scripts/prepare-test-for-old-node.js b/scripts/prepare-test-for-old-node.js deleted file mode 100644 index a5ef6dbfa1..0000000000 --- a/scripts/prepare-test-for-old-node.js +++ /dev/null @@ -1,32 +0,0 @@ -"use strict"; - -const fs = require("node:fs"); -const path = require("node:path"); - -/** - * @returns {Promise} - */ -async function setup() { - const serverCodePath = path.resolve(__dirname, "../lib/Server.js"); - let serverCode = await fs.promises.readFile(serverCodePath, "utf8"); - - serverCode = serverCode.replaceAll( - /\(await import\((".+")\)\)\.default/g, - "require($1)", - ); - - await fs.promises.writeFile(serverCodePath, serverCode); -} - -Promise.resolve() - .then(() => setup()) - // eslint-disable-next-line unicorn/prefer-top-level-await - .then( - () => { - // eslint-disable-next-line no-console - console.log("The setup was successful"); - }, - (error) => { - throw error; - }, - ); diff --git a/test/e2e/overlay.test.js b/test/e2e/overlay.test.js index be3c059233..5ccb678bdd 100644 --- a/test/e2e/overlay.test.js +++ b/test/e2e/overlay.test.js @@ -2,6 +2,7 @@ const path = require("node:path"); const fs = require("graceful-fs"); +const prettier = require("prettier"); const waitForExpect = require("wait-for-expect"); const webpack = require("webpack"); const Server = require("../../lib/Server"); @@ -68,19 +69,7 @@ const delay = (ms) => setTimeout(resolve, ms); }); -let prettier; -let prettierHTML; -let prettierCSS; - describe("overlay", () => { - beforeAll(async () => { - // Due problems with ESM modules for Node.js@18 - // TODO replace it on import/require when Node.js@18 will be dropped - prettier = require("../../node_modules/prettier/standalone"); - prettierHTML = require("../../node_modules/prettier/plugins/html"); - prettierCSS = require("../../node_modules/prettier/plugins/postcss"); - }); - it("should show a warning for initial compilation", async () => { const compiler = webpack(config); @@ -113,13 +102,11 @@ describe("overlay", () => { expect( await prettier.format(pageHtml, { parser: "html", - plugins: [prettierHTML, prettierCSS], }), ).toMatchSnapshot("page html"); expect( await prettier.format(overlayHtml, { parser: "html", - plugins: [prettierHTML, prettierCSS], }), ).toMatchSnapshot("overlay html"); } finally { @@ -160,13 +147,11 @@ describe("overlay", () => { expect( await prettier.format(pageHtml, { parser: "html", - plugins: [prettierHTML, prettierCSS], }), ).toMatchSnapshot("page html"); expect( await prettier.format(overlayHtml, { parser: "html", - plugins: [prettierHTML, prettierCSS], }), ).toMatchSnapshot("overlay html"); } finally { @@ -211,13 +196,11 @@ describe("overlay", () => { expect( await prettier.format(pageHtml, { parser: "html", - plugins: [prettierHTML, prettierCSS], }), ).toMatchSnapshot("page html"); expect( await prettier.format(overlayHtml, { parser: "html", - plugins: [prettierHTML, prettierCSS], }), ).toMatchSnapshot("overlay html"); } finally { @@ -260,13 +243,11 @@ describe("overlay", () => { expect( await prettier.format(pageHtml, { parser: "html", - plugins: [prettierHTML, prettierCSS], }), ).toMatchSnapshot("page html"); expect( await prettier.format(overlayHtml, { parser: "html", - plugins: [prettierHTML, prettierCSS], }), ).toMatchSnapshot("overlay html"); } finally { @@ -308,13 +289,11 @@ describe("overlay", () => { expect( await prettier.format(pageHtml, { parser: "html", - plugins: [prettierHTML, prettierCSS], }), ).toMatchSnapshot("page html"); expect( await prettier.format(overlayHtml, { parser: "html", - plugins: [prettierHTML, prettierCSS], }), ).toMatchSnapshot("overlay html"); } finally { @@ -346,7 +325,6 @@ describe("overlay", () => { expect( await prettier.format(pageHtml, { parser: "html", - plugins: [prettierHTML, prettierCSS], }), ).toMatchSnapshot("page html initial"); @@ -371,13 +349,11 @@ describe("overlay", () => { expect( await prettier.format(pageHtml, { parser: "html", - plugins: [prettierHTML, prettierCSS], }), ).toMatchSnapshot("page html with error"); expect( await prettier.format(overlayHtml, { parser: "html", - plugins: [prettierHTML, prettierCSS], }), ).toMatchSnapshot("overlay html"); @@ -394,7 +370,6 @@ describe("overlay", () => { expect( await prettier.format(pageHtml, { parser: "html", - plugins: [prettierHTML, prettierCSS], }), ).toMatchSnapshot("page html after fix error"); } finally { @@ -426,7 +401,6 @@ describe("overlay", () => { expect( await prettier.format(pageHtml, { parser: "html", - plugins: [prettierHTML, prettierCSS], }), ).toMatchSnapshot("page html initial"); @@ -451,13 +425,11 @@ describe("overlay", () => { expect( await prettier.format(pageHtml, { parser: "html", - plugins: [prettierHTML, prettierCSS], }), ).toMatchSnapshot("page html with error"); expect( await prettier.format(overlayHtml, { parser: "html", - plugins: [prettierHTML, prettierCSS], }), ).toMatchSnapshot("overlay html"); @@ -477,13 +449,11 @@ describe("overlay", () => { expect( await prettier.format(pageHtml, { parser: "html", - plugins: [prettierHTML, prettierCSS], }), ).toMatchSnapshot("page html with other error"); expect( await prettier.format(overlayHtml, { parser: "html", - plugins: [prettierHTML, prettierCSS], }), ).toMatchSnapshot("overlay html"); @@ -500,7 +470,6 @@ describe("overlay", () => { expect( await prettier.format(pageHtml, { parser: "html", - plugins: [prettierHTML, prettierCSS], }), ).toMatchSnapshot("page html after fix error"); } finally { @@ -532,7 +501,6 @@ describe("overlay", () => { expect( await prettier.format(pageHtml, { parser: "html", - plugins: [prettierHTML, prettierCSS], }), ).toMatchSnapshot("page html initial"); @@ -557,13 +525,11 @@ describe("overlay", () => { expect( await prettier.format(pageHtml, { parser: "html", - plugins: [prettierHTML, prettierCSS], }), ).toMatchSnapshot("page html with error"); expect( await prettier.format(overlayHtml, { parser: "html", - plugins: [prettierHTML, prettierCSS], }), ).toMatchSnapshot("overlay html"); @@ -586,7 +552,6 @@ describe("overlay", () => { expect( await prettier.format(pageHtml, { parser: "html", - plugins: [prettierHTML, prettierCSS], }), ).toMatchSnapshot("page html after close"); @@ -677,7 +642,6 @@ describe("overlay", () => { expect( await prettier.format(pageHtml, { parser: "html", - plugins: [prettierHTML, prettierCSS], }), ).toMatchSnapshot("page html"); } finally { @@ -720,7 +684,6 @@ describe("overlay", () => { expect( await prettier.format(pageHtml, { parser: "html", - plugins: [prettierHTML, prettierCSS], }), ).toMatchSnapshot("page html"); } finally { @@ -810,13 +773,11 @@ describe("overlay", () => { expect( await prettier.format(pageHtml, { parser: "html", - plugins: [prettierHTML, prettierCSS], }), ).toMatchSnapshot("page html"); expect( await prettier.format(overlayHtml, { parser: "html", - plugins: [prettierHTML, prettierCSS], }), ).toMatchSnapshot("overlay html"); } finally { @@ -860,13 +821,11 @@ describe("overlay", () => { expect( await prettier.format(pageHtml, { parser: "html", - plugins: [prettierHTML, prettierCSS], }), ).toMatchSnapshot("page html"); expect( await prettier.format(overlayHtml, { parser: "html", - plugins: [prettierHTML, prettierCSS], }), ).toMatchSnapshot("overlay html"); } finally { @@ -912,13 +871,11 @@ describe("overlay", () => { expect( await prettier.format(pageHtml, { parser: "html", - plugins: [prettierHTML, prettierCSS], }), ).toMatchSnapshot("page html"); expect( await prettier.format(overlayHtml, { parser: "html", - plugins: [prettierHTML, prettierCSS], }), ).toMatchSnapshot("overlay html"); } finally { @@ -964,13 +921,11 @@ describe("overlay", () => { expect( await prettier.format(pageHtml, { parser: "html", - plugins: [prettierHTML, prettierCSS], }), ).toMatchSnapshot("page html"); expect( await prettier.format(overlayHtml, { parser: "html", - plugins: [prettierHTML, prettierCSS], }), ).toMatchSnapshot("overlay html"); } finally { @@ -1011,7 +966,6 @@ describe("overlay", () => { expect( await prettier.format(pageHtml, { parser: "html", - plugins: [prettierHTML, prettierCSS], }), ).toMatchSnapshot("page html"); } finally { @@ -1054,7 +1008,6 @@ describe("overlay", () => { expect( await prettier.format(pageHtml, { parser: "html", - plugins: [prettierHTML, prettierCSS], }), ).toMatchSnapshot("page html"); } finally { @@ -1145,13 +1098,11 @@ describe("overlay", () => { expect( await prettier.format(pageHtml, { parser: "html", - plugins: [prettierHTML, prettierCSS], }), ).toMatchSnapshot("page html"); expect( await prettier.format(overlayHtml, { parser: "html", - plugins: [prettierHTML, prettierCSS], }), ).toMatchSnapshot("overlay html"); } finally { @@ -1195,13 +1146,11 @@ describe("overlay", () => { expect( await prettier.format(pageHtml, { parser: "html", - plugins: [prettierHTML, prettierCSS], }), ).toMatchSnapshot("page html"); expect( await prettier.format(overlayHtml, { parser: "html", - plugins: [prettierHTML, prettierCSS], }), ).toMatchSnapshot("overlay html"); } finally { @@ -1258,13 +1207,11 @@ describe("overlay", () => { expect( await prettier.format(pageHtml, { parser: "html", - plugins: [prettierHTML, prettierCSS], }), ).toMatchSnapshot("page html"); expect( await prettier.format(overlayHtml, { parser: "html", - plugins: [prettierHTML, prettierCSS], }), ).toMatchSnapshot("overlay html"); } finally { @@ -1331,13 +1278,11 @@ describe("overlay", () => { expect( await prettier.format(pageHtml, { parser: "html", - plugins: [prettierHTML, prettierCSS], }), ).toMatchSnapshot("page html"); expect( await prettier.format(overlayHtml, { parser: "html", - plugins: [prettierHTML, prettierCSS], }), ).toMatchSnapshot("overlay html"); } finally { @@ -1379,7 +1324,6 @@ describe("overlay", () => { expect( await prettier.format(pageHtml, { parser: "html", - plugins: [prettierHTML, prettierCSS], }), ).toMatchSnapshot("page html"); } finally { @@ -1425,13 +1369,11 @@ describe("overlay", () => { expect( await prettier.format(pageHtml, { parser: "html", - plugins: [prettierHTML, prettierCSS], }), ).toMatchSnapshot("page html"); expect( await prettier.format(overlayHtml, { parser: "html", - plugins: [prettierHTML, prettierCSS], }), ).toMatchSnapshot("overlay html"); } finally { @@ -1477,13 +1419,11 @@ describe("overlay", () => { expect( await prettier.format(pageHtml, { parser: "html", - plugins: [prettierHTML, prettierCSS], }), ).toMatchSnapshot("page html"); expect( await prettier.format(overlayHtml, { parser: "html", - plugins: [prettierHTML, prettierCSS], }), ).toMatchSnapshot("overlay html"); } finally { @@ -1528,13 +1468,11 @@ describe("overlay", () => { expect( await prettier.format(pageHtml, { parser: "html", - plugins: [prettierHTML, prettierCSS], }), ).toMatchSnapshot("page html"); expect( await prettier.format(overlayHtml, { parser: "html", - plugins: [prettierHTML, prettierCSS], }), ).toMatchSnapshot("overlay html"); @@ -1557,7 +1495,6 @@ describe("overlay", () => { expect( await prettier.format(pageHtmlAfterClose, { parser: "html", - plugins: [prettierHTML, prettierCSS], }), ).toMatchSnapshot("page html"); } finally { @@ -1611,13 +1548,11 @@ describe("overlay", () => { expect( await prettier.format(pageHtml, { parser: "html", - plugins: [prettierHTML, prettierCSS], }), ).toMatchSnapshot("page html"); expect( await prettier.format(overlayHtml, { parser: "html", - plugins: [prettierHTML, prettierCSS], }), ).toMatchSnapshot("overlay html"); } finally { @@ -1672,13 +1607,11 @@ describe("overlay", () => { expect( await prettier.format(pageHtml, { parser: "html", - plugins: [prettierHTML, prettierCSS], }), ).toMatchSnapshot("page html"); expect( await prettier.format(overlayHtml, { parser: "html", - plugins: [prettierHTML, prettierCSS], }), ).toMatchSnapshot("overlay html"); } finally { @@ -1724,7 +1657,6 @@ describe("overlay", () => { expect( await prettier.format(overlayHtml, { parser: "html", - plugins: [prettierHTML, prettierCSS], }), ).toMatchSnapshot("overlay html"); } finally { @@ -1814,7 +1746,6 @@ describe("overlay", () => { expect( await prettier.format(overlayHtml, { parser: "html", - plugins: [prettierHTML, prettierCSS], }), ).toMatchSnapshot("overlay html"); } finally { @@ -1956,7 +1887,6 @@ describe("overlay", () => { expect( await prettier.format(pageHtml, { parser: "html", - plugins: [prettierHTML, prettierCSS], }), ).toMatchSnapshot("page html"); expect( @@ -1967,7 +1897,6 @@ describe("overlay", () => { ), { parser: "html", - plugins: [prettierHTML, prettierCSS], }, ), ).toMatchSnapshot("overlay html"); diff --git a/test/server/open-option.test.js b/test/server/open-option.test.js index 11e249d4ba..292eb59b07 100644 --- a/test/server/open-option.test.js +++ b/test/server/open-option.test.js @@ -7,34 +7,18 @@ const port = require("../ports-map")["open-option"]; const internalIPv4 = Server.findIp("v4", false); -let open; - -const needRequireMock = - process.version.startsWith("v18") || process.version.startsWith("v19"); - -if (needRequireMock) { - open = require("open"); - - jest.mock("open"); - - open.mockImplementation(() => ({ - catch: jest.fn(), - })); -} - describe('"open" option', () => { let compiler; + let open; beforeEach(async () => { compiler = webpack(config); - if (!needRequireMock) { - jest.unstable_mockModule("open", () => ({ - default: jest.fn(() => Promise.resolve()), - })); + jest.unstable_mockModule("open", () => ({ + default: jest.fn(() => Promise.resolve()), + })); - open = (await import("open")).default; - } + open = (await import("open")).default; }); afterEach(async () => { From b97eea69e59e35eef835a424b5de51036a255fef Mon Sep 17 00:00:00 2001 From: Sebastian Beltran Date: Tue, 20 Jan 2026 10:35:12 -0500 Subject: [PATCH 2/3] feat: update baseline-browser-mapping to version 2.9.16 --- package-lock.json | 7 ++++--- package.json | 1 + 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index b76fbf7d16..fdbf0f2dc0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -62,6 +62,7 @@ "acorn": "^8.14.0", "babel-jest": "^30.0.4", "babel-loader": "^10.0.0", + "baseline-browser-mapping": "^2.9.16", "connect": "^3.7.0", "core-js": "^3.38.1", "cspell": "^8.15.5", @@ -6819,9 +6820,9 @@ "license": "MIT" }, "node_modules/baseline-browser-mapping": { - "version": "2.8.29", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.29.tgz", - "integrity": "sha512-sXdt2elaVnhpDNRDz+1BDx1JQoJRuNk7oVlAlbGiFkLikHCAQiccexF/9e91zVi6RCgqspl04aP+6Cnl9zRLrA==", + "version": "2.9.16", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.16.tgz", + "integrity": "sha512-KeUZdBuxngy825i8xvzaK1Ncnkx0tBmb3k8DkEuqjKRkmtvNTjey2ZsNeh8Dw4lfKvbCOu9oeNx2TKm2vHqcRw==", "devOptional": true, "license": "Apache-2.0", "bin": { diff --git a/package.json b/package.json index 0cc2dc22d7..4879567bc4 100644 --- a/package.json +++ b/package.json @@ -94,6 +94,7 @@ "acorn": "^8.14.0", "babel-jest": "^30.0.4", "babel-loader": "^10.0.0", + "baseline-browser-mapping": "^2.9.16", "connect": "^3.7.0", "core-js": "^3.38.1", "cspell": "^8.15.5", From 8b505920daceca60c905973a78d778d8345ea50d Mon Sep 17 00:00:00 2001 From: Sebastian Beltran Date: Tue, 20 Jan 2026 10:42:08 -0500 Subject: [PATCH 3/3] chore: remove baseline-browser-mapping dependency --- package-lock.json | 1 - package.json | 1 - 2 files changed, 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index fdbf0f2dc0..0824a00b3a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -62,7 +62,6 @@ "acorn": "^8.14.0", "babel-jest": "^30.0.4", "babel-loader": "^10.0.0", - "baseline-browser-mapping": "^2.9.16", "connect": "^3.7.0", "core-js": "^3.38.1", "cspell": "^8.15.5", diff --git a/package.json b/package.json index 4879567bc4..0cc2dc22d7 100644 --- a/package.json +++ b/package.json @@ -94,7 +94,6 @@ "acorn": "^8.14.0", "babel-jest": "^30.0.4", "babel-loader": "^10.0.0", - "baseline-browser-mapping": "^2.9.16", "connect": "^3.7.0", "core-js": "^3.38.1", "cspell": "^8.15.5",