Skip to content

Commit c22d870

Browse files
committed
feat: prevent multiple server starts on recompilation and ensure clean shutdown
1 parent 0e9081f commit c22d870

File tree

2 files changed

+58
-5
lines changed

2 files changed

+58
-5
lines changed

lib/Server.js

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

3447+
let started = false;
3448+
34473449
this.compiler.hooks.watchRun.tapPromise(pluginName, async () => {
3448-
await this.start();
3450+
if (!started) {
3451+
started = true;
3452+
await this.start();
3453+
}
34493454
});
34503455

3451-
this.compiler.hooks.watchClose.tap(pluginName, async () => {
3456+
this.compiler.hooks.shutdown.tapPromise(pluginName, async () => {
3457+
started = false;
34523458
await this.stop();
34533459
});
34543460
}

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)