Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(create-vite): migrate to TypeScript #9941

Merged
merged 1 commit into from
Sep 3, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
"@types/micromatch": "^4.0.2",
"@types/minimist": "^1.2.2",
"@types/node": "^17.0.42",
"@types/prompts": "^2.4.0",
"@types/prompts": "^2.0.14",
"@types/resolve": "^1.20.2",
"@types/sass": "~1.43.1",
"@types/semver": "^7.3.12",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// @ts-check
import fs from 'node:fs'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
Expand All @@ -19,10 +18,27 @@ import {

// Avoids autoconversion to number of the project name by defining that the args
// non associated with an option ( _ ) needs to be parsed as a string. See #4606
const argv = minimist(process.argv.slice(2), { string: ['_'] })
const argv = minimist<{
t?: string
template?: string
}>(process.argv.slice(2), { string: ['_'] })
const cwd = process.cwd()

const FRAMEWORKS = [
type ColorFunc = (str: string | number) => string
type Framework = {
name: string
display: string
color: ColorFunc
variants: FrameworkVariant[]
}
type FrameworkVariant = {
name: string
display: string
color: ColorFunc
customCommand?: string
}

const FRAMEWORKS: Framework[] = [
{
name: 'vanilla',
display: 'Vanilla',
Expand Down Expand Up @@ -149,25 +165,29 @@ const TEMPLATES = FRAMEWORKS.map(
(f) => (f.variants && f.variants.map((v) => v.name)) || [f.name]
).reduce((a, b) => a.concat(b), [])

const renameFiles = {
const renameFiles: Record<string, string | undefined> = {
_gitignore: '.gitignore'
}

const defaultTargetDir = 'vite-project'

async function init() {
let targetDir = formatTargetDir(argv._[0])
let template = argv.template || argv.t
const argTargetDir = formatTargetDir(argv._[0])
const argTemplate = argv.template || argv.t

const defaultTargetDir = 'vite-project'
let targetDir = argTargetDir || defaultTargetDir
const getProjectName = () =>
targetDir === '.' ? path.basename(path.resolve()) : targetDir

let result = {}
let result: prompts.Answers<
'projectName' | 'overwrite' | 'packageName' | 'framework' | 'variant'
>

try {
result = await prompts(
[
{
type: targetDir ? null : 'text',
type: argTargetDir ? null : 'text',
name: 'projectName',
message: reset('Project name:'),
initial: defaultTargetDir,
Expand All @@ -186,7 +206,7 @@ async function init() {
` is not empty. Remove existing files and continue?`
},
{
type: (_, { overwrite } = {}) => {
type: (_, { overwrite }: { overwrite?: boolean }) => {
if (overwrite === false) {
throw new Error(red('✖') + ' Operation cancelled')
}
Expand All @@ -203,12 +223,13 @@ async function init() {
isValidPackageName(dir) || 'Invalid package.json name'
},
{
type: template && TEMPLATES.includes(template) ? null : 'select',
type:
argTemplate && TEMPLATES.includes(argTemplate) ? null : 'select',
name: 'framework',
message:
typeof template === 'string' && !TEMPLATES.includes(template)
typeof argTemplate === 'string' && !TEMPLATES.includes(argTemplate)
? reset(
`"${template}" isn't a valid template. Please choose from below: `
`"${argTemplate}" isn't a valid template. Please choose from below: `
)
: reset('Select a framework:'),
initial: 0,
Expand All @@ -221,12 +242,11 @@ async function init() {
})
},
{
type: (framework) =>
type: (framework: Framework) =>
framework && framework.variants ? 'select' : null,
name: 'variant',
message: reset('Select a variant:'),
// @ts-ignore
choices: (framework) =>
choices: (framework: Framework) =>
framework.variants.map((variant) => {
const variantColor = variant.color
return {
Expand All @@ -242,7 +262,7 @@ async function init() {
}
}
)
} catch (cancelled) {
} catch (cancelled: any) {
console.log(cancelled.message)
return
}
Expand All @@ -259,23 +279,15 @@ async function init() {
}

// determine template
template = variant || framework || template
const template: string = variant || framework || argTemplate

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

if (template.startsWith('custom-')) {
const getCustomCommand = (name) => {
for (const f of FRAMEWORKS) {
for (const v of f.variants || []) {
if (v.name === name) {
return v.customCommand
}
}
}
}
const customCommand = getCustomCommand(template)
const { customCommand } =
FRAMEWORKS.flatMap((f) => f.variants).find((v) => v.name === template) ?? {}
if (customCommand) {
const fullCustomCommand = customCommand
.replace('TARGET_DIR', targetDir)
.replace(/^npm create/, `${pkgManager} create`)
Expand Down Expand Up @@ -309,10 +321,8 @@ async function init() {
`template-${template}`
)

const write = (file, content) => {
const targetPath = renameFiles[file]
? path.join(root, renameFiles[file])
: path.join(root, file)
const write = (file: string, content?: string) => {
const targetPath = path.join(root, renameFiles[file] ?? file)
if (content) {
fs.writeFileSync(targetPath, content)
} else {
Expand Down Expand Up @@ -350,14 +360,11 @@ async function init() {
console.log()
}

/**
* @param {string | undefined} targetDir
*/
function formatTargetDir(targetDir) {
function formatTargetDir(targetDir: string | undefined) {
return targetDir?.trim().replace(/\/+$/g, '')
}

function copy(src, dest) {
function copy(src: string, dest: string) {
const stat = fs.statSync(src)
if (stat.isDirectory()) {
copyDir(src, dest)
Expand All @@ -366,19 +373,13 @@ function copy(src, dest) {
}
}

/**
* @param {string} projectName
*/
function isValidPackageName(projectName) {
function isValidPackageName(projectName: string) {
return /^(?:@[a-z0-9-*~][a-z0-9-*._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/.test(
projectName
)
}

/**
* @param {string} projectName
*/
function toValidPackageName(projectName) {
function toValidPackageName(projectName: string) {
return projectName
.trim()
.toLowerCase()
Expand All @@ -387,11 +388,7 @@ function toValidPackageName(projectName) {
.replace(/[^a-z0-9-~]+/g, '-')
}

/**
* @param {string} srcDir
* @param {string} destDir
*/
function copyDir(srcDir, destDir) {
function copyDir(srcDir: string, destDir: string) {
fs.mkdirSync(destDir, { recursive: true })
for (const file of fs.readdirSync(srcDir)) {
const srcFile = path.resolve(srcDir, file)
Expand All @@ -400,18 +397,12 @@ function copyDir(srcDir, destDir) {
}
}

/**
* @param {string} path
*/
function isEmpty(path) {
function isEmpty(path: string) {
const files = fs.readdirSync(path)
return files.length === 0 || (files.length === 1 && files[0] === '.git')
}

/**
* @param {string} dir
*/
function emptyDir(dir) {
function emptyDir(dir: string) {
if (!fs.existsSync(dir)) {
return
}
Expand All @@ -423,11 +414,7 @@ function emptyDir(dir) {
}
}

/**
* @param {string | undefined} userAgent process.env.npm_config_user_agent
* @returns object | undefined
*/
function pkgFromUserAgent(userAgent) {
function pkgFromUserAgent(userAgent: string | undefined) {
if (!userAgent) return undefined
const pkgSpec = userAgent.split(' ')[0]
const pkgSpecArr = pkgSpec.split('/')
Expand Down
14 changes: 14 additions & 0 deletions packages/create-vite/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"include": ["src", "__tests__"],
"compilerOptions": {
"outDir": "dist",
"target": "ES2020",
"module": "ES2020",
"moduleResolution": "Node",
"strict": true,
"declaration": false,
"sourceMap": false,
"noUnusedLocals": true,
"esModuleInterop": true
}
}
10 changes: 6 additions & 4 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.