Skip to content

Commit 2d6b91f

Browse files
committed
feat: prevent multiple server starts on recompilation and ensure clean shutdown
1 parent 07c25c9 commit 2d6b91f

2 files changed

Lines changed: 58 additions & 5 deletions

File tree

lib/Server.js

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3459,11 +3459,17 @@ class Server {
34593459
this.isPlugin = true;
34603460
this.logger = this.compiler.getInfrastructureLogger(pluginName);
34613461

3462+
let started = false;
3463+
34623464
this.compiler.hooks.watchRun.tapPromise(pluginName, async () => {
3463-
await this.start();
3465+
if (!started) {
3466+
started = true;
3467+
await this.start();
3468+
}
34643469
});
34653470

3466-
this.compiler.hooks.watchClose.tap(pluginName, async () => {
3471+
this.compiler.hooks.shutdown.tapPromise(pluginName, async () => {
3472+
started = false;
34673473
await this.stop();
34683474
});
34693475
}

test/e2e/api.test.js

Lines changed: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,7 @@ describe("API", () => {
1616

1717
server.apply(compiler);
1818

19-
// Use compile helper which waits for the server to be ready
20-
const { watching } = await compile(compiler, port);
19+
await compile(compiler, port);
2120

2221
const { page, browser } = await runBrowser();
2322

@@ -42,7 +41,55 @@ describe("API", () => {
4241
expect(pageErrors).toMatchSnapshot("page errors");
4342

4443
await browser.close();
45-
watching.close();
44+
await new Promise((resolve) => {
45+
compiler.close(resolve);
46+
});
47+
});
48+
49+
it("should not start the server multiple times on recompilation", async () => {
50+
const compiler = webpack(config);
51+
const server = new Server({ port });
52+
const startSpy = jest.spyOn(server, "start");
53+
54+
server.apply(compiler);
55+
56+
const { watching } = await compile(compiler, port);
57+
58+
// Trigger a recompilation by invalidating
59+
await new Promise((resolve) => {
60+
watching.invalidate(() => {
61+
resolve();
62+
});
63+
});
64+
65+
// Wait for the recompilation to finish
66+
await new Promise((resolve) => {
67+
setTimeout(resolve, 2000);
68+
});
69+
70+
expect(startSpy).toHaveBeenCalledTimes(1);
71+
72+
startSpy.mockRestore();
73+
await new Promise((resolve) => {
74+
compiler.close(resolve);
75+
});
76+
});
77+
78+
it("should stop the server cleanly via compiler.close()", async () => {
79+
const compiler = webpack(config);
80+
const server = new Server({ port });
81+
const stopSpy = jest.spyOn(server, "stop");
82+
83+
server.apply(compiler);
84+
85+
await compile(compiler, port);
86+
87+
await new Promise((resolve) => {
88+
compiler.close(resolve);
89+
});
90+
91+
expect(stopSpy).toHaveBeenCalledTimes(1);
92+
stopSpy.mockRestore();
4693
});
4794

4895
describe("WEBPACK_SERVE environment variable", () => {

0 commit comments

Comments
 (0)