From e823546afcf4c66970af0da52f638d5f5e7824be Mon Sep 17 00:00:00 2001 From: Sebastian Beltran Date: Mon, 16 Feb 2026 03:31:15 +0000 Subject: [PATCH 1/2] feat: plugin support --- lib/Server.js | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/lib/Server.js b/lib/Server.js index 41cbe3f654..9e0d40db78 100644 --- a/lib/Server.js +++ b/lib/Server.js @@ -311,6 +311,8 @@ const DEFAULT_ALLOWED_PROTOCOLS = /^(file|.+-extension):/i; * @property {typeof useFn} use */ +const pluginName = "webpack-dev-server"; + /** * @template {BasicApplication} [A=ExpressApplication] * @template {BasicServer} [S=HTTPServer] @@ -330,7 +332,7 @@ class Server { /** * @type {ReturnType} */ - this.logger = this.compiler.getInfrastructureLogger("webpack-dev-server"); + this.logger = this.compiler.getInfrastructureLogger(pluginName); this.options = options; /** * @type {FSWatcher[]} @@ -3399,6 +3401,23 @@ class Server { .then(() => callback(), callback) .catch(callback); } + + /** + * @param {Compiler} compiler compiler + * @returns {void} + */ + apply(compiler) { + const pluginName = this.constructor.name; + this.compiler = compiler; + + this.compiler.hooks.watchRun.tapPromise(pluginName, async () => { + await this.start(); + }); + + this.compiler.hooks.watchClose.tap(pluginName, async () => { + await this.stop(); + }); + } } module.exports = Server; From 1ece81c17b3602d251312cd26567cb57e7df5add Mon Sep 17 00:00:00 2001 From: Sebastian Beltran Date: Sat, 21 Feb 2026 18:12:12 +0000 Subject: [PATCH 2/2] fix: correct errors when a compiler is not passed to the constructor --- lib/Server.js | 26 +++++++++++++++++++------- test/e2e/api.test.js | 34 ++++++++++++++++++++++++++++++++++ test/helpers/compile.js | 12 ++++++++++++ 3 files changed, 65 insertions(+), 7 deletions(-) create mode 100644 test/helpers/compile.js diff --git a/lib/Server.js b/lib/Server.js index 9e0d40db78..3563b24f00 100644 --- a/lib/Server.js +++ b/lib/Server.js @@ -328,11 +328,14 @@ class Server { baseDataPath: "options", }); - this.compiler = compiler; - /** - * @type {ReturnType} - */ - this.logger = this.compiler.getInfrastructureLogger(pluginName); + if (compiler) { + this.compiler = compiler; + + /** + * @type {ReturnType} + */ + this.logger = this.compiler.getInfrastructureLogger(pluginName); + } this.options = options; /** * @type {FSWatcher[]} @@ -1589,6 +1592,9 @@ class Server { * @returns {void} */ setupProgressPlugin() { + // In the case where there is no compiler and it’s not being used as a plugin. + if (this.compiler === undefined) return; + const { ProgressPlugin } = /** @type {MultiCompiler} */ (this.compiler).compilers @@ -1633,6 +1639,7 @@ class Server { * @returns {Promise} */ async initialize() { + if (this.compiler === undefined) return; this.setupHooks(); await this.setupApp(); @@ -1708,7 +1715,7 @@ class Server { needForceShutdown = true; this.stopCallback(() => { - if (typeof this.compiler.close === "function") { + if (typeof this.compiler?.close === "function") { this.compiler.close(() => { // eslint-disable-next-line n/no-process-exit process.exit(); @@ -1783,11 +1790,14 @@ class Server { * @returns {void} */ setupHooks() { + if (this.compiler === undefined) return; + this.compiler.hooks.invalid.tap("webpack-dev-server", () => { if (this.webSocketServer) { this.sendMessage(this.webSocketServer.clients, "invalid"); } }); + this.compiler.hooks.done.tap( "webpack-dev-server", /** @@ -1842,6 +1852,7 @@ class Server { * @returns {void} */ setupMiddlewares() { + if (this.compiler === undefined) return; /** * @type {Array} */ @@ -2333,6 +2344,7 @@ class Server { // middleware for serving webpack bundle /** @type {import("webpack-dev-middleware").API} */ this.middleware = webpackDevMiddleware( + // @ts-expect-error this.compiler, this.options.devMiddleware, ); @@ -3407,8 +3419,8 @@ class Server { * @returns {void} */ apply(compiler) { - const pluginName = this.constructor.name; this.compiler = compiler; + this.logger = this.compiler.getInfrastructureLogger(pluginName); this.compiler.hooks.watchRun.tapPromise(pluginName, async () => { await this.start(); diff --git a/test/e2e/api.test.js b/test/e2e/api.test.js index ee09842d04..4f66eb67be 100644 --- a/test/e2e/api.test.js +++ b/test/e2e/api.test.js @@ -4,11 +4,45 @@ const path = require("node:path"); const webpack = require("webpack"); const Server = require("../../lib/Server"); const config = require("../fixtures/client-config/webpack.config"); +const compile = require("../helpers/compile"); const runBrowser = require("../helpers/run-browser"); const sessionSubscribe = require("../helpers/session-subscribe"); const port = require("../ports-map").api; describe("API", () => { + it("should work with plugin API", async () => { + const compiler = webpack(config); + const server = new Server({ port }); + + server.apply(compiler); + await compile(compiler); + + const { page, browser } = await runBrowser(); + + const pageErrors = []; + const consoleMessages = []; + + page + .on("console", (message) => { + consoleMessages.push(message); + }) + .on("pageerror", (error) => { + pageErrors.push(error); + }); + + await page.goto(`http://127.0.0.1:${port}/`, { + waitUntil: "networkidle0", + }); + + expect(consoleMessages.map((message) => message.text())).toMatchSnapshot( + "console messages", + ); + expect(pageErrors).toMatchSnapshot("page errors"); + + await browser.close(); + compiler.watching.close(); + }); + describe("WEBPACK_SERVE environment variable", () => { const OLD_ENV = process.env; let server; diff --git a/test/helpers/compile.js b/test/helpers/compile.js new file mode 100644 index 0000000000..2696e4b053 --- /dev/null +++ b/test/helpers/compile.js @@ -0,0 +1,12 @@ +"use strict"; + +module.exports = (compiler) => + new Promise((resolve, reject) => { + compiler.run((error, stats) => { + if (error) { + return reject(error); + } + + return resolve(stats); + }); + });