@@ -21,24 +21,6 @@ import chalk from 'chalk';
21
21
import validate from 'validate-npm-package-name' ;
22
22
import inquirer , { ListQuestion , Question } from 'inquirer' ;
23
23
24
- const envPrefix : Record < string , string > = {
25
- vanilla : 'VITE_' ,
26
- 'vanilla-ts' : 'VITE_' ,
27
- react : 'VITE_' ,
28
- 'react-ts' : 'VITE_' ,
29
- vue : 'VITE_' ,
30
- 'vue-ts' : 'VITE_' ,
31
- svelte : 'VITE_' ,
32
- 'svelte-ts' : 'VITE_' ,
33
- nextjs : 'NEXT_PUBLIC_' ,
34
- 'nextjs-ts' : 'NEXT_PUBLIC_' ,
35
- nuxtjs : '' ,
36
- 'nuxtjs-ts' : ''
37
- } ;
38
- const envFileNameVariant : Record < string , string > = {
39
- nextjs : '.env.local' ,
40
- 'nextjs-ts' : '.env.local' ,
41
- } ;
42
24
const rename : Record < string , string > = {
43
25
'.gitignore.default' : '.gitignore'
44
26
} ;
@@ -47,40 +29,36 @@ export function init() {
47
29
console . log (
48
30
`${ chalk . greenBright ( 'Welcome' ) } to the ${ chalk . cyan ( 'Create LIFF App' ) } `
49
31
) ;
50
- prompt ( questions ) . then ( async ( answers ) => await createLiffApp ( answers ) ) ;
32
+ prompt ( questions ) . then ( async ( answers ) => await createLiffApp ( answers ) ) ;
51
33
}
52
34
53
35
type PackageManager = 'npm' | 'yarn'
54
36
55
37
export async function createLiffApp ( answers : Answers ) {
56
- const { projectName, template, language, installNow, liffId } = answers ;
57
- const packageManager : PackageManager = installNow ? await inquirer . prompt ( {
58
- type : 'list' ,
59
- name : 'packageManager' ,
60
- message : 'Which package manager do you want to use?' ,
61
- choices : [
62
- {
63
- key : 'yarn' ,
64
- value : 'yarn' ,
65
- checked : true ,
66
- } ,
67
- {
68
- key : 'npm' ,
69
- value : 'npm' ,
70
- checked : false ,
71
- } ,
72
- ] ,
73
- } ) . then ( ( { packageManager } ) => packageManager as PackageManager ) : 'npm' ;
74
-
75
- const templateName = `${ template } ${ language === 'JavaScript' ? '' : '-ts' } ` ;
38
+ const { projectName, template, language, liffId } = answers ;
39
+ const templateConfig = templates [ template ] as TemplateOptions | undefined ;
40
+ if ( ! templateConfig ) {
41
+ throw new Error ( `Invalid template name: ${ template } ` ) ;
42
+ }
43
+ const isTypescript = language === 'TypeScript' ;
76
44
const cwd = process . cwd ( ) ;
77
45
const root = path . join ( cwd , projectName ) ;
46
+ const packageManager = answers . packageManager as PackageManager ;
47
+ const isYarn = packageManager === 'yarn' ;
78
48
79
49
try {
80
- // create directory
81
- fs . mkdirSync ( root , { recursive : true } ) ;
50
+ if ( templateConfig ?. getCreateAppScript ) {
51
+ // generate project using `create-app`
52
+ const script = templateConfig . getCreateAppScript ( { isTypescript, isYarn, projectName } ) ;
53
+ console . log ( '\nGenerating liff app using `create-app`, this might take a while.\n' ) ;
54
+ await executeCreateAppScript ( script ) ;
55
+ } else {
56
+ // create directory
57
+ fs . mkdirSync ( root , { recursive : true } ) ;
58
+ }
82
59
83
60
// copy files
61
+ const templateName = `${ template } ${ isTypescript ? '-ts' : '' } ` ;
84
62
const templateDir = path . join ( __dirname , '../templates' , templateName ) ;
85
63
const files = fs . readdirSync ( templateDir ) ;
86
64
for ( const file of files . filter ( f => f !== 'package.json' ) ) {
@@ -89,25 +67,37 @@ export async function createLiffApp(answers: Answers) {
89
67
copy ( src , dest ) ;
90
68
}
91
69
92
- // create package.json
93
- const packageName = isValidPackageName ( projectName ) ? projectName : toValidPackageName ( projectName ) ;
94
- const pkg = require ( path . join ( templateDir , 'package.json' ) ) ;
95
- pkg . name = packageName ;
96
- fs . writeFileSync ( path . join ( root , 'package.json' ) , JSON . stringify ( pkg , null , 2 ) ) ;
70
+ if ( ! templateConfig ?. getCreateAppScript ) {
71
+ // create package.json
72
+ const packageName = isValidPackageName ( projectName ) ? projectName : toValidPackageName ( projectName ) ;
73
+ const pkg = require ( path . join ( templateDir , 'package.json' ) ) ;
74
+ pkg . name = packageName ;
75
+ fs . writeFileSync ( path . join ( root , 'package.json' ) , JSON . stringify ( pkg , null , 2 ) ) ;
76
+ }
97
77
98
78
// create .env file
99
- const content = `${ envPrefix [ templateName ] } LIFF_ID=${ liffId } ` ;
100
- const envFileName = envFileNameVariant [ templateName ] || '.env' ;
79
+ const content = `${ templateConfig . envPrefix } LIFF_ID=${ liffId } ` ;
80
+ const envFileName = templateConfig ?. envFileNameVariant || '.env' ;
101
81
fs . writeFileSync ( path . join ( root , envFileName ) , content ) ;
102
82
103
83
// install
104
- const isYarn = packageManager === 'yarn' ;
105
- if ( installNow ) {
106
- await install ( root , isYarn ) ;
84
+ const { dependencies, devDependencies, tsDevDependencies } = templateConfig ;
85
+ if ( isTypescript ) devDependencies . push ( ...tsDevDependencies ) ;
86
+
87
+ console . log ( '\nInstalling dependencies:' ) ;
88
+ dependencies . forEach ( ( dependency ) => console . log ( `- ${ chalk . blue ( dependency ) } ` ) ) ;
89
+ console . log ( ) ;
90
+ await install ( { root, isYarn, dependencies, isDev : false } ) ;
91
+
92
+ if ( devDependencies . length ) {
93
+ console . log ( '\nInstalling devDependencies:' ) ;
94
+ devDependencies . forEach ( ( dependency ) => console . log ( `- ${ chalk . blue ( dependency ) } ` ) ) ;
95
+ console . log ( ) ;
96
+ await install ( { root, isYarn, dependencies : devDependencies , isDev : true } ) ;
107
97
}
108
98
109
99
// Done
110
- showDoneComments ( { projectName, installNow , isYarn } ) ;
100
+ showDoneComments ( { projectName, isYarn } ) ;
111
101
} catch ( error ) {
112
102
console . error ( error ) ;
113
103
process . exit ( 1 ) ;
@@ -148,11 +138,51 @@ function toValidPackageName(name: string): string {
148
138
. replace ( / [ ^ a - z 0 - 9 - ~ ] + / g, '-' ) ;
149
139
}
150
140
151
- function install ( root : string , isYarn : boolean ) {
141
+ function executeCreateAppScript ( script : string [ ] ) {
142
+ return new Promise < void > ( ( resolve , reject ) => {
143
+ try {
144
+ const [ command , ...args ] = script ;
145
+ const child = spawn ( command , args , {
146
+ stdio : 'inherit' ,
147
+ env : { ...process . env , ADBLOCK : '1' , DISABLE_OPENCOLLECTIVE : '1' } ,
148
+ } ) ;
149
+ child . on ( 'close' , ( code ) => {
150
+ if ( code !== 0 ) {
151
+ reject ( { command : `${ command } ${ args . join ( ' ' ) } ` } ) ;
152
+ return ;
153
+ }
154
+ resolve ( ) ;
155
+ } ) ;
156
+ } catch ( error ) {
157
+ reject ( `Error occurred during installation: ${ error } ` ) ;
158
+ }
159
+ } ) ;
160
+ }
161
+
162
+ function install ( {
163
+ root,
164
+ dependencies,
165
+ isYarn,
166
+ isDev,
167
+ } : {
168
+ root : string ;
169
+ dependencies : string [ ] ;
170
+ isYarn : boolean ;
171
+ isDev : boolean ;
172
+ } ) {
152
173
return new Promise < void > ( ( resolve , reject ) => {
153
174
try {
154
175
const command = isYarn ? 'yarnpkg' : 'npm' ;
155
- const args = [ 'install' , isYarn ? '--cwd' : '--prefix' , root ] ;
176
+ const args : string [ ] = [ ] ;
177
+ if ( isYarn ) {
178
+ args . push ( 'add' , '--exact' , '--cwd' , root ) ;
179
+ if ( isDev ) args . push ( '--dev' ) ;
180
+ } else {
181
+ args . push ( 'install' , '--save-exact' , '--prefix' , root ) ;
182
+ if ( isDev ) args . push ( '--save-dev' ) ;
183
+ }
184
+ args . push ( ...dependencies ) ;
185
+
156
186
const child = spawn ( command , args , {
157
187
stdio : 'inherit' ,
158
188
env : { ...process . env , ADBLOCK : '1' , DISABLE_OPENCOLLECTIVE : '1' }
@@ -170,16 +200,9 @@ function install(root: string, isYarn: boolean) {
170
200
} ) ;
171
201
}
172
202
173
- function showDoneComments ( { projectName, installNow , isYarn} : { projectName : string , installNow : boolean , isYarn : boolean } ) {
203
+ function showDoneComments ( { projectName, isYarn } : { projectName : string ; isYarn : boolean } ) {
174
204
console . log ( '\n\nDone! Now run: \n' ) ;
175
205
console . log ( ` cd ${ chalk . blue ( projectName ) } ` ) ;
176
- if ( ! installNow ) {
177
- if ( isYarn ) {
178
- console . log ( ' yarn' ) ;
179
- } else {
180
- console . log ( ' npm install' ) ;
181
- }
182
- }
183
206
if ( isYarn ) {
184
207
console . log ( ' yarn dev\n\n' ) ;
185
208
} else {
@@ -270,8 +293,103 @@ const questions: Array<Question | ListQuestion> = [
270
293
}
271
294
} ,
272
295
{
273
- type : 'confirm' ,
274
- name : 'installNow' ,
275
- message : 'Do you want to install it now with package manager?'
296
+ type : 'list' ,
297
+ name : 'packageManager' ,
298
+ message : 'Which package manager do you want to use?' ,
299
+ choices : [
300
+ {
301
+ key : 'yarn' ,
302
+ value : 'yarn' ,
303
+ checked : true ,
304
+ } ,
305
+ {
306
+ key : 'npm' ,
307
+ value : 'npm' ,
308
+ checked : false ,
309
+ } ,
310
+ ] ,
276
311
}
277
312
] ;
313
+
314
+ type TemplateOptions = {
315
+ envPrefix : string ;
316
+ envFileNameVariant ?: string ;
317
+ dependencies : string [ ] ;
318
+ devDependencies : string [ ] ;
319
+ tsDevDependencies : string [ ] ;
320
+ getCreateAppScript ?: ( args : CreateAppScriptOptions ) => string [ ] ;
321
+ } ;
322
+ type CreateAppScriptOptions = {
323
+ isTypescript : boolean ;
324
+ isYarn : boolean ;
325
+ projectName : string ;
326
+ } ;
327
+ const templates : Record < string , TemplateOptions > = {
328
+ vanilla : {
329
+ envPrefix : 'VITE_' ,
330
+ dependencies : [ '@line/liff' ] ,
331
+ devDependencies : [ 'vite' ] ,
332
+ tsDevDependencies : [ 'typescript' ] ,
333
+ } ,
334
+ react : {
335
+ envPrefix : 'VITE_' ,
336
+ dependencies : [ '@line/liff' , 'react' , 'react-dom' ] ,
337
+ devDependencies : [ '@vitejs/plugin-react' , 'vite' ] ,
338
+ tsDevDependencies : [ '@types/react' , '@types/react-dom' , 'typescript' ] ,
339
+ } ,
340
+ vue : {
341
+ envPrefix : 'VITE_' ,
342
+ dependencies : [ '@line/liff' , 'vue' ] ,
343
+ devDependencies : [ '@vitejs/plugin-vue' , 'vite' ] ,
344
+ tsDevDependencies : [ 'typescript' , 'vue-tsc' ] ,
345
+ } ,
346
+ svelte : {
347
+ envPrefix : 'VITE_' ,
348
+ dependencies : [ '@line/liff' ] ,
349
+ devDependencies : [ '@sveltejs/vite-plugin-svelte' , 'svelte' , 'vite' ] ,
350
+ tsDevDependencies : [ '@tsconfig/svelte' , 'svelte-check' , 'svelte-preprocess' , 'tslib' , 'typescript' ] ,
351
+ } ,
352
+ nextjs : {
353
+ envPrefix : 'NEXT_PUBLIC_' ,
354
+ envFileNameVariant : '.env.local' ,
355
+ dependencies : [ '@line/liff' ] ,
356
+ devDependencies : [ ] ,
357
+ tsDevDependencies : [ ] ,
358
+ getCreateAppScript : ( { isTypescript, isYarn, projectName } ) => {
359
+ const script = [ ] ;
360
+ if ( isYarn ) {
361
+ script . push ( 'yarnpkg' , 'create' , 'next-app' ) ;
362
+ } else {
363
+ script . push ( 'npx' , 'create-next-app' , '--use-npm' ) ;
364
+ }
365
+ script . push ( projectName ) ;
366
+ if ( isTypescript ) script . push ( '--ts' ) ;
367
+
368
+ return script ;
369
+ } ,
370
+ } ,
371
+ nuxtjs : {
372
+ envPrefix : '' ,
373
+ dependencies : [ '@line/liff' ] ,
374
+ devDependencies : [ ] ,
375
+ tsDevDependencies : [ ] ,
376
+ getCreateAppScript : ( { isTypescript, isYarn, projectName } ) => {
377
+ const answers = {
378
+ name : projectName ,
379
+ pm : isYarn ? 'yarn' : 'npm' ,
380
+ language : isTypescript ? 'ts' : 'js' ,
381
+ features : [ 'axios' ] ,
382
+ linter : [ 'eslint' ] ,
383
+ ui : 'none' ,
384
+ test : 'none' ,
385
+ mode : 'universal' ,
386
+ target : 'server' ,
387
+ devTools : 'none' ,
388
+ vcs : 'none' ,
389
+ } ;
390
+ const script = [ 'npx' , 'create-nuxt-app' , projectName , '--answers' , JSON . stringify ( answers ) ] ;
391
+
392
+ return script ;
393
+ } ,
394
+ } ,
395
+ } ;
0 commit comments