From 8c15e0cabf392fae73ea482698c8f47362634e59 Mon Sep 17 00:00:00 2001 From: sapphi-red Date: Thu, 1 Sep 2022 15:02:32 +0900 Subject: [PATCH] refactor(create-vite): migrate to TypeScript --- package.json | 2 +- .../create-vite/src/{index.js => index.ts} | 115 ++++++++---------- packages/create-vite/tsconfig.json | 14 +++ pnpm-lock.yaml | 10 +- 4 files changed, 72 insertions(+), 69 deletions(-) rename packages/create-vite/src/{index.js => index.ts} (81%) create mode 100644 packages/create-vite/tsconfig.json diff --git a/package.json b/package.json index 2a41ad3a384ed9..a4e029cde05307 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/packages/create-vite/src/index.js b/packages/create-vite/src/index.ts similarity index 81% rename from packages/create-vite/src/index.js rename to packages/create-vite/src/index.ts index 8e5aa02ff4a40e..799ca93a75cd47 100755 --- a/packages/create-vite/src/index.js +++ b/packages/create-vite/src/index.ts @@ -1,4 +1,3 @@ -// @ts-check import fs from 'node:fs' import path from 'node:path' import { fileURLToPath } from 'node:url' @@ -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', @@ -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 = { _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, @@ -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') } @@ -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, @@ -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 { @@ -242,7 +262,7 @@ async function init() { } } ) - } catch (cancelled) { + } catch (cancelled: any) { console.log(cancelled.message) return } @@ -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`) @@ -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 { @@ -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) @@ -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() @@ -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) @@ -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 } @@ -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('/') diff --git a/packages/create-vite/tsconfig.json b/packages/create-vite/tsconfig.json new file mode 100644 index 00000000000000..0ec39bdf6a1404 --- /dev/null +++ b/packages/create-vite/tsconfig.json @@ -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 + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1d459cc2893bd6..6c27045e25270e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -25,7 +25,7 @@ importers: '@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 @@ -81,7 +81,7 @@ importers: '@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 @@ -2807,8 +2807,10 @@ packages: /@types/parse-json/4.0.0: resolution: {integrity: sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==} - /@types/prompts/2.4.0: - resolution: {integrity: sha512-7th8Opn+0XlN0O6qzO7dXOPwL6rigq/EwRS2DntaTHwSw8cLaYKeAPt5dWEKHSL+ffVSUl1itTPUC06+FlsV4Q==} + /@types/prompts/2.0.14: + resolution: {integrity: sha512-HZBd99fKxRWpYCErtm2/yxUZv6/PBI9J7N4TNFffl5JbrYMHBwF25DjQGTW3b3jmXq+9P6/8fCIb2ee57BFfYA==} + dependencies: + '@types/node': 17.0.42 dev: true /@types/resolve/1.17.1: