@@ -19,6 +19,7 @@ const BINARIES = [
19
19
] ;
20
20
21
21
const COMMON_FILES = path . resolve ( __dirname , '../templates/common' ) ;
22
+ const COMMON_LOCAL_FILES = path . resolve ( __dirname , '../templates/common-local' ) ;
22
23
const JS_FILES = path . resolve ( __dirname , '../templates/js-library' ) ;
23
24
const EXPO_FILES = path . resolve ( __dirname , '../templates/expo-library' ) ;
24
25
const CPP_FILES = path . resolve ( __dirname , '../templates/cpp-library' ) ;
@@ -27,6 +28,10 @@ const NATIVE_COMMON_FILES = path.resolve(
27
28
__dirname ,
28
29
'../templates/native-common'
29
30
) ;
31
+ const NATIVE_COMMON_EXAMPLE_FILES = path . resolve (
32
+ __dirname ,
33
+ '../templates/native-common-example'
34
+ ) ;
30
35
31
36
const NATIVE_FILES = {
32
37
module_legacy : path . resolve ( __dirname , '../templates/native-library-legacy' ) ,
@@ -82,6 +87,8 @@ type ArgName =
82
87
| 'repo-url'
83
88
| 'languages'
84
89
| 'type'
90
+ | 'local'
91
+ | 'example'
85
92
| 'react-native-version' ;
86
93
87
94
type ProjectLanguages =
@@ -110,6 +117,7 @@ type Answers = {
110
117
repoUrl : string ;
111
118
languages : ProjectLanguages ;
112
119
type ?: ProjectType ;
120
+ example ?: boolean ;
113
121
reactNativeVersion ?: string ;
114
122
} ;
115
123
@@ -246,10 +254,60 @@ const args: Record<ArgName, yargs.Options> = {
246
254
description : 'Version of React Native to use, uses latest if not specified' ,
247
255
type : 'string' ,
248
256
} ,
257
+ 'local' : {
258
+ description : 'Whether to create a local library' ,
259
+ type : 'boolean' ,
260
+ } ,
261
+ 'example' : {
262
+ description : 'Whether to create a an example app' ,
263
+ type : 'boolean' ,
264
+ } ,
249
265
} ;
250
266
251
267
async function create ( argv : yargs . Arguments < any > ) {
252
- const folder = path . join ( process . cwd ( ) , argv . name ) ;
268
+ let local = false ;
269
+ let folder = path . join ( process . cwd ( ) , argv . name ) ;
270
+
271
+ if ( typeof argv . local === 'boolean' ) {
272
+ local = argv . local ;
273
+ } else {
274
+ let hasPackageJson = await fs . pathExists (
275
+ path . join ( process . cwd ( ) , 'package.json' )
276
+ ) ;
277
+
278
+ if ( hasPackageJson ) {
279
+ // If we're under a project with package.json, ask the user if they want to create a local library
280
+ const answers = await prompts ( [
281
+ {
282
+ type : 'confirm' ,
283
+ name : 'local' ,
284
+ message : `Looks like you're under a project folder. Do you want to create a local library?` ,
285
+ initial : true ,
286
+ } ,
287
+ {
288
+ type : ( previous : boolean ) => {
289
+ if ( previous ) {
290
+ return 'text' ;
291
+ }
292
+
293
+ return null ;
294
+ } ,
295
+ name : 'folder' ,
296
+ message : `Where to create the local library?` ,
297
+ initial : argv . name . includes ( '/' )
298
+ ? argv . name
299
+ : `packages/${ argv . name } ` ,
300
+ validate : ( input ) => Boolean ( input ) || 'Cannot be empty' ,
301
+ } ,
302
+ ] ) ;
303
+
304
+ local = answers . local ;
305
+
306
+ if ( local ) {
307
+ folder = path . join ( process . cwd ( ) , answers . folder ) ;
308
+ }
309
+ }
310
+ }
253
311
254
312
if ( await fs . pathExists ( folder ) ) {
255
313
console . log (
@@ -315,22 +373,22 @@ async function create(argv: yargs.Arguments<any>) {
315
373
validate : ( input ) => Boolean ( input ) || 'Cannot be empty' ,
316
374
} ,
317
375
'author-name' : {
318
- type : 'text' ,
376
+ type : local ? null : 'text' ,
319
377
name : 'authorName' ,
320
378
message : 'What is the name of package author?' ,
321
379
initial : name ,
322
380
validate : ( input ) => Boolean ( input ) || 'Cannot be empty' ,
323
381
} ,
324
382
'author-email' : {
325
- type : 'text' ,
383
+ type : local ? null : 'text' ,
326
384
name : 'authorEmail' ,
327
385
message : 'What is the email address for the package author?' ,
328
386
initial : email ,
329
387
validate : ( input ) =>
330
388
/ ^ \S + @ \S + $ / . test ( input ) || 'Must be a valid email address' ,
331
389
} ,
332
390
'author-url' : {
333
- type : 'text' ,
391
+ type : local ? null : 'text' ,
334
392
name : 'authorUrl' ,
335
393
message : 'What is the URL for the package author?' ,
336
394
// @ts -ignore: this is supported, but types are wrong
@@ -348,7 +406,7 @@ async function create(argv: yargs.Arguments<any>) {
348
406
validate : ( input ) => / ^ h t t p s ? : \/ \/ / . test ( input ) || 'Must be a valid URL' ,
349
407
} ,
350
408
'repo-url' : {
351
- type : 'text' ,
409
+ type : local ? null : 'text' ,
352
410
name : 'repoUrl' ,
353
411
message : 'What is the URL for the repository?' ,
354
412
// @ts -ignore: this is supported, but types are wrong
@@ -438,6 +496,7 @@ async function create(argv: yargs.Arguments<any>) {
438
496
repoUrl,
439
497
type = 'module-mixed' ,
440
498
languages = type === 'library' ? 'js' : 'java-objc' ,
499
+ example = ! local ,
441
500
reactNativeVersion,
442
501
} = {
443
502
...argv ,
@@ -503,7 +562,7 @@ async function create(argv: yargs.Arguments<any>) {
503
562
? 'mixed'
504
563
: 'legacy' ;
505
564
506
- const example = type === 'library' ? 'expo' : 'native' ;
565
+ const exampleType = type === 'library' ? 'expo' : 'native' ;
507
566
const project = slug . replace ( / ^ ( r e a c t - n a t i v e - | @ [ ^ / ] + \/ ) / , '' ) ;
508
567
509
568
let namespace : string | undefined ;
@@ -553,7 +612,7 @@ async function create(argv: yargs.Arguments<any>) {
553
612
url : authorUrl ,
554
613
} ,
555
614
repo : repoUrl ,
556
- example,
615
+ example : exampleType ,
557
616
year : new Date ( ) . getFullYear ( ) ,
558
617
} ;
559
618
@@ -589,7 +648,7 @@ async function create(argv: yargs.Arguments<any>) {
589
648
await fs . mkdirp ( folder ) ;
590
649
591
650
if ( reactNativeVersion != null ) {
592
- if ( example === 'expo' ) {
651
+ if ( exampleType === 'expo' ) {
593
652
console . warn (
594
653
`${ kleur . yellow ( '⚠' ) } Ignoring --react-native-version for Expo example`
595
654
) ;
@@ -602,32 +661,46 @@ async function create(argv: yargs.Arguments<any>) {
602
661
}
603
662
}
604
663
605
- const spinner = ora ( 'Generating example' ) . start ( ) ;
664
+ const spinner = ora ( ) . start ( ) ;
606
665
607
- await generateExampleApp ( {
608
- type : example ,
609
- dest : folder ,
610
- slug : options . project . slug ,
611
- projectName : options . project . name ,
612
- arch,
613
- reactNativeVersion,
614
- } ) ;
666
+ if ( example ) {
667
+ spinner . text = 'Generating example app' ;
668
+
669
+ await generateExampleApp ( {
670
+ type : exampleType ,
671
+ dest : folder ,
672
+ slug : options . project . slug ,
673
+ projectName : options . project . name ,
674
+ arch,
675
+ reactNativeVersion,
676
+ } ) ;
677
+ }
615
678
616
679
spinner . text = 'Copying files' ;
617
680
618
- await copyDir ( COMMON_FILES , folder ) ;
681
+ if ( local ) {
682
+ await copyDir ( COMMON_LOCAL_FILES , folder ) ;
683
+ } else {
684
+ await copyDir ( COMMON_FILES , folder ) ;
685
+ }
619
686
620
687
if ( languages === 'js' ) {
621
688
await copyDir ( JS_FILES , folder ) ;
622
689
await copyDir ( EXPO_FILES , folder ) ;
623
690
} else {
624
- await copyDir (
625
- path . join ( EXAMPLE_FILES , 'example' ) ,
626
- path . join ( folder , 'example' )
627
- ) ;
691
+ if ( example ) {
692
+ await copyDir (
693
+ path . join ( EXAMPLE_FILES , 'example' ) ,
694
+ path . join ( folder , 'example' )
695
+ ) ;
696
+ }
628
697
629
698
await copyDir ( NATIVE_COMMON_FILES , folder ) ;
630
699
700
+ if ( example ) {
701
+ await copyDir ( NATIVE_COMMON_EXAMPLE_FILES , folder ) ;
702
+ }
703
+
631
704
if ( moduleType === 'module' ) {
632
705
await copyDir ( NATIVE_FILES [ `${ moduleType } _${ arch } ` ] , folder ) ;
633
706
} else {
@@ -664,44 +737,113 @@ async function create(argv: yargs.Arguments<any>) {
664
737
}
665
738
}
666
739
667
- // Set `react` and `react-native` versions of root `package.json` from example `package.json`
668
- const examplePackageJson = fs . readJSONSync (
669
- path . join ( folder , 'example' , 'package.json' )
670
- ) ;
671
- const rootPackageJson = fs . readJSONSync ( path . join ( folder , 'package.json' ) ) ;
672
- rootPackageJson . devDependencies . react = examplePackageJson . dependencies . react ;
673
- rootPackageJson . devDependencies [ 'react-native' ] =
674
- examplePackageJson . dependencies [ 'react-native' ] ;
740
+ if ( example ) {
741
+ // Set `react` and `react-native` versions of root `package.json` from example `package.json`
742
+ const examplePackageJson = await fs . readJSON (
743
+ path . join ( folder , 'example' , 'package.json' )
744
+ ) ;
745
+ const rootPackageJson = await fs . readJSON (
746
+ path . join ( folder , 'package.json' )
747
+ ) ;
675
748
676
- fs . writeJSONSync ( path . join ( folder , 'package.json' ) , rootPackageJson , {
677
- spaces : 2 ,
678
- } ) ;
749
+ rootPackageJson . devDependencies . react =
750
+ examplePackageJson . dependencies . react ;
751
+ rootPackageJson . devDependencies [ 'react-native' ] =
752
+ examplePackageJson . dependencies [ 'react-native' ] ;
679
753
680
- try {
681
- await spawn ( 'git' , [ 'init' ] , { cwd : folder } ) ;
682
- await spawn ( 'git' , [ 'branch' , '-M' , 'main' ] , { cwd : folder } ) ;
683
- await spawn ( 'git' , [ 'add' , '.' ] , { cwd : folder } ) ;
684
- await spawn ( 'git' , [ 'commit' , '-m' , 'chore: initial commit' ] , {
685
- cwd : folder ,
754
+ await fs . writeJSON ( path . join ( folder , 'package.json' ) , rootPackageJson , {
755
+ spaces : 2 ,
686
756
} ) ;
687
- } catch ( e ) {
688
- // Ignore error
757
+ }
758
+
759
+ if ( ! local ) {
760
+ try {
761
+ await spawn ( 'git' , [ 'init' ] , { cwd : folder } ) ;
762
+ await spawn ( 'git' , [ 'branch' , '-M' , 'main' ] , { cwd : folder } ) ;
763
+ await spawn ( 'git' , [ 'add' , '.' ] , { cwd : folder } ) ;
764
+ await spawn ( 'git' , [ 'commit' , '-m' , 'chore: initial commit' ] , {
765
+ cwd : folder ,
766
+ } ) ;
767
+ } catch ( e ) {
768
+ // Ignore error
769
+ }
689
770
}
690
771
691
772
spinner . succeed (
692
- `Project created successfully at ${ kleur . yellow ( argv . name ) } !\n`
773
+ `Project created successfully at ${ kleur . yellow (
774
+ path . relative ( process . cwd ( ) , folder )
775
+ ) } !\n`
693
776
) ;
694
777
695
- const platforms = {
696
- ios : { name : 'iOS' , color : 'cyan' } ,
697
- android : { name : 'Android' , color : 'green' } ,
698
- ...( example === 'expo'
699
- ? ( { web : { name : 'Web' , color : 'blue' } } as const )
700
- : null ) ,
701
- } as const ;
778
+ if ( local ) {
779
+ let linked ;
780
+
781
+ const packageManager = ( await fs . pathExists (
782
+ path . join ( process . cwd ( ) , 'yarn.lock' )
783
+ ) )
784
+ ? 'yarn'
785
+ : 'npm' ;
786
+
787
+ const packageJsonPath = path . join ( process . cwd ( ) , 'package.json' ) ;
788
+
789
+ if ( await fs . pathExists ( packageJsonPath ) ) {
790
+ const packageJson = await fs . readJSON ( packageJsonPath ) ;
791
+ const isReactNativeProject = Boolean (
792
+ packageJson . dependencies ?. [ 'react-native' ]
793
+ ) ;
794
+
795
+ if ( isReactNativeProject ) {
796
+ packageJson . dependencies = packageJson . dependencies || { } ;
797
+ packageJson . dependencies [ slug ] =
798
+ packageManager === 'yarn'
799
+ ? `link:./${ path . relative ( process . cwd ( ) , folder ) } `
800
+ : `file:./${ path . relative ( process . cwd ( ) , folder ) } ` ;
801
+
802
+ await fs . writeJSON ( packageJsonPath , packageJson , {
803
+ spaces : 2 ,
804
+ } ) ;
805
+
806
+ linked = true ;
807
+ }
808
+ }
809
+
810
+ console . log (
811
+ dedent ( `
812
+ ${ kleur . magenta (
813
+ `${ kleur . bold ( 'Get started' ) } with the project`
814
+ ) } ${ kleur . gray ( ':' ) }
815
+
816
+ ${
817
+ ( linked
818
+ ? `- Run ${ kleur . blue (
819
+ `${ packageManager } install`
820
+ ) } to link the library\n`
821
+ : `- Link the library at ${ kleur . blue (
822
+ path . relative ( process . cwd ( ) , folder )
823
+ ) } based on your project setup'\n`) +
824
+ `- Run ${ kleur . blue (
825
+ 'cd ios; pod install; cd -'
826
+ ) } to install dependencies with CocoaPods\n` +
827
+ `- Run ${ kleur . blue ( 'npx react-native run-android' ) } or ${ kleur . blue (
828
+ 'npx react-native run-ios'
829
+ ) } to build and run the app\n` +
830
+ `- Import from ${ kleur . blue ( slug ) } and use it in your app.`
831
+ }
702
832
703
- console . log (
704
- dedent ( `
833
+ ${ kleur . yellow ( `Good luck!` ) }
834
+ ` )
835
+ ) ;
836
+ } else {
837
+ const platforms = {
838
+ ios : { name : 'iOS' , color : 'cyan' } ,
839
+ android : { name : 'Android' , color : 'green' } ,
840
+ ...( exampleType === 'expo'
841
+ ? ( { web : { name : 'Web' , color : 'blue' } } as const )
842
+ : null ) ,
843
+ } as const ;
844
+
845
+ console . log (
846
+ dedent ( `
705
847
${ kleur . magenta (
706
848
`${ kleur . bold ( 'Get started' ) } with the project`
707
849
) } ${ kleur . gray ( ':' ) }
@@ -722,7 +864,8 @@ async function create(argv: yargs.Arguments<any>) {
722
864
`See ${ kleur . bold ( 'CONTRIBUTING.md' ) } for more details. Good luck!`
723
865
) }
724
866
` )
725
- ) ;
867
+ ) ;
868
+ }
726
869
}
727
870
// eslint-disable-next-line babel/no-unused-expressions
728
871
yargs
0 commit comments