Closed aydn closed 1 year ago
Why not listen to those events yourself?
Those events never triggers in prompt mode. I want to return back to prev. menu or cancel a step with this keys.
'use strict';
process.on('SIGINT', () => console.log('bye!'));
const inquirer = require('inquirer');
inquirer.prompt({
type: 'input',
name: 'test',
message: 'hit CTRL + C to test SIGINT in prompt mode'
});
You can pass your own event listener like @SBoudrias suggested. I ended up doing this in my app since I needed synchronous behavior.
let stdin = process.stdin;
stdin.on("data", (key) => {
if (key == "\u0003") {
LOG("catching ctrl+c");
}
});
inquirer.prompt(questions, {input: stdin});
I would prefer prompt()
to return a rejected promise on Ctrl-C. This is achievable if you're willing to reach in and monkey with a Prompt
object's readline directly:
/**
* By default Inquirer handles Ctrl-C itself by force-quitting the process with
* no way to clean up. This wrapper around Inquirer throws a Error
* instead, allowing normal exception handling.
*/
async function safePrompt<T>(question: inquirer.Question<T>): Promise<T> {
const promptModule = inquirer.createPromptModule()
const ui = new inquirer.ui.Prompt((promptModule as any).prompts, {})
const deferred = PromiseUtils.deferred<T>()
// Remove the force-quit behavior
const rl = ui.rl
rl.listeners("SIGINT").forEach(listener => rl.off("SIGINT", listener as any))
// Insert our listener to reject the promise
function handleCtrlC() {
// remove the listener
rl.off("SIGINT", handleCtrlC)
// Clean up inquirer
ui.close()
// Then reject our promise
deferred.reject(
new Error("Aborted due to Ctrl-C during a prompt", ui)
)
}
rl.on("SIGINT", handleCtrlC)
// Run the UI
ui.run<T>([question]).then(deferred.resolve, deferred.reject)
return await deferred.promise
}
(Implementation of PromiseUtils.deferred left as an exercise to the reader)
I would prefer
prompt()
to return a rejected promise on Ctrl-C. This is achievable if you're willing to reach in and monkey with aPrompt
object's readline directly:/** * By default Inquirer handles Ctrl-C itself by force-quitting the process with * no way to clean up. This wrapper around Inquirer throws a Error * instead, allowing normal exception handling. */ async function safePrompt<T>(question: inquirer.Question<T>): Promise<T> { const promptModule = inquirer.createPromptModule() const ui = new inquirer.ui.Prompt((promptModule as any).prompts, {}) const deferred = PromiseUtils.deferred<T>() // Remove the force-quit behavior const rl = ui.rl rl.listeners("SIGINT").forEach(listener => rl.off("SIGINT", listener as any)) // Insert our listener to reject the promise function handleCtrlC() { // remove the listener rl.off("SIGINT", handleCtrlC) // Clean up inquirer ui.close() // Then reject our promise deferred.reject( new Error("Aborted due to Ctrl-C during a prompt", ui) ) } rl.on("SIGINT", handleCtrlC) // Run the UI ui.run<T>([question]).then(deferred.resolve, deferred.reject) return await deferred.promise }
(Implementation of PromiseUtils.deferred left as an exercise to the reader)
Hey, i'm using inquirer on nodejs, i would like to use this function but in a js file
i called the safePrompt using @justjake's code,
type Answer = {
templateName: string;
};
export async function askTemplateName() {
return safePrompt<Answer>([
{
name: 'templateName',
type: 'list',
message: 'template name:',
choices: ['node.js', 'next.js'],
},
]);
}
but i've seen an error like below, i think i definitely passed the name property to the Question object. i don't know why this error occurred.
node_modules/inquirer/lib/prompts/base.js:81
throw new Error('You must provide a `' + name + '` parameter');
at InputPrompt.throwParamError (node_modules/inquirer/lib/prompts/base.js:81:11)
at new Prompt (node_modules/inquirer/lib/prompts/base.js:38:12)
at new InputPrompt (node_modules/inquirer/lib/prompts/input.js:11:1)
at PromptUI.fetchAnswer (node_modules/inquirer/lib/ui/prompt.js:106:25)
at doInnerSub (node_modules/rxjs/src/internal/operators/mergeInternals.ts:71:15)
at outerNext (node_modules/rxjs/src/internal/operators/mergeInternals.ts:53:58)
at OperatorSubscriber._this._next (node_modules/rxjs/src/internal/operators/OperatorSubscriber.ts:70:13)
at OperatorSubscriber.Subscriber.next (node_modules/rxjs/src/internal/Subscriber.ts:75:12)
at node_modules/rxjs/src/internal/operators/mergeInternals.ts:85:24
at OperatorSubscriber._this._next (node_modules/rxjs/src/internal/operators/OperatorSubscriber.ts:70:13)
i implemented 'deferred promise' with reference to link,
export default class Deferred<T> {
public promise: Promise<T>;
// @ts-ignore
public resolve: (value: T | PromiseLike<T>) => void;
// @ts-ignore
public reject: (reason?: any) => void;
constructor() {
this.promise = new Promise<T>((resolve, reject) => {
this.resolve = resolve;
this.reject = reject;
});
}
}
and implement safePrompt like below.
export async function safePrompt<T>(question: DistinctQuestion<T>): Promise<T> {
const promptModule = inquirer.createPromptModule();
const ui = new inquirer.ui.Prompt<T>((promptModule as any).prompts);
const deferred = new Deferred<T>();
// Remove the force-quit behavior
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
const { rl } = ui;
rl.listeners('SIGINT').forEach(listener => rl.off('SIGINT', listener as any));
// Insert our listener to reject the promise
function handleCtrlC() {
// remove the listener
rl.off('SIGINT', handleCtrlC);
// Clean up inquirer
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
ui.close();
// Then reject our promise
deferred.reject(new Error(`Aborted due to Ctrl-C during a prompt. \n${ui.toString()}`));
}
rl.on('SIGINT', handleCtrlC);
// Run the UI
ui.run([question]).then(deferred.resolve, deferred.reject);
return deferred.promise;
}
How can i catch the SIGINT signal?
Closing this ticket as stale. Open new issues if you encounter problem with the new Inquirer API.
The new API expose a documented cancel
method to stop a readline. And I think the SIGINT handling is cleaner; though lemme know.
https://github.com/SBoudrias/Inquirer.js/blob/master/lib/ui/baseUI.js#L21
this.rl.on('SIGINT', this.onForceClose); process.on('exit', this.onForceClose);
Please provide an option or function to set exit handlers our own.