diff --git a/packages/webpack-cli/lib/utils/prompt.js b/packages/webpack-cli/lib/utils/prompt.js index e28b944b2a1..be3fe7c7436 100644 --- a/packages/webpack-cli/lib/utils/prompt.js +++ b/packages/webpack-cli/lib/utils/prompt.js @@ -1,3 +1,5 @@ +const utils = require("./index"); + const prompt = ({ message, defaultResponse, stream }) => { const readline = require("readline"); const rl = readline.createInterface({ @@ -9,7 +11,6 @@ const prompt = ({ message, defaultResponse, stream }) => { rl.question(`${message} `, (answer) => { // Close the stream rl.close(); - const response = (answer || defaultResponse).toLowerCase(); // Resolve with the input response @@ -19,6 +20,20 @@ const prompt = ({ message, defaultResponse, stream }) => { resolve(false); } }); + + const handleSIGINT = () => { + rl.close(); + process.stdout.write("\n"); + utils.logger.warn("Operation canceled."); + process.exit(0); + }; + + rl.on("SIGINT", () => { + handleSIGINT(); + }); + process.on("SIGINT", () => { + handleSIGINT(); + }); }); }; diff --git a/test/api/helpers/runAndKillPrompt.js b/test/api/helpers/runAndKillPrompt.js new file mode 100644 index 00000000000..88d20c0a05c --- /dev/null +++ b/test/api/helpers/runAndKillPrompt.js @@ -0,0 +1,9 @@ +const prompt = require("../../../packages/webpack-cli/lib/utils/prompt"); + +setTimeout(() => process.kill(process.pid, "SIGINT"), 1000); + +prompt({ + message: "Would you like to install package 'test'? (Yes/No):", + defaultResponse: "No", + stream: process.stdout, +}); diff --git a/test/api/prompt.test.js b/test/api/prompt.test.js index 752f2aafb49..261a88fef0d 100755 --- a/test/api/prompt.test.js +++ b/test/api/prompt.test.js @@ -1,5 +1,9 @@ const prompt = require("../../packages/webpack-cli/lib/utils/prompt"); +const { resolve } = require("path"); +// eslint-disable-next-line node/no-unpublished-require +const execa = require("execa"); const { Writable } = require("stream"); +const { isWindows } = require("../utils/test-utils"); describe("prompt", () => { class MyWritable extends Writable { @@ -32,9 +36,10 @@ describe("prompt", () => { expect(resultFail).toBe(false); }); - it('should work with "yes" && "y" response', async () => { + it('should work with "yes", "YES"," and y" response', async () => { const myWritable1 = new MyWritable("yes\r"); const myWritable2 = new MyWritable("y\r"); + const myWritable3 = new MyWritable("YES\r"); const resultSuccess1 = await prompt({ message: "message", @@ -48,8 +53,15 @@ describe("prompt", () => { stream: myWritable2, }); + const resultSuccess3 = await prompt({ + message: "message", + defaultResponse: "no", + stream: myWritable3, + }); + expect(resultSuccess1).toBe(true); expect(resultSuccess2).toBe(true); + expect(resultSuccess3).toBe(true); }); it("should work with unknown response", async () => { @@ -63,4 +75,23 @@ describe("prompt", () => { expect(result).toBe(false); }); + + it("should respond to SIGINT", async () => { + const runAndKillPrompt = resolve(__dirname, "./helpers/runAndKillPrompt.js"); + + const { exitCode, stderr, stdout } = await execa("node", [runAndKillPrompt], { + cwd: resolve(__dirname), + reject: false, + maxBuffer: Infinity, + }); + + // TODO: fix for windows + if (isWindows) { + expect(true).toBe(true); + } else { + expect(exitCode).toBe(0); + expect(stderr).toContain("[webpack-cli] Operation canceled."); + expect(stdout).toContain("Would you like to install package 'test'? (Yes/No):"); + } + }); });