本次我们来阅读下 create-vite 源码,先来看下项目的结构
create-vite 目前在 vite 的仓库作为一个子包
我们需要阅读其实只有 src\index.ts 目前代码 630 行核心代码是 init 函数不到 300 行
核心逻辑根据命令行参数或者用户的选择将对应的模板复制到对应的目录中!
template-xxxx 文件是最终需要拷贝的模版
先来看下用到了哪些库
cross-spawn
用于跨平台创建子进程的库,解决了在不同操作系统(如 Windows 和 Unix 系统)上使用 child_process.spawn 时遇到的兼容性问题。
minimist
用于解析命令行参数。
picocolors
用于在终端中给文本上色和格式化,增强命令行输出的可读性 猛击直达 github 仓库
5.5.3 版本发布时由 kolorist 替换为 picocolors
它类似 chalk、kolorist 但根据官方说明无论包体积还是加载速度均遥遥领先
prompts
用于创建命令行交互提示
unbuild
基于rollup的 JavaScript 或 TypeScript 构建和打包工具
使用 minimist 获取命令行参数
create-vite 的主要执行流程全部写在 init 函数中,外部是一些辅助函数、变量
解析命令行参数->根据用户选择获取对应的模版->将模版复制到用户指定的目录->输出提示信息
让我们来看代码
这里导入了所需的依赖,从 picocolors 解构出用于用于命令行输出颜色的函数
使用 minimist 创建一个 argv 对象用于解析命令行参数
然后是一些全局变量
提示信息就是输入 -h or -help 参数后输出的内容
init 函数
minimist 会把命令行参数解析为对象的形式 node index.js --overwrite -h --text=1111 会被解析为 { _: [], overwrite: true, h: true, help: true, text: 1111 }
获取命令行参数
formatTargetDir 函数
不输入任何参数
指定输出目录 node index.js ./11111/ node index.js ./11111 node index.js . 会解析出不同的结果
通过 prompts 进行交互获取用户选择
prompts 是一个轻量、美观、人性化的交互提示库猛击直达仓库
result 存储的是最终的结果,每个交互选项对应一个字段一一对应
prompts.override 的作用是可以预先回答某个交互,这里根据是否传递 --overwrite 参数来设置
可以看到如果传递了 --overwrite 参数即使目录已经存在也不会进行提示
我们继续分析下交互配置,正常情况下只会触发 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 校验名称是否合法,不合法则进行提示
framework
让用户选择框架
可以通过 --template vue 参数直接指定,如果用户指定了 --template or -t 则跳过
TEMPLATES 是所有内置模版的值
argTemplate 的就是 --template or -t 传递的值
判断 argTemplate 是否存在、是否在 TEMPLATES 存在做不同的处理
variant
根据 framework 选择对应的模板
如果传递了 --template or -t 命令行参数
(framework: Framework) => framework && framework.variants ? 'select' : null
最终 type 会为 null 直接跳过 variant 选择
framework 是上一步选择的框架对象数据
最终解析出用户的选择
输入非法项目名 packageName 才会有值
生成项目文件
确定模板
解析包管理器信息
pkgFromUserAgent 函数
执行自定义指令
这部分代码感觉个人感觉不用看
判断选择的模板里是否有自定义指令,如果有就根据不同的包管理器生成执行命令,同步执行将输出打印到控制台
生成项目文件
单独处理 package.json 设置项目名称
如果 isReactSwc 为 true 则需要单独处理
setupReactSwc 函数
输出提示信息
输出项目创建完成后的提示信息,根据包管理器输出不同的安装和启动命令
package.json
然后我们来看下 package.json 的内容
files
files 是发布 npm 包含的文件这里指定了三个 "index.js", "template-*", "dist"
README.md、package.json、LICENSE 会在发布 npm 的时候默认上传,如果不希望某些文件上传,可以使用 .npmignore 文件来排除。
bin
node 会在全局安装的时候将 package.json 的 bin 的配置注册为可执行命令
也就是全局安装后可以使用 create-vite xxx or cva xxx 创建项目
让我们来看下 create-vite 指向的 index.js 文件即输入 create-vite xxx 执行的文件
文件开头的 #!/usr/bin/env node 是用于指定脚本文件的解释器,它指定了使用 env 命令来查找名为 node 的可执行文件,并使用它来运行脚本文件。
文件中的 import './dist/index.mjs' 指向的是通过 unbuild 编译后的 packages/create-vite/src/index.ts 文件
scripts
"scripts": {
"dev": "unbuild --stub", // 快速生成一个存根版本,便于快速测试和迭代
"build": "unbuild", // 打包
"typecheck": "tsc --noEmit", // ts 类型检查
"prepublishOnly": "npm run build" // 执行 `npm publish` 之前自动运行 `npm run build`
},
总结
本文分析了 create-vite 的源码核心逻辑是根据用户的选择将现有的模版复制到对应的目录 init 函数中实现了这一逻辑
使用 minimist 解析命令行参数、使用 picocolors 给命令行输出内容增加颜色、使用 prompts 与用户进行交互获取用户的输入、使用 unbuild 打包
第一次写源码分析不由感叹能读明白是一回事能写的让别人看明白又是另一回事!