Skip to content

Commit c1d8ab1

Browse files
authored
fix: single answer questions dont get stored with metadata (#633)
### Summary If a question has a single answer on `create-react-native-library`, we skip that question. However, due to a bug, single answer questions weren't making it to the metadata stored with new project. This PR fixes that ### Test plan A good example is the `Fabric view with backward compat`. This option had a single language setting `kotlin and objective c`. To test this PR, you can create a new project with these options and make sure the language is getting stored properly.
1 parent 99128be commit c1d8ab1

File tree

1 file changed

+96
-91
lines changed
  • packages/create-react-native-library/src

1 file changed

+96
-91
lines changed

packages/create-react-native-library/src/index.ts

Lines changed: 96 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,11 @@ const TYPE_CHOICES: {
214214
},
215215
];
216216

217+
type Question = Omit<PromptObject<keyof Answers>, 'validate' | 'name'> & {
218+
validate?: (value: string) => boolean | string;
219+
name: keyof Answers;
220+
};
221+
217222
const args: Record<ArgName, yargs.Options> = {
218223
'slug': {
219224
description: 'Name of the npm package',
@@ -364,10 +369,7 @@ async function create(_argv: yargs.Arguments<any>) {
364369

365370
const basename = path.basename(folder);
366371

367-
const questions: (Omit<PromptObject<keyof Answers>, 'validate' | 'name'> & {
368-
validate?: (value: string) => boolean | string;
369-
name: string;
370-
})[] = [
372+
const questions: Question[] = [
371373
{
372374
type: 'text',
373375
name: 'slug',
@@ -476,108 +478,58 @@ async function create(_argv: yargs.Arguments<any>) {
476478
});
477479
}
478480

479-
const validate = (answers: Answers) => {
480-
for (const [key, value] of Object.entries(answers)) {
481-
if (value == null) {
482-
continue;
483-
}
481+
assertOptions(questions, argv);
484482

485-
const question = questions.find((q) => q.name === key);
483+
const singleChoiceAnswers: Partial<Answers> = {};
484+
const finalQuestions: Question[] = [];
486485

487-
if (question == null) {
488-
continue;
489-
}
486+
for (const question of questions) {
487+
// Skip questions which are passed as parameter and pass validation
488+
if (
489+
argv[question.name] != null &&
490+
question.validate?.(argv[question.name]) !== false
491+
) {
492+
continue;
493+
}
490494

491-
let valid = question.validate ? question.validate(String(value)) : true;
492-
493-
// We also need to guard against invalid choices
494-
// If we don't already have a validation message to provide a better error
495-
if (typeof valid !== 'string' && 'choices' in question) {
496-
const choices =
497-
typeof question.choices === 'function'
498-
? question.choices(
499-
undefined,
500-
// @ts-expect-error: it complains about optional values, but it should be fine
501-
answers,
502-
question
503-
)
504-
: question.choices;
505-
506-
if (choices && !choices.some((choice) => choice.value === value)) {
507-
valid = `Supported values are - ${choices.map((c) =>
508-
kleur.green(c.value)
509-
)}`;
510-
}
511-
}
495+
// Don't prompt questions with a single choice
496+
if (Array.isArray(question.choices) && question.choices.length === 1) {
497+
const onlyChoice = question.choices[0]!;
498+
singleChoiceAnswers[question.name] = onlyChoice.value;
512499

513-
if (valid !== true) {
514-
let message = `Invalid value ${kleur.red(
515-
String(value)
516-
)} passed for ${kleur.blue(key)}`;
500+
continue;
501+
}
517502

518-
if (typeof valid === 'string') {
519-
message += `: ${valid}`;
520-
}
503+
const { type, choices } = question;
521504

522-
console.log(message);
505+
// Don't prompt dynamic questions with a single choice
506+
if (type === 'select' && typeof choices === 'function') {
507+
question.type = (prev, values, prompt) => {
508+
const dynamicChoices = choices(prev, { ...argv, ...values }, prompt);
523509

524-
process.exit(1);
525-
}
510+
if (dynamicChoices && dynamicChoices.length === 1) {
511+
const onlyChoice = dynamicChoices[0]!;
512+
singleChoiceAnswers[question.name] = onlyChoice.value;
513+
return null;
514+
}
515+
516+
return type;
517+
};
526518
}
527-
};
528519

529-
// Validate arguments passed to the CLI
530-
validate(argv);
520+
finalQuestions.push(question);
521+
}
522+
523+
const promptAnswers = await prompts(finalQuestions);
531524

532525
const answers = {
533526
...argv,
534527
local,
535-
...(await prompts(
536-
questions
537-
.filter((question) => {
538-
// Skip questions which are passed as parameter and pass validation
539-
if (
540-
argv[question.name] != null &&
541-
question.validate?.(argv[question.name]) !== false
542-
) {
543-
return false;
544-
}
545-
546-
// Skip questions with a single choice
547-
if (
548-
Array.isArray(question.choices) &&
549-
question.choices.length === 1
550-
) {
551-
return false;
552-
}
553-
554-
return true;
555-
})
556-
.map((question) => {
557-
const { type, choices } = question;
558-
559-
// Skip dynamic questions with a single choice
560-
if (type === 'select' && typeof choices === 'function') {
561-
return {
562-
...question,
563-
type: (prev, values, prompt) => {
564-
const result = choices(prev, { ...argv, ...values }, prompt);
565-
566-
if (result && result.length === 1) {
567-
return null;
568-
}
569-
570-
return type;
571-
},
572-
};
573-
}
574-
575-
return question;
576-
})
577-
)),
528+
...singleChoiceAnswers,
529+
...promptAnswers,
578530
} as Answers;
579531

580-
validate(answers);
532+
assertOptions(questions, answers);
581533

582534
const {
583535
slug,
@@ -1000,3 +952,56 @@ yargs
1000952
'strip-dashed': true,
1001953
})
1002954
.strict().argv;
955+
956+
/**
957+
* Makes sure the answers are in expected form and ends the process with error if they are not
958+
*/
959+
export function assertOptions(questions: Question[], answers: Answers) {
960+
for (const [key, value] of Object.entries(answers)) {
961+
if (value == null) {
962+
continue;
963+
}
964+
965+
const question = questions.find((q) => q.name === key);
966+
967+
if (question == null) {
968+
continue;
969+
}
970+
971+
let valid = question.validate ? question.validate(String(value)) : true;
972+
973+
// We also need to guard against invalid choices
974+
// If we don't already have a validation message to provide a better error
975+
if (typeof valid !== 'string' && 'choices' in question) {
976+
const choices =
977+
typeof question.choices === 'function'
978+
? question.choices(
979+
undefined,
980+
// @ts-expect-error: it complains about optional values, but it should be fine
981+
answers,
982+
question
983+
)
984+
: question.choices;
985+
986+
if (choices && !choices.some((choice) => choice.value === value)) {
987+
valid = `Supported values are - ${choices.map((c) =>
988+
kleur.green(c.value)
989+
)}`;
990+
}
991+
}
992+
993+
if (valid !== true) {
994+
let message = `Invalid value ${kleur.red(
995+
String(value)
996+
)} passed for ${kleur.blue(key)}`;
997+
998+
if (typeof valid === 'string') {
999+
message += `: ${valid}`;
1000+
}
1001+
1002+
console.log(message);
1003+
1004+
process.exit(1);
1005+
}
1006+
}
1007+
}

0 commit comments

Comments
 (0)