@@ -214,6 +214,11 @@ const TYPE_CHOICES: {
214
214
} ,
215
215
] ;
216
216
217
+ type Question = Omit < PromptObject < keyof Answers > , 'validate' | 'name' > & {
218
+ validate ?: ( value : string ) => boolean | string ;
219
+ name : keyof Answers ;
220
+ } ;
221
+
217
222
const args : Record < ArgName , yargs . Options > = {
218
223
'slug' : {
219
224
description : 'Name of the npm package' ,
@@ -364,10 +369,7 @@ async function create(_argv: yargs.Arguments<any>) {
364
369
365
370
const basename = path . basename ( folder ) ;
366
371
367
- const questions : ( Omit < PromptObject < keyof Answers > , 'validate' | 'name' > & {
368
- validate ?: ( value : string ) => boolean | string ;
369
- name : string ;
370
- } ) [ ] = [
372
+ const questions : Question [ ] = [
371
373
{
372
374
type : 'text' ,
373
375
name : 'slug' ,
@@ -476,108 +478,58 @@ async function create(_argv: yargs.Arguments<any>) {
476
478
} ) ;
477
479
}
478
480
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 ) ;
484
482
485
- const question = questions . find ( ( q ) => q . name === key ) ;
483
+ const singleChoiceAnswers : Partial < Answers > = { } ;
484
+ const finalQuestions : Question [ ] = [ ] ;
486
485
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
+ }
490
494
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 ;
512
499
513
- if ( valid !== true ) {
514
- let message = `Invalid value ${ kleur . red (
515
- String ( value )
516
- ) } passed for ${ kleur . blue ( key ) } `;
500
+ continue ;
501
+ }
517
502
518
- if ( typeof valid === 'string' ) {
519
- message += `: ${ valid } ` ;
520
- }
503
+ const { type, choices } = question ;
521
504
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 ) ;
523
509
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
+ } ;
526
518
}
527
- } ;
528
519
529
- // Validate arguments passed to the CLI
530
- validate ( argv ) ;
520
+ finalQuestions . push ( question ) ;
521
+ }
522
+
523
+ const promptAnswers = await prompts ( finalQuestions ) ;
531
524
532
525
const answers = {
533
526
...argv ,
534
527
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 ,
578
530
} as Answers ;
579
531
580
- validate ( answers ) ;
532
+ assertOptions ( questions , answers ) ;
581
533
582
534
const {
583
535
slug,
@@ -1000,3 +952,56 @@ yargs
1000
952
'strip-dashed' : true ,
1001
953
} )
1002
954
. 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