1
- import {
2
- DEFAULT_COMPONENTS ,
3
- DEFAULT_LIB ,
4
- getConfig ,
5
- rawConfigSchema ,
6
- resolveConfigPaths ,
7
- } from '@/src/utils/get-config' ;
1
+ import { getConfig } from '@/src/utils/get-config' ;
8
2
import { handleError } from '@/src/utils/handle-error' ;
9
3
import { logger } from '@/src/utils/logger' ;
4
+ import { promptForConfig } from '@/src/utils/prompt-for-config' ;
10
5
import chalk from 'chalk' ;
11
6
import { execSync } from 'child_process' ;
12
7
import { Command } from 'commander' ;
@@ -22,11 +17,6 @@ import { z } from 'zod';
22
17
const filePath = fileURLToPath ( import . meta. url ) ;
23
18
const fileDir = path . dirname ( filePath ) ;
24
19
25
- const initOptionsSchema = z . object ( {
26
- cwd : z . string ( ) ,
27
- overwrite : z . boolean ( ) ,
28
- } ) ;
29
-
30
20
const REQUIRED_DEPENDENCIES = [
31
21
'nativewind' ,
32
22
'expo-navigation-bar' ,
@@ -52,68 +42,117 @@ const TEMPLATE_FILES = [
52
42
'lib/icons/iconWithClassName.ts' ,
53
43
] as const ;
54
44
55
- async function installDependencies ( cwd : string , spinner : Ora ) {
56
- try {
57
- spinner . text = 'Installing dependencies...' ;
58
- await execa ( 'npx' , [ 'expo' , 'install' , ...REQUIRED_DEPENDENCIES ] , {
59
- cwd,
60
- stdio : 'inherit' ,
45
+ const initOptionsSchema = z . object ( {
46
+ cwd : z . string ( ) ,
47
+ overwrite : z . boolean ( ) ,
48
+ } ) ;
49
+
50
+ export const init = new Command ( )
51
+ . name ( 'init' )
52
+ . description ( 'Initialize the required configuration for your React Native project' )
53
+ . option (
54
+ '-c, --cwd <cwd>' ,
55
+ 'the working directory. defaults to the current directory.' ,
56
+ process . cwd ( )
57
+ )
58
+ . option ( '-o, --overwrite' , 'overwrite existing files' , false )
59
+ . action ( async ( opts ) => {
60
+ try {
61
+ const options = initOptionsSchema . parse ( opts ) ;
62
+ const cwd = path . resolve ( options . cwd ) ;
63
+
64
+ await validateProjectDirectory ( cwd ) ;
65
+ await checkGitStatus ( cwd ) ;
66
+ await initializeProject ( cwd , options . overwrite ) ;
67
+ } catch ( error ) {
68
+ handleError ( error ) ;
69
+ }
70
+ } ) ;
71
+
72
+ async function validateProjectDirectory ( cwd : string ) {
73
+ if ( ! existsSync ( cwd ) ) {
74
+ logger . error ( `The path ${ cwd } does not exist. Please try again.` ) ;
75
+ process . exit ( 1 ) ;
76
+ }
77
+
78
+ if ( ! existsSync ( path . join ( cwd , 'package.json' ) ) ) {
79
+ logger . error (
80
+ 'No package.json found. Please run this command in a React Native project directory.'
81
+ ) ;
82
+ process . exit ( 1 ) ;
83
+ }
84
+ }
85
+
86
+ async function checkGitStatus ( cwd : string ) {
87
+ if ( await shouldPromptGitWarning ( cwd ) ) {
88
+ const { proceed } = await prompts ( {
89
+ type : 'confirm' ,
90
+ name : 'proceed' ,
91
+ message :
92
+ 'The Git repository is dirty (uncommitted changes). It is recommended to commit your changes before proceeding. Do you want to continue?' ,
93
+ initial : false ,
61
94
} ) ;
62
- spinner . text = 'Dependencies installed successfully' ;
95
+
96
+ if ( ! proceed ) {
97
+ logger . info ( 'Installation cancelled.' ) ;
98
+ process . exit ( 0 ) ;
99
+ }
100
+ }
101
+ }
102
+
103
+ async function shouldPromptGitWarning ( cwd : string ) : Promise < boolean > {
104
+ try {
105
+ execSync ( 'git rev-parse --is-inside-work-tree' , { cwd } ) ;
106
+ const status = execSync ( 'git status --porcelain' , { cwd } ) . toString ( ) ;
107
+ return ! ! status ;
63
108
} catch ( error ) {
64
- spinner . fail ( 'Failed to install dependencies' ) ;
65
- handleError ( error ) ;
66
- process . exit ( 1 ) ;
109
+ return false ;
67
110
}
68
111
}
69
112
70
- async function promptForConfig ( cwd : string ) {
71
- const highlight = ( text : string ) => chalk . cyan ( text ) ;
113
+ async function initializeProject ( cwd : string , overwrite : boolean ) {
114
+ const spinner = ora ( `Initializing project...` ) . start ( ) ;
72
115
73
116
try {
74
- const options = await prompts ( [
75
- {
76
- type : 'text' ,
77
- name : 'components' ,
78
- message : `Configure the import alias for ${ highlight ( 'components' ) } :` ,
79
- initial : DEFAULT_COMPONENTS ,
80
- } ,
81
- {
82
- type : 'text' ,
83
- name : 'lib' ,
84
- message : `Configure the import alias for ${ highlight ( 'lib' ) } :` ,
85
- initial : DEFAULT_LIB ,
86
- } ,
87
- ] ) ;
117
+ let config = await getConfig ( cwd ) ;
88
118
89
- const components = options . components || DEFAULT_COMPONENTS ;
90
- const lib = options . lib || DEFAULT_LIB ;
119
+ if ( ! config ) {
120
+ spinner . stop ( ) ;
121
+ config = await promptForConfig ( cwd ) ;
122
+ spinner . start ( ) ;
123
+ }
91
124
92
- const config = rawConfigSchema . parse ( {
93
- aliases : {
94
- components,
95
- lib,
96
- } ,
97
- } ) ;
125
+ const templatesDir = path . join ( fileDir , '../__generated/starter-base' ) ;
98
126
99
- const { proceed } = await prompts ( {
100
- type : 'confirm' ,
101
- name : 'proceed' ,
102
- message : `Write configuration to ${ highlight ( 'components.json' ) } . Proceed?` ,
103
- initial : true ,
104
- } ) ;
127
+ await installDependencies ( cwd , spinner ) ;
128
+ await updateTsConfig ( cwd , config , spinner ) ;
105
129
106
- if ( proceed ) {
107
- logger . info ( '' ) ;
108
- const spinner = ora ( `Writing components.json...` ) . start ( ) ;
109
- const targetPath = path . resolve ( cwd , 'components.json' ) ;
110
- await fs . writeFile ( targetPath , JSON . stringify ( config , null , 2 ) , 'utf8' ) ;
111
- spinner . succeed ( ) ;
130
+ spinner . text = 'Adding config and utility files...' ;
131
+ for ( const file of TEMPLATE_FILES ) {
132
+ await copyTemplateFile ( file , templatesDir , cwd , spinner , overwrite ) ;
112
133
}
113
134
114
- return await resolveConfigPaths ( cwd , config ) ;
135
+ await updateLayoutFile ( cwd , spinner ) ;
136
+
137
+ spinner . succeed ( 'Initialization completed successfully!' ) ;
115
138
} catch ( error ) {
116
- logger . error ( 'Failed to configure project.' ) ;
139
+ spinner . fail ( 'Initialization failed' ) ;
140
+ handleError ( error ) ;
141
+ process . exit ( 1 ) ;
142
+ }
143
+ }
144
+
145
+ async function installDependencies ( cwd : string , spinner : Ora ) {
146
+ try {
147
+ spinner . text = 'Installing dependencies...' ;
148
+ await execa ( 'npx' , [ 'expo' , 'install' , ...REQUIRED_DEPENDENCIES ] , {
149
+ cwd,
150
+ stdio : 'inherit' ,
151
+ } ) ;
152
+ spinner . text = 'Dependencies installed successfully' ;
153
+ } catch ( error ) {
154
+ spinner . fail ( 'Failed to install dependencies' ) ;
155
+ handleError ( error ) ;
117
156
process . exit ( 1 ) ;
118
157
}
119
158
}
@@ -229,98 +268,3 @@ async function updateLayoutFile(cwd: string, spinner: Ora) {
229
268
handleError ( error ) ;
230
269
}
231
270
}
232
-
233
- async function shouldPromptGitWarning ( cwd : string ) : Promise < boolean > {
234
- try {
235
- execSync ( 'git rev-parse --is-inside-work-tree' , { cwd } ) ;
236
- const status = execSync ( 'git status --porcelain' , { cwd } ) . toString ( ) ;
237
- return ! ! status ;
238
- } catch ( error ) {
239
- return false ;
240
- }
241
- }
242
-
243
- async function validateProjectDirectory ( cwd : string ) {
244
- if ( ! existsSync ( cwd ) ) {
245
- logger . error ( `The path ${ cwd } does not exist. Please try again.` ) ;
246
- process . exit ( 1 ) ;
247
- }
248
-
249
- if ( ! existsSync ( path . join ( cwd , 'package.json' ) ) ) {
250
- logger . error (
251
- 'No package.json found. Please run this command in a React Native project directory.'
252
- ) ;
253
- process . exit ( 1 ) ;
254
- }
255
- }
256
-
257
- async function checkGitStatus ( cwd : string ) {
258
- if ( await shouldPromptGitWarning ( cwd ) ) {
259
- const { proceed } = await prompts ( {
260
- type : 'confirm' ,
261
- name : 'proceed' ,
262
- message :
263
- 'The Git repository is dirty (uncommitted changes). It is recommended to commit your changes before proceeding. Do you want to continue?' ,
264
- initial : false ,
265
- } ) ;
266
-
267
- if ( ! proceed ) {
268
- logger . info ( 'Installation cancelled.' ) ;
269
- process . exit ( 0 ) ;
270
- }
271
- }
272
- }
273
-
274
- async function initializeProject ( cwd : string , overwrite : boolean ) {
275
- const spinner = ora ( `Initializing project...` ) . start ( ) ;
276
-
277
- try {
278
- let config = await getConfig ( cwd ) ;
279
-
280
- if ( ! config ) {
281
- spinner . stop ( ) ;
282
- config = await promptForConfig ( cwd ) ;
283
- spinner . start ( ) ;
284
- }
285
-
286
- const templatesDir = path . join ( fileDir , '../__generated/starter-base' ) ;
287
-
288
- await installDependencies ( cwd , spinner ) ;
289
- await updateTsConfig ( cwd , config , spinner ) ;
290
-
291
- spinner . text = 'Adding config and utility files...' ;
292
- for ( const file of TEMPLATE_FILES ) {
293
- await copyTemplateFile ( file , templatesDir , cwd , spinner , overwrite ) ;
294
- }
295
-
296
- await updateLayoutFile ( cwd , spinner ) ;
297
-
298
- spinner . succeed ( 'Initialization completed successfully!' ) ;
299
- } catch ( error ) {
300
- spinner . fail ( 'Initialization failed' ) ;
301
- handleError ( error ) ;
302
- process . exit ( 1 ) ;
303
- }
304
- }
305
-
306
- export const init = new Command ( )
307
- . name ( 'init' )
308
- . description ( 'Initialize the required configuration for your React Native project' )
309
- . option (
310
- '-c, --cwd <cwd>' ,
311
- 'the working directory. defaults to the current directory.' ,
312
- process . cwd ( )
313
- )
314
- . option ( '-o, --overwrite' , 'overwrite existing files' , false )
315
- . action ( async ( opts ) => {
316
- try {
317
- const options = initOptionsSchema . parse ( opts ) ;
318
- const cwd = path . resolve ( options . cwd ) ;
319
-
320
- await validateProjectDirectory ( cwd ) ;
321
- await checkGitStatus ( cwd ) ;
322
- await initializeProject ( cwd , options . overwrite ) ;
323
- } catch ( error ) {
324
- handleError ( error ) ;
325
- }
326
- } ) ;
0 commit comments