import prompts, { PromptObject, Answers, Choice } from 'prompts'; export interface ExtendedPromptObject extends PromptObject { before?: ( answers: Answers, context: C, ) => Promise | any; after?: ( answers: Answers, context: C, ) => Promise | any; next?: ( answers: Answers, context: C, ) => Array>; getDynamicChoices?: ( answers: Answers, context: C, ) => Promise>; repeatUntil?: (answers: Answers) => boolean; warn?: string; } // Currently `prompts` package evaluates all values with function type 👿. // So we don't want to pass extended values. const promptifyQuestion = ( question: ExtendedPromptObject, ): PromptObject => { const promptifiedQuestion = { ...question }; delete promptifiedQuestion.before; delete promptifiedQuestion.after; delete promptifiedQuestion.next; delete promptifiedQuestion.repeatUntil; delete promptifiedQuestion.getDynamicChoices; return promptifiedQuestion; }; async function run( questions: Array>, context: T, prevAnswers: Answers = {}, ): Promise> { let answers: Answers = prevAnswers; const onCancel = () => { throw new Error('Aborted'); }; for (const question of questions) { if (question.before) { const beforeResult = await question.before(answers, context); if (beforeResult) { answers = { ...answers, ...beforeResult }; } } if (question.getDynamicChoices) { question.choices = await question.getDynamicChoices(answers, context); } const answer = await prompts([promptifyQuestion(question)], { onCancel, }); answers = { ...answers, ...answer }; if (question.after) { const afterResult = await question.after(answers, context); answers = { ...answers, ...afterResult }; } if (question.next) { const nextAnswers = await run( question.next(answers, context), context, answers, ); answers = { ...answers, ...nextAnswers }; } if (question.repeatUntil && !question.repeatUntil(answers)) { const nextAnswers = await run([question], context, answers); answers = { ...answers, ...nextAnswers }; } } return answers; } export default run; export { Answers } from 'prompts';