Skip to content

Commit

Permalink
Fix(inquirer): Ensure no mutation of the question objects
Browse files Browse the repository at this point in the history
  • Loading branch information
SBoudrias committed Jul 17, 2024
1 parent 3272b94 commit 2cc9e31
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 32 deletions.
60 changes: 31 additions & 29 deletions packages/inquirer/src/types.mts
Original file line number Diff line number Diff line change
Expand Up @@ -41,43 +41,45 @@ export interface QuestionMap {
}

type PromptConfigMap<A extends Answers> = {
[key in keyof QuestionMap]: DistributiveMerge<
QuestionMap[keyof QuestionMap],
{
type: keyof QuestionMap;
name: KeyUnion<A>;
when?: AsyncGetterFunction<boolean, Prettify<A>> | boolean;
askAnswered?: boolean;
message:
| Pick<QuestionMap[keyof QuestionMap], 'message'>
| AsyncGetterFunction<
Pick<QuestionMap[keyof QuestionMap], 'message'>,
Prettify<A>
>;
choices?:
| Pick<QuestionMap[keyof QuestionMap], 'choices'>
| string[]
| AsyncGetterFunction<
Pick<QuestionMap[keyof QuestionMap], 'choices'> | string[],
Prettify<A>
>;
default?:
| Pick<QuestionMap[keyof QuestionMap], 'default'>
| AsyncGetterFunction<
Pick<QuestionMap[keyof QuestionMap], 'default'> | string[],
Prettify<A>
>;
}
[key in keyof QuestionMap]: Readonly<
DistributiveMerge<
QuestionMap[keyof QuestionMap],
{
type: keyof QuestionMap;
name: KeyUnion<A>;
when?: AsyncGetterFunction<boolean, Prettify<A>> | boolean;
askAnswered?: boolean;
message:
| Pick<QuestionMap[keyof QuestionMap], 'message'>
| AsyncGetterFunction<
Pick<QuestionMap[keyof QuestionMap], 'message'>,
Prettify<A>
>;
choices?:
| Pick<QuestionMap[keyof QuestionMap], 'choices'>
| string[]
| AsyncGetterFunction<
Pick<QuestionMap[keyof QuestionMap], 'choices'> | string[],
Prettify<A>
>;
default?:
| Pick<QuestionMap[keyof QuestionMap], 'default'>
| AsyncGetterFunction<
Pick<QuestionMap[keyof QuestionMap], 'default'> | string[],
Prettify<A>
>;
}
>
>;
};

export type Question<A extends Answers> = PromptConfigMap<A>[keyof PromptConfigMap<A>];

export type QuestionAnswerMap<A extends Answers> =
| { [name in KeyUnion<A>]: Omit<Question<A>, 'name'> }
| Readonly<{ [name in KeyUnion<A>]: Omit<Question<A>, 'name'> }>
| never;

export type QuestionArray<A extends Answers> = Question<A>[] | never;
export type QuestionArray<A extends Answers> = readonly Question<A>[] | never;

export type QuestionObservable<A extends Answers> = Observable<Question<A>> | never;

Expand Down
20 changes: 17 additions & 3 deletions packages/inquirer/src/ui/prompt.mts
Original file line number Diff line number Diff line change
Expand Up @@ -158,8 +158,22 @@ function setupReadlineOptions(opt: StreamOptions = {}) {
};
}

function isQuestionArray<A extends Answers>(
questions:
| QuestionArray<A>
| QuestionAnswerMap<A>
| QuestionObservable<A>
| Question<A>,
): questions is QuestionArray<A> {
return Array.isArray(questions);
}

function isQuestionMap<A extends Answers>(
questions: QuestionArray<A> | QuestionAnswerMap<A> | Question<A>,
questions:
| QuestionArray<A>
| QuestionAnswerMap<A>
| QuestionObservable<A>
| Question<A>,
): questions is QuestionAnswerMap<A> {
return Object.values(questions).every(
(maybeQuestion) =>
Expand Down Expand Up @@ -207,7 +221,7 @@ export default class PromptsRunner<A extends Answers> {
this.answers = typeof answers === 'object' ? { ...answers } : {};

let obs: Observable<Question<A>>;
if (Array.isArray(questions)) {
if (isQuestionArray(questions)) {
obs = from(questions);
} else if (isObservable(questions)) {
obs = questions;
Expand Down Expand Up @@ -366,7 +380,7 @@ export default class PromptsRunner<A extends Answers> {
setDefaultType = (question: Question<A>): Observable<Question<A>> => {
// Default type to input
if (!this.prompts[question.type]) {
question.type = 'input';
question = Object.assign({}, question, { type: 'input' });
}

return defer(() => of(question));
Expand Down

0 comments on commit 2cc9e31

Please sign in to comment.