diff --git a/.changeset/server-reporting-overhead.md b/.changeset/server-reporting-overhead.md
new file mode 100644
index 0000000000..7a91a3eea4
--- /dev/null
+++ b/.changeset/server-reporting-overhead.md
@@ -0,0 +1,5 @@
+---
+"webpack-dev-server": patch
+---
+
+Collect only the statistics needed by the asset report, reuse directory-listing middleware, and serialize each WebSocket broadcast only once.
diff --git a/lib/Server.js b/lib/Server.js
index 38577856c9..587a2006ea 100644
--- a/lib/Server.js
+++ b/lib/Server.js
@@ -2171,14 +2171,22 @@ class Server {
'
',
);
+ const isMultiStats =
+ typeof (/** @type {MultiStats} */ (stats).stats) !== "undefined";
+ const jsonStats = /** @type {Stats | MultiStats} */ (stats).toJson({
+ all: false,
+ children: isMultiStats,
+ assets: true,
+ publicPath: true,
+ });
+
/**
* @type {StatsCompilation[]}
*/
- const statsForPrint =
- typeof (/** @type {MultiStats} */ (stats).stats) !== "undefined"
- ? /** @type {NonNullable} */
- (/** @type {MultiStats} */ (stats).toJson().children)
- : [/** @type {Stats} */ (stats).toJson()];
+ const statsForPrint = isMultiStats
+ ? /** @type {NonNullable} */
+ (jsonStats.children)
+ : [jsonStats];
res.write("Assets Report:
");
@@ -2411,6 +2419,11 @@ class Server {
for (const staticOption of staticOptions) {
for (const publicPath of staticOption.publicPath) {
if (staticOption.serveIndex) {
+ const serveIndexMiddleware = serveIndex(
+ staticOption.directory,
+ staticOption.serveIndex,
+ );
+
middlewares.push({
name: "serve-index",
path: publicPath,
@@ -2426,11 +2439,7 @@ class Server {
return next();
}
- serveIndex(
- staticOption.directory,
- /** @type {ServeIndexOptions} */
- (staticOption.serveIndex),
- )(req, res, next);
+ serveIndexMiddleware(req, res, next);
},
});
}
@@ -3339,10 +3348,14 @@ class Server {
* @param {EXPECTED_ANY=} params params
*/
sendMessage(clients, type, data, params) {
+ /** @type {string | undefined} */
+ let message;
+
for (const client of clients) {
// `ws` uses `WebSocket.OPEN`, which is `1`
if (client.readyState === 1) {
- client.send(JSON.stringify({ type, data, params }));
+ message ??= JSON.stringify({ type, data, params });
+ client.send(message);
}
}
}
diff --git a/test/e2e/built-in-routes.test.js b/test/e2e/built-in-routes.test.js
index 9432839afb..544c74fe77 100644
--- a/test/e2e/built-in-routes.test.js
+++ b/test/e2e/built-in-routes.test.js
@@ -1,5 +1,6 @@
import { afterEach, beforeEach, describe, it } from "node:test";
import { expect } from "expect";
+import { spyOn } from "jest-mock";
import webpack from "webpack";
import Server from "../../lib/Server.js";
import config from "../fixtures/client-config/webpack.config.js";
@@ -61,6 +62,11 @@ describe("Built in routes", () => {
});
it("should handle GET request to directory index and list all middleware directories", async (t) => {
+ const stats = await new Promise((resolve) => {
+ server.middleware.waitUntilValid(resolve);
+ });
+ const toJsonSpy = spyOn(stats, "toJson");
+
page
.on("console", (message) => {
consoleMessages.push(message);
@@ -85,6 +91,14 @@ describe("Built in routes", () => {
t.assert.snapshot(consoleMessages.map((message) => message.text()));
t.assert.snapshot(pageErrors);
+
+ expect(toJsonSpy).toHaveBeenCalledWith({
+ all: false,
+ children: false,
+ assets: true,
+ publicPath: true,
+ });
+ toJsonSpy.mockRestore();
});
it("should handle HEAD request to directory index", async (t) => {
@@ -191,6 +205,11 @@ describe("Built in routes", () => {
});
it("should handle GET request to directory index and list all middleware directories", async (t) => {
+ const stats = await new Promise((resolve) => {
+ server.middleware.waitUntilValid(resolve);
+ });
+ const toJsonSpy = spyOn(stats, "toJson");
+
page
.on("console", (message) => {
consoleMessages.push(message);
@@ -215,6 +234,14 @@ describe("Built in routes", () => {
t.assert.snapshot(consoleMessages.map((message) => message.text()));
t.assert.snapshot(pageErrors);
+
+ expect(toJsonSpy).toHaveBeenCalledWith({
+ all: false,
+ children: true,
+ assets: true,
+ publicPath: true,
+ });
+ toJsonSpy.mockRestore();
});
});
});