create-vite 源码阅读

262 阅读5分钟

本次我们来阅读下 create-vite 源码,先来看下项目的结构

create-vite 目前在 vite 的仓库作为一个子包

image.png

我们需要阅读其实只有 src\index.ts 目前代码 630 行核心代码是 init 函数不到 300 行

核心逻辑根据命令行参数或者用户的选择将对应的模板复制到对应的目录中!

template-xxxx 文件是最终需要拷贝的模版

猛击直达官方仓库

先来看下用到了哪些库

image.png

cross-spawn

用于跨平台创建子进程的库,解决了在不同操作系统(如 Windows 和 Unix 系统)上使用 child_process.spawn 时遇到的兼容性问题。

minimist

用于解析命令行参数。

picocolors

用于在终端中给文本上色和格式化,增强命令行输出的可读性 猛击直达 github 仓库

5.5.3 版本发布时由 kolorist 替换为 picocolors

它类似 chalk、kolorist 但根据官方说明无论包体积还是加载速度均遥遥领先

image.png

prompts

用于创建命令行交互提示

image.png

unbuild

基于rollup的 JavaScript 或 TypeScript 构建和打包工具

猛击查看完整代码

使用 minimist 获取命令行参数

create-vite 的主要执行流程全部写在 init 函数中,外部是一些辅助函数、变量

解析命令行参数->根据用户选择获取对应的模版->将模版复制到用户指定的目录->输出提示信息

让我们来看代码

这里导入了所需的依赖,从 picocolors 解构出用于用于命令行输出颜色的函数

image.png

使用 minimist 创建一个 argv 对象用于解析命令行参数

image.png

然后是一些全局变量

image.png

提示信息就是输入 -h or -help 参数后输出的内容

image.png

init 函数

minimist 会把命令行参数解析为对象的形式 node index.js --overwrite -h --text=1111 会被解析为 { _: [], overwrite: true, h: true, help: true, text: 1111 }

image.png

获取命令行参数

image.png

formatTargetDir 函数

image.png

不输入任何参数

image.png

指定输出目录 node index.js ./11111/ node index.js ./11111 node index.js . 会解析出不同的结果

image.png

通过 prompts 进行交互获取用户选择

prompts 是一个轻量、美观、人性化的交互提示库猛击直达仓库

result 存储的是最终的结果,每个交互选项对应一个字段一一对应

image.png

prompts.override 的作用是可以预先回答某个交互,这里根据是否传递 --overwrite 参数来设置

image.png

可以看到如果传递了 --overwrite 参数即使目录已经存在也不会进行提示

image.png

我们继续分析下交互配置,正常情况下只会触发 projectName、framework、variant

projectName

让用户输入项目名称,如果命令行已经传递则跳过

{
  type: argTargetDir ? null : 'text', // 如果命令行已指定目标目录则跳过此提示
  name: 'projectName',
  message: reset('Project name:'), // 重置命令行输出的文字颜色
  initial: defaultTargetDir, // 默认值 'vite-project'
  onState: (state) => {
    // state 输入的值 使用 formatTargetDir 格式化,如果 state 为空则使用默认值
    targetDir = formatTargetDir(state.value) || defaultTargetDir
  },
},

packageName

校验项目名称 projectName 是否合法,合法就直接通过否则通过 toValidPackageName函数转换名称作为默认值,让用户重新输入

validate 使用 isValidPackageName 校验名称是否合法,不合法则进行提示

iShot_2024-11-05_11.31.03.gif

image.png

framework

让用户选择框架

可以通过 --template vue 参数直接指定,如果用户指定了 --template or -t 则跳过

TEMPLATES 是所有内置模版的值

image.png

argTemplate 的就是 --template or -t 传递的值

image.png

判断 argTemplate 是否存在、是否在 TEMPLATES 存在做不同的处理

image.png

variant

根据 framework 选择对应的模板

如果传递了 --template or -t 命令行参数

(framework: Framework) => framework && framework.variants ? 'select' : null

最终 type 会为 null 直接跳过 variant 选择

framework 是上一步选择的框架对象数据

image.png

image.png

最终解析出用户的选择

image.png

输入非法项目名 packageName 才会有值

image.png

生成项目文件

确定模板

image.png

解析包管理器信息

image.png

pkgFromUserAgent 函数

image.png

执行自定义指令

这部分代码感觉个人感觉不用看

判断选择的模板里是否有自定义指令,如果有就根据不同的包管理器生成执行命令,同步执行将输出打印到控制台

image.png

image.png

生成项目文件

image.png

单独处理 package.json 设置项目名称

image.png

如果 isReactSwc 为 true 则需要单独处理

image.png

setupReactSwc 函数

image.png

输出提示信息

输出项目创建完成后的提示信息,根据包管理器输出不同的安装和启动命令

image.png

image.png

package.json

然后我们来看下 package.json 的内容

files

image.png

files 是发布 npm 包含的文件这里指定了三个 "index.js", "template-*", "dist"

image.png

README.md、package.json、LICENSE 会在发布 npm 的时候默认上传,如果不希望某些文件上传,可以使用 .npmignore 文件来排除。

bin

image.png

node 会在全局安装的时候将 package.json 的 bin 的配置注册为可执行命令

也就是全局安装后可以使用 create-vite xxx or cva xxx 创建项目

让我们来看下 create-vite 指向的 index.js 文件即输入 create-vite xxx 执行的文件

文件开头的 #!/usr/bin/env node 是用于指定脚本文件的解释器,它指定了使用 env 命令来查找名为 node 的可执行文件,并使用它来运行脚本文件。

image.png

文件中的 import './dist/index.mjs' 指向的是通过 unbuild 编译后的 packages/create-vite/src/index.ts 文件

image.png

scripts

"scripts": {
    "dev": "unbuild --stub", // 快速生成一个存根版本,便于快速测试和迭代
    "build": "unbuild", // 打包
    "typecheck": "tsc --noEmit", // ts 类型检查
    "prepublishOnly": "npm run build" // 执行 `npm publish` 之前自动运行 `npm run build`
},

image.png

总结

本文分析了 create-vite 的源码核心逻辑是根据用户的选择将现有的模版复制到对应的目录 init 函数中实现了这一逻辑

使用 minimist 解析命令行参数、使用 picocolors 给命令行输出内容增加颜色、使用 prompts 与用户进行交互获取用户的输入、使用 unbuild 打包

第一次写源码分析不由感叹能读明白是一回事能写的让别人看明白又是另一回事!