From a6ac2faa6c2f892fb1226b9f1ed107c0767949e7 Mon Sep 17 00:00:00 2001 From: Marcelo Shima Date: Tue, 17 Sep 2024 15:51:42 -0300 Subject: [PATCH] fix(inquirer): adjust parameters (#1556) * don't expose `rl` --- packages/inquirer/inquirer.test.mts | 9 ++-- packages/inquirer/src/ui/prompt.mts | 73 ++++++++++++++--------------- 2 files changed, 42 insertions(+), 40 deletions(-) diff --git a/packages/inquirer/inquirer.test.mts b/packages/inquirer/inquirer.test.mts index 06a431b1c..23cacbb02 100644 --- a/packages/inquirer/inquirer.test.mts +++ b/packages/inquirer/inquirer.test.mts @@ -62,8 +62,11 @@ class StubEventualyFailingPrompt { timeout?: NodeJS.Timeout; run() { - this.timeout = setTimeout(() => {}, 1000); - return Promise.reject(new Error('This test prompt always reject')); + return new Promise((_, reject) => { + this.timeout = setTimeout(() => { + reject(new Error('This test prompt always reject')); + }, 1000); + }); } close() { @@ -802,7 +805,7 @@ describe('AbortSignal support', () => { localPrompt.registerPrompt('stub', StubEventualyFailingPrompt); const promise = localPrompt({ type: 'stub', name: 'q1', message: 'message' }); - abortController.abort(); + setTimeout(() => abortController.abort(), 0); await expect(promise).rejects.toThrow(AbortPromptError); }); diff --git a/packages/inquirer/src/ui/prompt.mts b/packages/inquirer/src/ui/prompt.mts index 4bd262c30..4f6016306 100644 --- a/packages/inquirer/src/ui/prompt.mts +++ b/packages/inquirer/src/ui/prompt.mts @@ -117,7 +117,7 @@ export interface LegacyPromptConstructor { export type PromptFn = ( config: Config, - context?: StreamOptions, + context: StreamOptions & { signal: AbortSignal }, ) => Promise; /** @@ -197,7 +197,6 @@ export default class PromptsRunner { process: Observable = EMPTY; private abortController: AbortController = new AbortController(); private opt: StreamOptions; - rl?: InquirerReadline; constructor(prompts: PromptCollection, opt: StreamOptions = {}) { this.opt = opt; @@ -300,62 +299,70 @@ export default class PromptsRunner { let cleanupSignal: (() => void) | undefined; const promptFn: PromptFn = isPromptConstructor(prompt) - ? (q, { signal } = {}) => + ? (q, opt) => new Promise((resolve, reject) => { + const { signal } = opt; + if (signal.aborted) { + reject(new AbortPromptError({ cause: signal.reason })); + return; + } + const rl = readline.createInterface( - setupReadlineOptions(this.opt), + setupReadlineOptions(opt), ) as InquirerReadline; - rl.resume(); + + /** + * Handle the ^C exit + */ + const onForceClose = () => { + this.close(); + process.kill(process.pid, 'SIGINT'); + console.log(''); + }; const onClose = () => { - process.removeListener('exit', this.onForceClose); - rl.removeListener('SIGINT', this.onForceClose); + process.removeListener('exit', onForceClose); + rl.removeListener('SIGINT', onForceClose); rl.setPrompt(''); rl.output.unmute(); rl.output.write(ansiEscapes.cursorShow); rl.output.end(); rl.close(); }; - this.rl = rl; // Make sure new prompt start on a newline when closing - process.on('exit', this.onForceClose); - rl.on('SIGINT', this.onForceClose); + process.on('exit', onForceClose); + rl.on('SIGINT', onForceClose); const activePrompt = new prompt(q, rl, this.answers); const cleanup = () => { onClose(); - this.rl = undefined; cleanupSignal?.(); }; - if (signal) { - const abort = () => { - reject(new AbortPromptError({ cause: signal.reason })); - cleanup(); - }; - if (signal.aborted) { - abort(); - return; - } - signal.addEventListener('abort', abort); - cleanupSignal = () => { - signal.removeEventListener('abort', abort); - cleanupSignal = undefined; - }; - } + const abort = () => { + reject(new AbortPromptError({ cause: signal.reason })); + cleanup(); + }; + signal.addEventListener('abort', abort); + cleanupSignal = () => { + signal.removeEventListener('abort', abort); + cleanupSignal = undefined; + }; + activePrompt.run().then(resolve, reject).finally(cleanup); }) : prompt; + let cleanupModuleSignal: (() => void) | undefined; const { signal: moduleSignal } = this.opt; if (moduleSignal?.aborted) { this.abortController.abort(moduleSignal.reason); } else if (moduleSignal) { - const abort = (reason: unknown) => this.abortController?.abort(reason); + const abort = () => this.abortController?.abort(moduleSignal.reason); moduleSignal.addEventListener('abort', abort); - cleanupSignal = () => { + cleanupModuleSignal = () => { moduleSignal.removeEventListener('abort', abort); }; } @@ -369,18 +376,10 @@ export default class PromptsRunner { })) .finally(() => { cleanupSignal?.(); + cleanupModuleSignal?.(); }); }; - /** - * Handle the ^C exit - */ - private onForceClose = () => { - this.close(); - process.kill(process.pid, 'SIGINT'); - console.log(''); - }; - /** * Close the interface and cleanup listeners */