diff --git a/lib/Server.js b/lib/Server.js index 16d1c428af..44584dd3c7 100644 --- a/lib/Server.js +++ b/lib/Server.js @@ -1114,11 +1114,31 @@ class Server { if (this.options.setupExitSignals) { const signals = ["SIGINT", "SIGTERM"]; + let needForceShutdown = false; + + const exitProcess = () => { + // eslint-disable-next-line no-process-exit + process.exit(); + }; + signals.forEach((signal) => { process.on(signal, () => { + if (needForceShutdown) { + exitProcess(); + } + + this.logger.info( + "Gracefully shutting down. To force exit, press ^C again. Please wait..." + ); + + needForceShutdown = true; + this.stopCallback(() => { - // eslint-disable-next-line no-process-exit - process.exit(); + if (typeof this.compiler.close === "function") { + this.compiler.close(exitProcess); + } else { + exitProcess(); + } }); }); }); diff --git a/test/server/setupExitSignals-option.test.js b/test/server/setupExitSignals-option.test.js index 68f8dcafc2..8cd84ed906 100644 --- a/test/server/setupExitSignals-option.test.js +++ b/test/server/setupExitSignals-option.test.js @@ -11,6 +11,7 @@ describe("setupExitSignals option", () => { let exitSpy; let stopCallbackSpy; let stdinResumeSpy; + let closeCallbackSpy; const signals = ["SIGINT", "SIGTERM"]; @@ -36,6 +37,10 @@ describe("setupExitSignals option", () => { .spyOn(process.stdin, "resume") .mockImplementation(() => {}); stopCallbackSpy = jest.spyOn(server, "stopCallback"); + + if (server.compiler.close) { + closeCallbackSpy = jest.spyOn(server.compiler, "close"); + } }); afterEach(async () => { @@ -57,6 +62,10 @@ describe("setupExitSignals option", () => { if (doExit) { expect(stopCallbackSpy.mock.calls.length).toEqual(1); + if (server.compiler.close) { + expect(closeCallbackSpy.mock.calls.length).toEqual(1); + } + clearInterval(interval); resolve();