1
- // @ts -check
2
1
import fs from 'node:fs'
3
2
import path from 'node:path'
4
3
import { fileURLToPath } from 'node:url'
@@ -19,10 +18,27 @@ import {
19
18
20
19
// Avoids autoconversion to number of the project name by defining that the args
21
20
// non associated with an option ( _ ) needs to be parsed as a string. See #4606
22
- const argv = minimist ( process . argv . slice ( 2 ) , { string : [ '_' ] } )
21
+ const argv = minimist < {
22
+ t ?: string
23
+ template ?: string
24
+ } > ( process . argv . slice ( 2 ) , { string : [ '_' ] } )
23
25
const cwd = process . cwd ( )
24
26
25
- const FRAMEWORKS = [
27
+ type ColorFunc = ( str : string | number ) => string
28
+ type Framework = {
29
+ name : string
30
+ display : string
31
+ color : ColorFunc
32
+ variants : FrameworkVariant [ ]
33
+ }
34
+ type FrameworkVariant = {
35
+ name : string
36
+ display : string
37
+ color : ColorFunc
38
+ customCommand ?: string
39
+ }
40
+
41
+ const FRAMEWORKS : Framework [ ] = [
26
42
{
27
43
name : 'vanilla' ,
28
44
display : 'Vanilla' ,
@@ -149,25 +165,29 @@ const TEMPLATES = FRAMEWORKS.map(
149
165
( f ) => ( f . variants && f . variants . map ( ( v ) => v . name ) ) || [ f . name ]
150
166
) . reduce ( ( a , b ) => a . concat ( b ) , [ ] )
151
167
152
- const renameFiles = {
168
+ const renameFiles : Record < string , string | undefined > = {
153
169
_gitignore : '.gitignore'
154
170
}
155
171
172
+ const defaultTargetDir = 'vite-project'
173
+
156
174
async function init ( ) {
157
- let targetDir = formatTargetDir ( argv . _ [ 0 ] )
158
- let template = argv . template || argv . t
175
+ const argTargetDir = formatTargetDir ( argv . _ [ 0 ] )
176
+ const argTemplate = argv . template || argv . t
159
177
160
- const defaultTargetDir = 'vite-project'
178
+ let targetDir = argTargetDir || defaultTargetDir
161
179
const getProjectName = ( ) =>
162
180
targetDir === '.' ? path . basename ( path . resolve ( ) ) : targetDir
163
181
164
- let result = { }
182
+ let result : prompts . Answers <
183
+ 'projectName' | 'overwrite' | 'packageName' | 'framework' | 'variant'
184
+ >
165
185
166
186
try {
167
187
result = await prompts (
168
188
[
169
189
{
170
- type : targetDir ? null : 'text' ,
190
+ type : argTargetDir ? null : 'text' ,
171
191
name : 'projectName' ,
172
192
message : reset ( 'Project name:' ) ,
173
193
initial : defaultTargetDir ,
@@ -186,7 +206,7 @@ async function init() {
186
206
` is not empty. Remove existing files and continue?`
187
207
} ,
188
208
{
189
- type : ( _ , { overwrite } = { } ) => {
209
+ type : ( _ , { overwrite } : { overwrite ?: boolean } ) => {
190
210
if ( overwrite === false ) {
191
211
throw new Error ( red ( '✖' ) + ' Operation cancelled' )
192
212
}
@@ -203,12 +223,13 @@ async function init() {
203
223
isValidPackageName ( dir ) || 'Invalid package.json name'
204
224
} ,
205
225
{
206
- type : template && TEMPLATES . includes ( template ) ? null : 'select' ,
226
+ type :
227
+ argTemplate && TEMPLATES . includes ( argTemplate ) ? null : 'select' ,
207
228
name : 'framework' ,
208
229
message :
209
- typeof template === 'string' && ! TEMPLATES . includes ( template )
230
+ typeof argTemplate === 'string' && ! TEMPLATES . includes ( argTemplate )
210
231
? reset (
211
- `"${ template } " isn't a valid template. Please choose from below: `
232
+ `"${ argTemplate } " isn't a valid template. Please choose from below: `
212
233
)
213
234
: reset ( 'Select a framework:' ) ,
214
235
initial : 0 ,
@@ -221,12 +242,11 @@ async function init() {
221
242
} )
222
243
} ,
223
244
{
224
- type : ( framework ) =>
245
+ type : ( framework : Framework ) =>
225
246
framework && framework . variants ? 'select' : null ,
226
247
name : 'variant' ,
227
248
message : reset ( 'Select a variant:' ) ,
228
- // @ts -ignore
229
- choices : ( framework ) =>
249
+ choices : ( framework : Framework ) =>
230
250
framework . variants . map ( ( variant ) => {
231
251
const variantColor = variant . color
232
252
return {
@@ -242,7 +262,7 @@ async function init() {
242
262
}
243
263
}
244
264
)
245
- } catch ( cancelled ) {
265
+ } catch ( cancelled : any ) {
246
266
console . log ( cancelled . message )
247
267
return
248
268
}
@@ -259,23 +279,15 @@ async function init() {
259
279
}
260
280
261
281
// determine template
262
- template = variant || framework || template
282
+ const template : string = variant || framework || argTemplate
263
283
264
284
const pkgInfo = pkgFromUserAgent ( process . env . npm_config_user_agent )
265
285
const pkgManager = pkgInfo ? pkgInfo . name : 'npm'
266
286
const isYarn1 = pkgManager === 'yarn' && pkgInfo ?. version . startsWith ( '1.' )
267
287
268
- if ( template . startsWith ( 'custom-' ) ) {
269
- const getCustomCommand = ( name ) => {
270
- for ( const f of FRAMEWORKS ) {
271
- for ( const v of f . variants || [ ] ) {
272
- if ( v . name === name ) {
273
- return v . customCommand
274
- }
275
- }
276
- }
277
- }
278
- const customCommand = getCustomCommand ( template )
288
+ const { customCommand } =
289
+ FRAMEWORKS . flatMap ( ( f ) => f . variants ) . find ( ( v ) => v . name === template ) ?? { }
290
+ if ( customCommand ) {
279
291
const fullCustomCommand = customCommand
280
292
. replace ( 'TARGET_DIR' , targetDir )
281
293
. replace ( / ^ n p m c r e a t e / , `${ pkgManager } create` )
@@ -309,10 +321,8 @@ async function init() {
309
321
`template-${ template } `
310
322
)
311
323
312
- const write = ( file , content ) => {
313
- const targetPath = renameFiles [ file ]
314
- ? path . join ( root , renameFiles [ file ] )
315
- : path . join ( root , file )
324
+ const write = ( file : string , content ?: string ) => {
325
+ const targetPath = path . join ( root , renameFiles [ file ] ?? file )
316
326
if ( content ) {
317
327
fs . writeFileSync ( targetPath , content )
318
328
} else {
@@ -350,14 +360,11 @@ async function init() {
350
360
console . log ( )
351
361
}
352
362
353
- /**
354
- * @param {string | undefined } targetDir
355
- */
356
- function formatTargetDir ( targetDir ) {
363
+ function formatTargetDir ( targetDir : string | undefined ) {
357
364
return targetDir ?. trim ( ) . replace ( / \/ + $ / g, '' )
358
365
}
359
366
360
- function copy ( src , dest ) {
367
+ function copy ( src : string , dest : string ) {
361
368
const stat = fs . statSync ( src )
362
369
if ( stat . isDirectory ( ) ) {
363
370
copyDir ( src , dest )
@@ -366,19 +373,13 @@ function copy(src, dest) {
366
373
}
367
374
}
368
375
369
- /**
370
- * @param {string } projectName
371
- */
372
- function isValidPackageName ( projectName ) {
376
+ function isValidPackageName ( projectName : string ) {
373
377
return / ^ (?: @ [ a - z 0 - 9 - * ~ ] [ a -z 0 -9 -* ._ ~ ] * \/ ) ? [ a - z 0 - 9 - ~ ] [ a - z 0 - 9 - ._ ~ ] * $ / . test (
374
378
projectName
375
379
)
376
380
}
377
381
378
- /**
379
- * @param {string } projectName
380
- */
381
- function toValidPackageName ( projectName ) {
382
+ function toValidPackageName ( projectName : string ) {
382
383
return projectName
383
384
. trim ( )
384
385
. toLowerCase ( )
@@ -387,11 +388,7 @@ function toValidPackageName(projectName) {
387
388
. replace ( / [ ^ a - z 0 - 9 - ~ ] + / g, '-' )
388
389
}
389
390
390
- /**
391
- * @param {string } srcDir
392
- * @param {string } destDir
393
- */
394
- function copyDir ( srcDir , destDir ) {
391
+ function copyDir ( srcDir : string , destDir : string ) {
395
392
fs . mkdirSync ( destDir , { recursive : true } )
396
393
for ( const file of fs . readdirSync ( srcDir ) ) {
397
394
const srcFile = path . resolve ( srcDir , file )
@@ -400,18 +397,12 @@ function copyDir(srcDir, destDir) {
400
397
}
401
398
}
402
399
403
- /**
404
- * @param {string } path
405
- */
406
- function isEmpty ( path ) {
400
+ function isEmpty ( path : string ) {
407
401
const files = fs . readdirSync ( path )
408
402
return files . length === 0 || ( files . length === 1 && files [ 0 ] === '.git' )
409
403
}
410
404
411
- /**
412
- * @param {string } dir
413
- */
414
- function emptyDir ( dir ) {
405
+ function emptyDir ( dir : string ) {
415
406
if ( ! fs . existsSync ( dir ) ) {
416
407
return
417
408
}
@@ -423,11 +414,7 @@ function emptyDir(dir) {
423
414
}
424
415
}
425
416
426
- /**
427
- * @param {string | undefined } userAgent process.env.npm_config_user_agent
428
- * @returns object | undefined
429
- */
430
- function pkgFromUserAgent ( userAgent ) {
417
+ function pkgFromUserAgent ( userAgent : string | undefined ) {
431
418
if ( ! userAgent ) return undefined
432
419
const pkgSpec = userAgent . split ( ' ' ) [ 0 ]
433
420
const pkgSpecArr = pkgSpec . split ( '/' )
0 commit comments