@@ -10,6 +10,7 @@ import githubUsername from 'github-username';
10
10
import prompts , { type PromptObject } from './utils/prompts' ;
11
11
import generateExampleApp from './utils/generateExampleApp' ;
12
12
import { spawn } from './utils/spawn' ;
13
+ import { version } from '../package.json' ;
13
14
14
15
const FALLBACK_BOB_VERSION = '0.20.0' ;
15
16
@@ -107,6 +108,7 @@ type ProjectType =
107
108
| 'library' ;
108
109
109
110
type Answers = {
111
+ name : string ;
110
112
slug : string ;
111
113
description : string ;
112
114
authorName : string ;
@@ -117,6 +119,7 @@ type Answers = {
117
119
type ?: ProjectType ;
118
120
example ?: boolean ;
119
121
reactNativeVersion ?: string ;
122
+ local ?: boolean ;
120
123
} ;
121
124
122
125
const LANGUAGE_CHOICES : {
@@ -265,7 +268,10 @@ const args: Record<ArgName, yargs.Options> = {
265
268
266
269
// FIXME: fix the type
267
270
// eslint-disable-next-line @typescript-eslint/no-explicit-any
268
- async function create ( argv : yargs . Arguments < any > ) {
271
+ async function create ( _argv : yargs . Arguments < any > ) {
272
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
273
+ const { _, $0, ...argv } = _argv ;
274
+
269
275
let local = false ;
270
276
271
277
if ( typeof argv . local === 'boolean' ) {
@@ -355,13 +361,11 @@ async function create(argv: yargs.Arguments<any>) {
355
361
356
362
const basename = path . basename ( folder ) ;
357
363
358
- const questions : Record <
359
- ArgName ,
360
- Omit < PromptObject < keyof Answers > , 'validate' > & {
361
- validate ?: ( value : string ) => boolean | string ;
362
- }
363
- > = {
364
- 'slug' : {
364
+ const questions : ( Omit < PromptObject < keyof Answers > , 'validate' | 'name' > & {
365
+ validate ?: ( value : string ) => boolean | string ;
366
+ name : string ;
367
+ } ) [ ] = [
368
+ {
365
369
type : 'text' ,
366
370
name : 'slug' ,
367
371
message : 'What is the name of the npm package?' ,
@@ -374,28 +378,28 @@ async function create(argv: yargs.Arguments<any>) {
374
378
validateNpmPackage ( input ) . validForNewPackages ||
375
379
'Must be a valid npm package name' ,
376
380
} ,
377
- 'description' : {
381
+ {
378
382
type : 'text' ,
379
383
name : 'description' ,
380
384
message : 'What is the description for the package?' ,
381
385
validate : ( input ) => Boolean ( input ) || 'Cannot be empty' ,
382
386
} ,
383
- 'author-name' : {
387
+ {
384
388
type : local ? null : 'text' ,
385
389
name : 'authorName' ,
386
390
message : 'What is the name of package author?' ,
387
391
initial : name ,
388
392
validate : ( input ) => Boolean ( input ) || 'Cannot be empty' ,
389
393
} ,
390
- 'author-email' : {
394
+ {
391
395
type : local ? null : 'text' ,
392
396
name : 'authorEmail' ,
393
397
message : 'What is the email address for the package author?' ,
394
398
initial : email ,
395
399
validate : ( input ) =>
396
400
/ ^ \S + @ \S + $ / . test ( input ) || 'Must be a valid email address' ,
397
401
} ,
398
- 'author-url' : {
402
+ {
399
403
type : local ? null : 'text' ,
400
404
name : 'authorUrl' ,
401
405
message : 'What is the URL for the package author?' ,
@@ -413,7 +417,7 @@ async function create(argv: yargs.Arguments<any>) {
413
417
} ,
414
418
validate : ( input ) => / ^ h t t p s ? : \/ \/ / . test ( input ) || 'Must be a valid URL' ,
415
419
} ,
416
- 'repo-url' : {
420
+ {
417
421
type : local ? null : 'text' ,
418
422
name : 'repoUrl' ,
419
423
message : 'What is the URL for the repository?' ,
@@ -428,13 +432,13 @@ async function create(argv: yargs.Arguments<any>) {
428
432
} ,
429
433
validate : ( input ) => / ^ h t t p s ? : \/ \/ / . test ( input ) || 'Must be a valid URL' ,
430
434
} ,
431
- 'type' : {
435
+ {
432
436
type : 'select' ,
433
437
name : 'type' ,
434
438
message : 'What type of library do you want to develop?' ,
435
439
choices : TYPE_CHOICES ,
436
440
} ,
437
- 'languages' : {
441
+ {
438
442
type : 'select' ,
439
443
name : 'languages' ,
440
444
message : 'Which languages do you want to use?' ,
@@ -448,15 +452,15 @@ async function create(argv: yargs.Arguments<any>) {
448
452
} ) ;
449
453
} ,
450
454
} ,
451
- } ;
455
+ ] ;
452
456
453
457
// Validate arguments passed to the CLI
454
458
for ( const [ key , value ] of Object . entries ( argv ) ) {
455
459
if ( value == null ) {
456
460
continue ;
457
461
}
458
462
459
- const question = questions [ key as ArgName ] ;
463
+ const question = questions . find ( ( q ) => q . name === key ) ;
460
464
461
465
if ( question == null ) {
462
466
continue ;
@@ -494,49 +498,37 @@ async function create(argv: yargs.Arguments<any>) {
494
498
}
495
499
}
496
500
497
- const {
498
- slug,
499
- description,
500
- authorName,
501
- authorEmail,
502
- authorUrl,
503
- repoUrl,
504
- type = 'module-mixed' ,
505
- languages = type === 'library' ? 'js' : 'java-objc' ,
506
- example : hasExample ,
507
- reactNativeVersion,
508
- } = {
501
+ const answers = {
509
502
...argv ,
503
+ local,
510
504
...( await prompts (
511
- Object . entries ( questions )
512
- . filter ( ( [ k , v ] ) => {
513
- // Skip 'with-recommended-options' question if type of language is passed
505
+ questions
506
+ . filter ( ( question ) => {
507
+ // Skip questions which are passed as parameter and pass validation
514
508
if (
515
- k === 'with-recommended-options' &&
516
- ( argv . languages || argv . type )
509
+ argv [ question . name ] != null &&
510
+ question . validate ?. ( argv [ question . name ] ) !== false
517
511
) {
518
512
return false ;
519
513
}
520
514
521
- // Skip questions which are passed as parameter and pass validation
522
- if ( argv [ k ] != null && v . validate ?.( argv [ k ] ) !== false ) {
523
- return false ;
524
- }
525
-
526
515
// Skip questions with a single choice
527
- if ( Array . isArray ( v . choices ) && v . choices . length === 1 ) {
516
+ if (
517
+ Array . isArray ( question . choices ) &&
518
+ question . choices . length === 1
519
+ ) {
528
520
return false ;
529
521
}
530
522
531
523
return true ;
532
524
} )
533
- . map ( ( [ , v ] ) => {
534
- const { type, choices } = v ;
525
+ . map ( ( question ) => {
526
+ const { type, choices } = question ;
535
527
536
528
// Skip dynamic questions with a single choice
537
529
if ( type === 'select' && typeof choices === 'function' ) {
538
530
return {
539
- ...v ,
531
+ ...question ,
540
532
type : ( prev , values , prompt ) => {
541
533
const result = choices ( prev , { ...argv , ...values } , prompt ) ;
542
534
@@ -549,24 +541,37 @@ async function create(argv: yargs.Arguments<any>) {
549
541
} ;
550
542
}
551
543
552
- return v ;
544
+ return question ;
553
545
} )
554
546
) ) ,
555
547
} as Answers ;
556
548
549
+ const {
550
+ slug,
551
+ description,
552
+ authorName,
553
+ authorEmail,
554
+ authorUrl,
555
+ repoUrl,
556
+ type = 'module-mixed' ,
557
+ languages = type === 'library' ? 'js' : 'java-objc' ,
558
+ example : hasExample ,
559
+ reactNativeVersion,
560
+ } = answers ;
561
+
557
562
// Get latest version of Bob from NPM
558
- let version : string ;
563
+ let bobVersion : string ;
559
564
560
565
try {
561
- version = await Promise . race ( [
566
+ bobVersion = await Promise . race ( [
562
567
new Promise < string > ( ( resolve ) => {
563
568
setTimeout ( ( ) => resolve ( FALLBACK_BOB_VERSION ) , 1000 ) ;
564
569
} ) ,
565
570
spawn ( 'npm' , [ 'view' , 'react-native-builder-bob' , 'dist-tags.latest' ] ) ,
566
571
] ) ;
567
572
} catch ( e ) {
568
573
// Fallback to a known version if we couldn't fetch
569
- version = FALLBACK_BOB_VERSION ;
574
+ bobVersion = FALLBACK_BOB_VERSION ;
570
575
}
571
576
572
577
const moduleType = type . startsWith ( 'view-' ) ? 'view' : 'module' ;
@@ -598,7 +603,7 @@ async function create(argv: yargs.Arguments<any>) {
598
603
599
604
const options = {
600
605
bob : {
601
- version : version || FALLBACK_BOB_VERSION ,
606
+ version : bobVersion || FALLBACK_BOB_VERSION ,
602
607
} ,
603
608
project : {
604
609
slug,
@@ -795,6 +800,40 @@ async function create(argv: yargs.Arguments<any>) {
795
800
}
796
801
}
797
802
803
+ // Some of the passed args can already be derived from the generated package.json file.
804
+ const ignoredAnswers : ( keyof Answers ) [ ] = [
805
+ 'name' ,
806
+ 'slug' ,
807
+ 'description' ,
808
+ 'authorName' ,
809
+ 'authorEmail' ,
810
+ 'authorUrl' ,
811
+ 'repoUrl' ,
812
+ 'example' ,
813
+ 'reactNativeVersion' ,
814
+ 'local' ,
815
+ ] ;
816
+
817
+ type AnswerEntries < T extends keyof Answers = keyof Answers > = [
818
+ T ,
819
+ Answers [ T ] ,
820
+ ] [ ] ;
821
+
822
+ const libraryMetadata = Object . fromEntries (
823
+ ( Object . entries ( answers ) as AnswerEntries ) . filter (
824
+ ( [ answer ] ) => ! ignoredAnswers . includes ( answer )
825
+ )
826
+ ) ;
827
+ libraryMetadata . version = version ;
828
+
829
+ const libraryPackageJson = await fs . readJson (
830
+ path . join ( folder , 'package.json' )
831
+ ) ;
832
+ libraryPackageJson [ 'create-react-native-library' ] = libraryMetadata ;
833
+ await fs . writeJson ( path . join ( folder , 'package.json' ) , libraryPackageJson , {
834
+ spaces : 2 ,
835
+ } ) ;
836
+
798
837
spinner . succeed (
799
838
`Project created successfully at ${ kleur . yellow (
800
839
path . relative ( process . cwd ( ) , folder )
@@ -916,4 +955,8 @@ yargs
916
955
917
956
process . exit ( 1 ) ;
918
957
} )
958
+ . parserConfiguration ( {
959
+ // don't pass kebab-case args to handler.
960
+ 'strip-dashed' : true ,
961
+ } )
919
962
. strict ( ) . argv ;
0 commit comments