FORMA

create-vite

pnpm create vite 实际执行的是 npm 包 create-vite(与 vite 本体不同)。工程化背景见 工程化概览工作原理

下文从包管理器如何拉起脚手架,到 create-vite 内部流程做串联说明。

下文逻辑以 Vite 仓库中 packages/create-vite 为准;具体实现会随版本迭代,以你本地安装的 create-vite 源码为准。

先分清两个概念:Vite 与 create-vite

  • Vite:构建工具与开发服务器(vite 命令、vite.config 等),负责开发与打包。
  • create-vite独立 npm 包,只做「把模板目录拷到目标文件夹、改 package.json、可选装依赖并启动 dev」等脚手架工作。

因此 pnpm create vite 实际执行的是 create-vite 这个包的入口,而不是直接调用 vite 本体。

pnpm create vite 是如何找到 create-vite 的?

各包管理器都约定:create <name> 会解析并执行名为 create-<name> 的包。

你输入的命令实际拉起的包(概念上)
pnpm create vitecreate-vite
npm create vite@latestcreate-vite(可指定版本)
yarn create vitecreate-vite

pnpm 会下载(或复用缓存)create-vite 到临时环境并执行其 bin,无需全局安装。这与「先 npm i -g create-vite 再执行」效果类似,但更干净。

源码入口与参数解析

create-vite 入口里用 mri 解析 process.argv(从第三个参数起,即用户传入部分),并配置了布尔选项短参数别名

ts
// 与仓库中一致的思路:pnpm create vite -t vue-ts
import mri from "mri";

const argv = mri<{
    template?: string;
    help?: boolean;
    overwrite?: boolean;
    immediate?: boolean;
    interactive?: boolean;
}>(process.argv.slice(2), {
    boolean: ["help", "overwrite", "immediate", "interactive"],
    alias: { h: "help", t: "template", i: "immediate" },
    string: ["template"],
});

要点:

  • -t / --template:指定模板 id,例如 vue-tsreact;合法值来自内置列表(见下文 TEMPLATES)。
  • -h / --help:打印帮助后退出。
  • -i / --immediate:创建完成后直接安装依赖并启动 dev(与交互里「是否现在安装」对应)。
  • --overwrite:目标目录非空时,等价于选择「清空后继续」。
  • --interactive / --no-interactive:强制进入或退出交互;未指定时,一般用 process.stdin.isTTY 判断是否 TTY 交互环境。

注意:短参数 i 对应的是 immediate(立即安装并启动),不是 interactive

核心流程:init() 在做什么?

create-vite 的主体是异步函数 init(),可概括为下面几步(与源码顺序一致,略有合并说明)。

1. 帮助信息

若传入 help,打印 helpMessage(含用法、选项与当前版本可用模板列表)后 return

2. 是否交互

const interactive = argInteractive ?? process.stdin.isTTY

非 TTY(如 CI、部分脚本环境)下往往走非交互分支:缺少信息时用默认值(例如默认目录名、默认模板等),具体以源码为准。

另外,源码里会通过 @vercel/detect-agentdetermineAgent() 判断是否在「AI Agent」环境,并在交互模式下提示可用一条命令非交互创建的方式,便于自动化。

3. 解析项目目录

  • 位置参数 argv._[0] 可作为目标目录(会经 formatTargetDir 清洗非法字符等)。
  • 未传且为交互模式时,用 @clack/prompts 询问 Project name,再得到 targetDir
  • 默认目录名在源码里为 vite-project(非交互且未指定时可能使用该默认)。

4. 目标目录已存在且非空

若目录存在且非空:

  • 传了 --overwrite 则直接清空(保留 .git 的策略见源码 emptyDir)。
  • 否则在交互模式下让用户选:取消 / 清空继续 / 忽略继续;非交互且未 overwrite 则可能直接取消。

5. package.jsonname 字段

packageName 通常取目标文件夹 basename;若不符合 npm 包名规则,会提示修正或自动 toValidPackageName

6. 选择模板(框架 + 变体)

  • 若已通过 -t 传入模板,且该字符串在 TEMPLATES 列表中,则直接使用。
  • 若传入的模板名不合法,交互模式下会提示重新选择;非交互则可能回退到默认模板(如 vanilla-ts,以源码为准)。

交互模式下通常是两步选择

  1. Select a framework:Vue、React、Vanilla 等(FRAMEWORKS 配置驱动)。
  2. Select a variant:如 vue-ts / vuereact-ts / react 等。

部分选项不是内置模板目录,而是 customCommand:选中后会拼好命令(把 TARGET_DIR 换成你的目录),spawn 执行另一条脚手架(例如 npm create vue@latest),然后 process.exit,不再走后面的「拷贝 template-*」逻辑。

7. React Compiler 变体

若模板名包含 react-compiler,会先置 isReactCompiler,并把模板名里的 -compiler 去掉以对应实际 template-* 目录;生成后再跑 setupReactCompiler 往项目里写入 Babel / 插件等相关依赖与配置(版本号以源码为准)。

8. 是否立即安装并启动

immediate 来自 -i / --immediate,或在交互里 confirm「Install with <pkgManager> and start now?」。
非交互且未指定时,一般为 false,只生成文件,最后打印 cd + install + dev 提示。

pkgManager 常从 npm_config_user_agent 解析(例如你在用 pnpm create 时倾向于用 pnpm 安装)。

9. 脚手架落盘:模板目录在哪?

内置模板路径大致为(相对 create-vite 包内入口文件):

../../template-${template}

例如 vue-tstemplate-vue-ts 目录。随后:

  • readdir 模板目录,先拷贝除 package.json 以外的文件
  • index.html 会读模板内容并替换 <title>...</title> 为项目展示名;
  • _gitignore 等特殊文件名会通过 renameFiles 映射为 .gitignore
  • 最后读模板内 package.json,把 name 写成 packageName,再写入目标目录。

10. 安装依赖与启动 dev

immediate 为真:

  • installcross-spawn 同步执行当前包管理器的 install 命令,cwd 为目标项目根目录;测试环境可通过 _VITE_TEST_CLI 跳过真实安装。
  • start:同理执行 dev 脚本。

否则用 prompts.outro 输出「接下来在项目里执行的三条命令」。

与文中示例代码的对应关系

你在文章里贴的 write / install / start 片段,正是上面落盘 + 可选安装启动部分的缩影。阅读源码时建议对照:

常用命令速查

bash
# 交互创建(推荐本地尝鲜)
pnpm create vite

# 指定目录与模板,适合脚本 / CI
pnpm create vite my-app --template vue-ts

# 创建后立刻安装并启动
pnpm create vite my-app --template react-ts --immediate

# 非交互(无 TTY 或显式关闭交互时使用)
pnpm create vite my-app --template vanilla-ts --no-interactive

总结(一条线)

包管理器解析 create vite → 下载并执行 create-vitemri 解析参数 →(TTY 则交互收集目录名、模板、是否立即安装)→ 校验模板 / 或转交 customCommand → 从 template-* 拷贝并改写 package.jsonindex.html → 按需 install + dev 或打印后续命令。

把这条链路记熟后,排查「为什么没进我想要的模板」「为什么 CI 里行为不一样」会直观很多:多半落在 TTY / --no-interactive、模板名是否合法、是否命中 customCommand 提前退出 这几类问题上。

参考文献

以下链接在编写时均可正常访问:

资料说明
create-vite 源码GitHub
Vite:创建项目官方入门

Series

vite

3 / 6