Skip to content

Commit 85fa2c8

Browse files
authored
refactor(create-vite): migrate to TypeScript (#9941)
1 parent 4158b98 commit 85fa2c8

File tree

4 files changed

+72
-69
lines changed

4 files changed

+72
-69
lines changed

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@
5151
"@types/micromatch": "^4.0.2",
5252
"@types/minimist": "^1.2.2",
5353
"@types/node": "^17.0.42",
54-
"@types/prompts": "^2.4.0",
54+
"@types/prompts": "^2.0.14",
5555
"@types/resolve": "^1.20.2",
5656
"@types/sass": "~1.43.1",
5757
"@types/semver": "^7.3.12",

packages/create-vite/src/index.js packages/create-vite/src/index.ts

+51-64
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
// @ts-check
21
import fs from 'node:fs'
32
import path from 'node:path'
43
import { fileURLToPath } from 'node:url'
@@ -19,10 +18,27 @@ import {
1918

2019
// Avoids autoconversion to number of the project name by defining that the args
2120
// 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: ['_'] })
2325
const cwd = process.cwd()
2426

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[] = [
2642
{
2743
name: 'vanilla',
2844
display: 'Vanilla',
@@ -149,25 +165,29 @@ const TEMPLATES = FRAMEWORKS.map(
149165
(f) => (f.variants && f.variants.map((v) => v.name)) || [f.name]
150166
).reduce((a, b) => a.concat(b), [])
151167

152-
const renameFiles = {
168+
const renameFiles: Record<string, string | undefined> = {
153169
_gitignore: '.gitignore'
154170
}
155171

172+
const defaultTargetDir = 'vite-project'
173+
156174
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
159177

160-
const defaultTargetDir = 'vite-project'
178+
let targetDir = argTargetDir || defaultTargetDir
161179
const getProjectName = () =>
162180
targetDir === '.' ? path.basename(path.resolve()) : targetDir
163181

164-
let result = {}
182+
let result: prompts.Answers<
183+
'projectName' | 'overwrite' | 'packageName' | 'framework' | 'variant'
184+
>
165185

166186
try {
167187
result = await prompts(
168188
[
169189
{
170-
type: targetDir ? null : 'text',
190+
type: argTargetDir ? null : 'text',
171191
name: 'projectName',
172192
message: reset('Project name:'),
173193
initial: defaultTargetDir,
@@ -186,7 +206,7 @@ async function init() {
186206
` is not empty. Remove existing files and continue?`
187207
},
188208
{
189-
type: (_, { overwrite } = {}) => {
209+
type: (_, { overwrite }: { overwrite?: boolean }) => {
190210
if (overwrite === false) {
191211
throw new Error(red('✖') + ' Operation cancelled')
192212
}
@@ -203,12 +223,13 @@ async function init() {
203223
isValidPackageName(dir) || 'Invalid package.json name'
204224
},
205225
{
206-
type: template && TEMPLATES.includes(template) ? null : 'select',
226+
type:
227+
argTemplate && TEMPLATES.includes(argTemplate) ? null : 'select',
207228
name: 'framework',
208229
message:
209-
typeof template === 'string' && !TEMPLATES.includes(template)
230+
typeof argTemplate === 'string' && !TEMPLATES.includes(argTemplate)
210231
? reset(
211-
`"${template}" isn't a valid template. Please choose from below: `
232+
`"${argTemplate}" isn't a valid template. Please choose from below: `
212233
)
213234
: reset('Select a framework:'),
214235
initial: 0,
@@ -221,12 +242,11 @@ async function init() {
221242
})
222243
},
223244
{
224-
type: (framework) =>
245+
type: (framework: Framework) =>
225246
framework && framework.variants ? 'select' : null,
226247
name: 'variant',
227248
message: reset('Select a variant:'),
228-
// @ts-ignore
229-
choices: (framework) =>
249+
choices: (framework: Framework) =>
230250
framework.variants.map((variant) => {
231251
const variantColor = variant.color
232252
return {
@@ -242,7 +262,7 @@ async function init() {
242262
}
243263
}
244264
)
245-
} catch (cancelled) {
265+
} catch (cancelled: any) {
246266
console.log(cancelled.message)
247267
return
248268
}
@@ -259,23 +279,15 @@ async function init() {
259279
}
260280

261281
// determine template
262-
template = variant || framework || template
282+
const template: string = variant || framework || argTemplate
263283

264284
const pkgInfo = pkgFromUserAgent(process.env.npm_config_user_agent)
265285
const pkgManager = pkgInfo ? pkgInfo.name : 'npm'
266286
const isYarn1 = pkgManager === 'yarn' && pkgInfo?.version.startsWith('1.')
267287

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) {
279291
const fullCustomCommand = customCommand
280292
.replace('TARGET_DIR', targetDir)
281293
.replace(/^npm create/, `${pkgManager} create`)
@@ -309,10 +321,8 @@ async function init() {
309321
`template-${template}`
310322
)
311323

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)
316326
if (content) {
317327
fs.writeFileSync(targetPath, content)
318328
} else {
@@ -350,14 +360,11 @@ async function init() {
350360
console.log()
351361
}
352362

353-
/**
354-
* @param {string | undefined} targetDir
355-
*/
356-
function formatTargetDir(targetDir) {
363+
function formatTargetDir(targetDir: string | undefined) {
357364
return targetDir?.trim().replace(/\/+$/g, '')
358365
}
359366

360-
function copy(src, dest) {
367+
function copy(src: string, dest: string) {
361368
const stat = fs.statSync(src)
362369
if (stat.isDirectory()) {
363370
copyDir(src, dest)
@@ -366,19 +373,13 @@ function copy(src, dest) {
366373
}
367374
}
368375

369-
/**
370-
* @param {string} projectName
371-
*/
372-
function isValidPackageName(projectName) {
376+
function isValidPackageName(projectName: string) {
373377
return /^(?:@[a-z0-9-*~][a-z0-9-*._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/.test(
374378
projectName
375379
)
376380
}
377381

378-
/**
379-
* @param {string} projectName
380-
*/
381-
function toValidPackageName(projectName) {
382+
function toValidPackageName(projectName: string) {
382383
return projectName
383384
.trim()
384385
.toLowerCase()
@@ -387,11 +388,7 @@ function toValidPackageName(projectName) {
387388
.replace(/[^a-z0-9-~]+/g, '-')
388389
}
389390

390-
/**
391-
* @param {string} srcDir
392-
* @param {string} destDir
393-
*/
394-
function copyDir(srcDir, destDir) {
391+
function copyDir(srcDir: string, destDir: string) {
395392
fs.mkdirSync(destDir, { recursive: true })
396393
for (const file of fs.readdirSync(srcDir)) {
397394
const srcFile = path.resolve(srcDir, file)
@@ -400,18 +397,12 @@ function copyDir(srcDir, destDir) {
400397
}
401398
}
402399

403-
/**
404-
* @param {string} path
405-
*/
406-
function isEmpty(path) {
400+
function isEmpty(path: string) {
407401
const files = fs.readdirSync(path)
408402
return files.length === 0 || (files.length === 1 && files[0] === '.git')
409403
}
410404

411-
/**
412-
* @param {string} dir
413-
*/
414-
function emptyDir(dir) {
405+
function emptyDir(dir: string) {
415406
if (!fs.existsSync(dir)) {
416407
return
417408
}
@@ -423,11 +414,7 @@ function emptyDir(dir) {
423414
}
424415
}
425416

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) {
431418
if (!userAgent) return undefined
432419
const pkgSpec = userAgent.split(' ')[0]
433420
const pkgSpecArr = pkgSpec.split('/')

packages/create-vite/tsconfig.json

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"include": ["src", "__tests__"],
3+
"compilerOptions": {
4+
"outDir": "dist",
5+
"target": "ES2020",
6+
"module": "ES2020",
7+
"moduleResolution": "Node",
8+
"strict": true,
9+
"declaration": false,
10+
"sourceMap": false,
11+
"noUnusedLocals": true,
12+
"esModuleInterop": true
13+
}
14+
}

pnpm-lock.yaml

+6-4
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)