Skip to content

Commit

Permalink
drop signal from module argument
Browse files Browse the repository at this point in the history
  • Loading branch information
mshima committed Sep 3, 2024
1 parent 57ce2cf commit af12865
Show file tree
Hide file tree
Showing 3 changed files with 21 additions and 65 deletions.
35 changes: 2 additions & 33 deletions packages/inquirer/inquirer.test.mts
Original file line number Diff line number Diff line change
Expand Up @@ -783,7 +783,7 @@ describe('AbortSignal support', () => {
await expect(promise).rejects.toThrow(AbortPromptError);
});

it('legacy prompts can be aborted', async () => {
it('legacy prompts can be aborted by module signal', async () => {
const abortController = new AbortController();
const localPrompt = inquirer.createPromptModule<TestQuestions>({
signal: abortController.signal,
Expand All @@ -795,21 +795,7 @@ describe('AbortSignal support', () => {
await expect(promise).rejects.toThrow(AbortPromptError);
});

it('legacy prompts can be aborted passing signal', async () => {
const abortController = new AbortController();
const localPrompt = inquirer.createPromptModule<TestQuestions>();
localPrompt.registerPrompt('stub', StubEventualyFailingPrompt);

const promise = localPrompt(
{ type: 'stub', name: 'q1', message: 'message' },
{},
{ signal: abortController.signal },
);
abortController.abort();
await expect(promise).rejects.toThrow(AbortPromptError);
});

it('modern prompts can be aborted', async () => {
it('modern prompts can be aborted by module signal', async () => {
const abortController = new AbortController();
const localPrompt = inquirer.createPromptModule<TestQuestions>({
signal: abortController.signal,
Expand All @@ -835,23 +821,6 @@ describe('AbortSignal support', () => {
promise.ui.close();
await expect(promise).rejects.toThrow(AbortPromptError);
});

it('modern prompts can be aborted passing signals', async () => {
const abortController = new AbortController();
const localPrompt = inquirer.createPromptModule<TestQuestions>();
localPrompt.registerPrompt(
'stub',
createPrompt(() => 'dummy prompt'),
);

const promise = localPrompt(
{ type: 'stub', name: 'q1', message: 'message' },
{},
{ signal: abortController.signal },
);
abortController.abort();
await expect(promise).rejects.toThrow(AbortPromptError);
});
});

describe('Non-TTY checks', () => {
Expand Down
13 changes: 4 additions & 9 deletions packages/inquirer/src/index.mts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import { Observable } from 'rxjs';

export type { QuestionMap } from './types.mjs';

const defaultPrompts: PromptCollection = {
const builtInPrompts: PromptCollection = {
input,
select,
/** @deprecated `list` is now named `select` */
Expand Down Expand Up @@ -70,7 +70,6 @@ export function createPromptModule<
>(
questions: NamedQuestion<Prettify<PrefilledAnswers & A>>[],
answers?: PrefilledAnswers,
opts?: { signal?: AbortSignal },
): PromptReturnType<Prettify<PrefilledAnswers & A>>;
function promptModule<
const A extends Answers,
Expand All @@ -80,36 +79,32 @@ export function createPromptModule<
[name in keyof A]: Question<Prettify<PrefilledAnswers & A>>;
},
answers?: PrefilledAnswers,
opts?: { signal?: AbortSignal },
): PromptReturnType<Prettify<PrefilledAnswers & Answers<Extract<keyof A, string>>>>;
function promptModule<
const A extends Answers,
PrefilledAnswers extends Answers = object,
>(
questions: Observable<NamedQuestion<Prettify<PrefilledAnswers & A>>>,
answers?: PrefilledAnswers,
opts?: { signal?: AbortSignal },
): PromptReturnType<Prettify<PrefilledAnswers & A>>;
function promptModule<
const A extends Answers,
PrefilledAnswers extends Answers = object,
>(
questions: NamedQuestion<A & PrefilledAnswers>,
answers?: PrefilledAnswers,
opts?: { signal?: AbortSignal },
): PromptReturnType<PrefilledAnswers & A>;
function promptModule<A extends Answers>(
questions: PromptSession<A>,
answers?: Partial<A>,
opts: { signal?: AbortSignal } = {},
): PromptReturnType<A> {
const runner = new PromptsRunner<A>(promptModule.prompts, opt);

const promptPromise = runner.run(questions, answers, opts);
const promptPromise = runner.run(questions, answers);
return Object.assign(promptPromise, { ui: runner });
}

promptModule.prompts = { ...defaultPrompts };
promptModule.prompts = { ...builtInPrompts };

/**
* Register a prompt type
Expand All @@ -126,7 +121,7 @@ export function createPromptModule<
* Register the defaults provider prompts
*/
promptModule.restoreDefaultPrompts = function () {
promptModule.prompts = { ...defaultPrompts };
promptModule.prompts = { ...builtInPrompts };
};

return promptModule;
Expand Down
38 changes: 15 additions & 23 deletions packages/inquirer/src/ui/prompt.mts
Original file line number Diff line number Diff line change
Expand Up @@ -198,11 +198,7 @@ export default class PromptsRunner<A extends Answers> {
this.prompts = prompts;
}

async run(
questions: PromptSession<A>,
answers?: Partial<A>,
opts: { signal?: AbortSignal } = {},
): Promise<A> {
async run(questions: PromptSession<A>, answers?: Partial<A>): Promise<A> {
// Keep global reference to the answers
this.answers = typeof answers === 'object' ? { ...answers } : {};

Expand All @@ -223,9 +219,7 @@ export default class PromptsRunner<A extends Answers> {
obs = from([questions]);
}

this.process = obs.pipe(
concatMap((question) => this.processQuestion(question, opts)),
);
this.process = obs.pipe(concatMap((question) => this.processQuestion(question)));

return lastValueFrom(
this.process.pipe(
Expand All @@ -239,7 +233,7 @@ export default class PromptsRunner<A extends Answers> {
.finally(() => this.close());
}

processQuestion(question: AnyQuestion<A>, opts: { signal?: AbortSignal } = {}) {
processQuestion(question: AnyQuestion<A>) {
question = { ...question };
return defer(() => {
const obs = of(question);
Expand Down Expand Up @@ -274,12 +268,12 @@ export default class PromptsRunner<A extends Answers> {

return of(question);
}),
concatMap((question) => this.fetchAnswer(question, opts)),
concatMap((question) => this.fetchAnswer(question)),
);
});
}

fetchAnswer(question: AnyQuestion<A>, opts: { signal?: AbortSignal } = {}) {
fetchAnswer(question: AnyQuestion<A>) {
const prompt = this.prompts[question.type];

if (prompt == null) {
Expand Down Expand Up @@ -337,30 +331,28 @@ export default class PromptsRunner<A extends Answers> {
})
: prompt;

const isSignal = (signal: AbortSignal | undefined): signal is AbortSignal =>
signal !== undefined;

const otherSignals = [opts.signal, this.opt.signal].filter(isSignal);
const abortedSignal = otherSignals.find((signal) => signal.aborted);
let signal: AbortSignal;
let cleanupSignal = () => {};
const { signal: moduleSignal } = this.opt;

if (abortedSignal) {
signal = AbortSignal.abort(abortedSignal.reason);
if (moduleSignal?.aborted) {
signal = AbortSignal.abort(moduleSignal.reason);
} else {
const abortController = new AbortController();
this.abortController = abortController;
signal = abortController.signal;
const abort = (reason: unknown) => this.abortController?.abort(reason);
otherSignals.forEach((signal) => signal.addEventListener('abort', abort));
cleanupSignal = () => {
otherSignals.forEach((signal) => signal.removeEventListener('abort', abort));
};
if (moduleSignal) {
moduleSignal.addEventListener('abort', abort);
cleanupSignal = () => {
moduleSignal.removeEventListener('abort', abort);
};
}
}

return defer(() =>
from(
promptFn(question, { ...this.opt, ...opts, signal })
promptFn(question, { ...this.opt, signal })
.then((answer: unknown) => ({ name: question.name, answer }))
.finally(() => {
cleanupSignal();
Expand Down

0 comments on commit af12865

Please sign in to comment.