import type { SpawnOptions } from 'node:child_process' import fs from 'node:fs' import path from 'node:path' import { fileURLToPath } from 'node:url' import util from 'node:util' import * as prompts from '@clack/prompts' import mri from 'mri' import { detectAgent } from 'std-env' import { xSync } from 'tinyexec' import packageInfo from '../package.json' with { type: 'json' } const { blue, blueBright, cyan, green, greenBright, magenta, red, redBright, reset, underline, yellow, } = createColors() const argv = mri<{ template?: string help?: boolean overwrite?: boolean immediate?: boolean interactive?: boolean eslint?: boolean }>(process.argv.slice(2), { boolean: ['help', 'overwrite', 'immediate', 'interactive', 'eslint'], alias: { h: 'help', t: 'template', i: 'immediate' }, string: ['template'], }) const cwd = process.cwd() // oxfmt-ignore const helpMessage = `\ Usage: create-vite [OPTION]... [DIRECTORY] Create a new Vite project in JavaScript or TypeScript. When running in TTY, the CLI will start in interactive mode. Options: -t, --template NAME use a specific template -i, --immediate / --no-immediate install dependencies and start dev --eslint / --no-eslint use ESLint instead of Oxlint (only for React templates) --overwrite remove existing files if target directory is not empty --interactive / --no-interactive force interactive / non-interactive mode -h, --help display this help message Available templates: ${yellow ('vanilla-ts vanilla' )} ${green ('vue-ts vue' )} ${cyan ('react-ts react' )} ${cyan ('react-compiler-ts react-compiler')} ${magenta ('preact-ts preact' )} ${redBright ('lit-ts lit' )} ${red ('svelte-ts svelte' )} ${blue ('solid-ts solid' )} ${blueBright('qwik-ts qwik' )}` type ColorFunc = (str: string) => string type Framework = { name: string display: string color: ColorFunc variants: FrameworkVariant[] } type FrameworkVariant = { name: string display: string link?: `https://${string}` color: ColorFunc customCommand?: string } const FRAMEWORKS: Framework[] = [ { name: 'vanilla', display: 'Vanilla', color: yellow, variants: [ { name: 'vanilla-ts', display: 'TypeScript', color: blue, }, { name: 'vanilla', display: 'JavaScript', color: yellow, }, ], }, { name: 'vue', display: 'Vue', color: green, variants: [ { name: 'vue-ts', display: 'TypeScript', color: blue, }, { name: 'vue', display: 'JavaScript', color: yellow, }, { name: 'custom-create-vue', display: 'Official Vue Starter ↗', color: green, customCommand: 'npm create vue@latest TARGET_DIR', }, { name: 'custom-nuxt', display: 'Nuxt ↗', link: 'https://nuxt.com', color: greenBright, customCommand: 'npm exec nuxi init TARGET_DIR', }, { name: 'custom-vike-vue', display: 'Vike ↗', link: 'https://vike.dev', color: greenBright, customCommand: 'npm create -- vike@latest --vue TARGET_DIR', }, ], }, { name: 'react', display: 'React', color: cyan, variants: [ { name: 'react-ts', display: 'TypeScript', color: blue, }, { name: 'react-compiler-ts', display: 'TypeScript + React Compiler', color: blue, }, { name: 'react', display: 'JavaScript', color: yellow, }, { name: 'react-compiler', display: 'JavaScript + React Compiler', color: yellow, }, { name: 'rsc', display: 'RSC', color: magenta, customCommand: 'npm exec tiged vitejs/vite-plugin-react/packages/plugin-rsc/examples/starter TARGET_DIR', }, { name: 'custom-react-router', display: 'React Router v7 ↗', link: 'https://reactrouter.com', color: cyan, customCommand: 'npm create react-router@latest TARGET_DIR', }, { name: 'custom-tanstack-start-react', display: 'TanStack Start ↗', link: 'https://tanstack.com/start', color: cyan, customCommand: 'npm exec -- @tanstack/cli@latest create TARGET_DIR --framework react --build-tool vite --interactive', }, { name: 'custom-tanstack-router-react', display: 'TanStack Router ↗', link: 'https://tanstack.com/router', color: cyan, customCommand: 'npm exec -- @tanstack/cli@latest create TARGET_DIR --framework react --build-tool vite --router-only --interactive', }, { name: 'redwoodsdk-standard', display: 'RedwoodSDK ↗', link: 'https://rwsdk.com', color: cyan, customCommand: 'npm create rwsdk@latest TARGET_DIR', }, { name: 'custom-vike-react', display: 'Vike ↗', link: 'https://vike.dev', color: cyan, customCommand: 'npm create -- vike@latest --react TARGET_DIR', }, ], }, { name: 'preact', display: 'Preact', color: magenta, variants: [ { name: 'preact-ts', display: 'TypeScript', color: blue, }, { name: 'preact', display: 'JavaScript', color: yellow, }, { name: 'custom-create-preact', display: 'Official Preact Starter ↗', color: magenta, customCommand: 'npm create preact@latest TARGET_DIR', }, ], }, { name: 'lit', display: 'Lit', color: redBright, variants: [ { name: 'lit-ts', display: 'TypeScript', color: blue, }, { name: 'lit', display: 'JavaScript', color: yellow, }, ], }, { name: 'svelte', display: 'Svelte', color: red, variants: [ { name: 'svelte-ts', display: 'TypeScript', color: blue, }, { name: 'svelte', display: 'JavaScript', color: yellow, }, { name: 'custom-svelte-kit', display: 'SvelteKit ↗', color: red, customCommand: 'npm exec sv create TARGET_DIR', }, ], }, { name: 'solid', display: 'Solid', color: blue, variants: [ { name: 'solid-ts', display: 'TypeScript', color: blue, }, { name: 'solid', display: 'JavaScript', color: yellow, }, { name: 'custom-tanstack-start-solid', display: 'TanStack Start ↗', link: 'https://tanstack.com/start', color: cyan, customCommand: 'npm exec -- @tanstack/cli@latest create TARGET_DIR --framework solid --build-tool vite --interactive', }, { name: 'custom-tanstack-router-solid', display: 'TanStack Router ↗', link: 'https://tanstack.com/router', color: cyan, customCommand: 'npm exec -- @tanstack/cli@latest create TARGET_DIR --framework solid --build-tool vite --router-only --interactive', }, { name: 'custom-vike-solid', display: 'Vike ↗', link: 'https://vike.dev', color: cyan, customCommand: 'npm create -- vike@latest --solid TARGET_DIR', }, ], }, { name: 'ember', display: 'Ember', color: redBright, variants: [ { name: 'ember-app-ts', display: 'TypeScript ↗', color: blueBright, customCommand: 'npm exec -- ember-cli@latest new TARGET_DIR --typescript', }, { name: 'ember-app', display: 'JavaScript ↗', color: redBright, customCommand: 'npm exec -- ember-cli@latest new TARGET_DIR', }, ], }, { name: 'qwik', display: 'Qwik', color: blueBright, variants: [ { name: 'qwik-ts', display: 'TypeScript', color: blueBright, }, { name: 'qwik', display: 'JavaScript', color: yellow, }, { name: 'custom-qwik-city', display: 'QwikCity ↗', color: blueBright, customCommand: 'npm create qwik@latest empty TARGET_DIR', }, ], }, { name: 'angular', display: 'Angular', color: red, variants: [ { name: 'custom-angular', display: 'Angular ↗', color: red, customCommand: 'npm exec @angular/cli@latest new TARGET_DIR', }, { name: 'custom-analog', display: 'Analog ↗', color: yellow, customCommand: 'npm create analog@latest TARGET_DIR', }, ], }, { name: 'marko', display: 'Marko', color: magenta, variants: [ { name: 'marko-run', display: 'Marko Run ↗', color: magenta, customCommand: 'npm create -- marko@latest --name TARGET_DIR', }, ], }, { name: 'others', display: 'Others', color: reset, variants: [ { name: 'create-vite-extra', display: 'Extra Vite Starters ↗', color: reset, customCommand: 'npm create vite-extra@latest TARGET_DIR', }, { name: 'create-electron-vite', display: 'Electron ↗', color: reset, customCommand: 'npm create electron-vite@latest TARGET_DIR', }, ], }, ] const TEMPLATES = FRAMEWORKS.map((f) => f.variants.map((v) => v.name)).reduce( (a, b) => a.concat(b), [], ) const renameFiles: Record = { _gitignore: '.gitignore', '_oxlintrc.json': '.oxlintrc.json', } const defaultTargetDir = 'vite-project' function run([command, ...args]: string[], options?: SpawnOptions) { let exitCode: number | undefined try { ;({ exitCode } = xSync(command, args, { nodeOptions: options })) } catch (error) { console.error(`\n${command} ${args.join(' ')} error!`) console.error(error) process.exit(1) } if (exitCode != null && exitCode > 0) { process.exit(exitCode) } } function install(root: string, agent: string) { if (process.env._VITE_TEST_CLI) { prompts.log.step( `Installing dependencies with ${agent}... (skipped in test)`, ) return } prompts.log.step(`Installing dependencies with ${agent}...`) run(getInstallCommand(agent), { stdio: 'inherit', cwd: root, }) } function start(root: string, agent: string) { if (process.env._VITE_TEST_CLI) { prompts.log.step('Starting dev server... (skipped in test)') return } prompts.log.step('Starting dev server...') run(getRunCommand(agent, 'dev'), { stdio: 'inherit', cwd: root, }) } async function init() { const argTargetDir = argv._[0] ? formatTargetDir(String(argv._[0])) : undefined const argTemplate = argv.template const argOverwrite = argv.overwrite const argImmediate = argv.immediate const argInteractive = argv.interactive const argEslint = argv.eslint const help = argv.help if (help) { console.log(helpMessage) return } console.log(`\n${cyan(`create-vite v${packageInfo.version}`)}\n`) const interactive = argInteractive ?? process.stdin.isTTY // Detect AI agent environment for better agent experience (AX) if (interactive && detectAgent().name) { console.log( '\nTo create in one go, run: create-vite --no-interactive --template