Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Changing confirm prompt so Ctrl+C exits the process #252

Open
tiagonapoli opened this issue Feb 4, 2020 · 13 comments
Open

Changing confirm prompt so Ctrl+C exits the process #252

tiagonapoli opened this issue Feb 4, 2020 · 13 comments
Labels
enhancement New feature or request question Further information is requested

Comments

@tiagonapoli
Copy link

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
}
@terkelg
Copy link
Owner

terkelg commented Mar 18, 2020

Thank you @tiagonapoli. Currently there are no native solutions to this. It should be an option in the next major version of prompts 👍

@terkelg terkelg pinned this issue Mar 18, 2020
@terkelg terkelg added enhancement New feature or request question Further information is requested labels Mar 18, 2020
@lnbxyz
Copy link

lnbxyz commented Mar 18, 2020

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?

@terkelg
Copy link
Owner

terkelg commented Mar 23, 2020

sorry, I don't have any timeline at the moment. I'm quite busy working on other projects 👍

@elliot-nelson
Copy link
Contributor

elliot-nelson commented Apr 22, 2020

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).

@elliot-nelson
Copy link
Contributor

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. 🥩

@Blazzike
Copy link

My recommended solution:

onState: (state) => {
  if (state.aborted) {
    process.nextTick(() => {
      process.exit(0);
    })
  }
}

@ChocolateLoverRaj
Copy link

@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?

@raineorshine
Copy link

raineorshine commented May 15, 2021

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.)

@MMK21Hub
Copy link

MMK21Hub commented Mar 6, 2022

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.

@andersaamodt
Copy link

andersaamodt commented Aug 10, 2022

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.

@jedwards1211
Copy link

@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...

@calebeby
Copy link

calebeby commented Oct 20, 2024

Prompts does not directly intercept signals. It does set the input to raw mode (to listen for individual keypresses). In raw mode the default ctrl+c handling doesn't work.

Here is the workaround I am using:

// Handle Ctrl+C even when raw mode is used (for prompts)
process.stdin.on("keypress", function (_chunk, key) {
  if (key && key.name === "c" && key.ctrl) {
    process.exit(130);
  }
});

Based on this stack overflow answer: https://stackoverflow.com/questions/20165605/detecting-ctrlc-in-node-js

Bonus: Since prompts hides your cursor for some prompts, adding this (kinda hacky) to the listener makes it show the cursor on exit

process.stdout.write("\x1B[?25h");

@jedwards1211
Copy link

@calebeby oh, thanks, I didn't think about that. It would be good for prompts to at least provide an option to handle Ctrl-C by doing process.kill(process.pid, 'SIGINT')

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request question Further information is requested
Projects
None yet
Development

No branches or pull requests