prompts
prompts copied to clipboard
Changing confirm prompt so Ctrl+C exits the process
Is your feature request related to a problem?
In my use case when pressing CTRL+C on a prompt the program should immediately terminate. Using the default confirm prompt I noted that it would answer yes
and continue the program, so I implemented the following solution - is there a native one?
import prompts from 'prompts'
interface PromptState {
aborted: boolean
}
const enableTerminalCursor = () => {
process.stdout.write('\x1B[?25h')
}
const onState = (state: PromptState) => {
if (state.aborted) {
// If we don't re-enable the terminal cursor before exiting
// the program, the cursor will remain hidden
enableTerminalCursor()
process.stdout.write('\n')
process.exit(1)
}
}
export const promptConfirm = async (message: string, initial = true): Promise<boolean> => {
const { response } = await prompts([{ message, initial, onState, type: 'confirm', name: 'response' }])
return response
}
Thank you @tiagonapoli. Currently there are no native solutions to this. It should be an option in the next major version of prompts 👍
Thank you @tiagonapoli. Currently there are no native solutions to this. It should be an option in the next major version of prompts 👍
This is great to hear since I'm currently facing the same issue! Would you happen to have a roadmap or any estimates for the next version?
sorry, I don't have any timeline at the moment. I'm quite busy working on other projects 👍
An alternate suggestion: if you have a lot of prompts, it might be better to make an onState that throws an error that can bubble up to the top, rather than immediately hard-exiting the program.
class AbortedError extends Error { }
function onState(state) {
if (state.aborted) throw new AbortedError();
}
Now you have a reusable onState you can attach to all of your prompts, and in the outermost catch of your application, you can choose to:
main().catch(error => {
if (!(error instanceof AbortedError)) {
console.error(error.stack);
}
process.exitCode = 1;
});
This way the prompts
package will clean up terminal stuff itself, because you aren't exiting the process before it can clean up, and your CLI program exits with an exit code 1 a little more gracefully (in my opinion).
Update: I should have tested my suggestion first, it looks like throwing from inside onState() actually ends up aborting the process anyway because you are inside a readline callback. So my "graceful" claim is not true today. 🥩
My recommended solution:
onState: (state) => {
if (state.aborted) {
process.nextTick(() => {
process.exit(0);
})
}
}
@tiagonapoli Maybe there could be an API similar to this:
import prompts from 'prompts'
prompts.setExitOnAbort(true)
Or this:
import prompts from 'prompts'
prompts.overrideAborting(false)
sorry, I don't have any timeline at the moment. I'm quite busy working on other projects 👍
@terkelg Can we (me or @tiagonapoli or anyone) implement these changes and make a pull request?
Please don't add a procedural interface that mutates the module instance. It's more error-prone and messy if you need to revert the behavior back.
If anything you could add exitOnAbort: true
as a prompt option, but I've come to prefer @Blazzike's solution since it gives you control over the exit code. Or you could do something like exitOnAbort: 0
, exitOnAbort: 1
, etc. (Personally I think exitOnAbort: 1
should be the default.)
I have the same problem. Unfortunately, pressing esc
is also treated as an abortion, but I only want Ctrl+C to exit the whole thing. I don't believe that there is any provided way to do this.
Yes, it seems @MMK21Hub is right, is there a workaround? Maybe it is possible to remove Prompt's SIGINT (Ctrl-C?) event listener, or to attach a new event listener that catches SIGINT preferentially and exits? I tried to do this and failed.
@terkelg why does prompts intercept any signals in the first place? It seems like a very strange design choice to me, but maybe there's something I'm not thinking about...