You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Targetdirectoryvite-projectisnotempty.Removeexistingfilesandcontinue?(y/n): Y
你可以选择通过输入 y 或 n 来告知 CLI 是否要清空该目录。并且,如果此时你输入的是 n,即不清空该文件夹,那么整个 CLI 的执行就会退出。这个过程对应的代码:
if(existing.length){const{ yes }=awaitprompt({type: 'confirm',name: 'yes',initial: 'Y',message:
`Target directory ${targetDir} is not empty.\n`+`Remove existing files and continue?`})if(yes){emptyDir(root)}else{return}}
2. 确定项目模版
在创建好项目文件夹后,CLI 会获取 --template 选项,即当我们输入这样的命令时:
npminit @vitejs/app--template文件夹名
如果 --template 选项不存在(即 undefined),则会询问要选择的项目模版:
lettemplate=argv.t||argv.templateif(!template){const{ t }=awaitprompt({type: "select",name: "t",message: "Select a template:",choices: TEMPLATES})template=stripColors(t)}
console.log(`\nDone. Now run:\n`)if(root!==cwd){console.log(` cd ${path.relative(cwd,root)}`)}console.log(` npm install (or \`yarn\`)`)console.log(` npm run dev (or \`yarn dev\`)`)console.log()
结语
虽然 Vite 的 create-app CLI 的实现仅仅只有 160 行的代码,但是它也较为全面地考虑了创建项目的各种场景,并做对应的兼容处理。简而言之,十分小而美。所以,我相信大家经过学习 Vite 的 create-app CLI 的实现,都应该可以随手甩出(实现)一个 CLI 的代码 😎 ~
The text was updated successfully, but these errors were encountered:
前言
前段时间,尤雨溪回答了一个广大网友都好奇的一个问题:Vite 会不会取代 Vue CLI?
答案是:是的!
那么,你开始学 Vite 了吗?用过 Vite 的同学应该都熟悉,创建一个 Vite 的项目模版是通过
npm init @vitejs/app
的方式。而npm init
命令是在[email protected]
开始支持的,实际上它是先帮你安装 Vite 的@vitejs/create-app
包(package),然后再执行create-app
命令。至于
@vitejs/create-app
则是在 Vite 项目的packages/create-app
文件夹下。其整体的目录结构:Vite 的
create-app
CLI(以下统称为create-app
CLI)具备的能力不多,目前只支持基础模版的创建,所以全部代码加起来只有 160 行,其整体的架构图:可以看出确实非常简单,也因此
create-app
CLI 是一个很值得入门学习如何实现简易版 CLI 的例子。那么,接下来本文将会围绕以下两个部分带着大家一起通过
create-app
CLI 来学习如何实现一个简易版的 CLI:create-app
中使用到的库(minimist
、kolorist
)逐步拆解、分析
create-app
CLI 源码create-app CLI 中使用到的库
create-app
CLI 实现用到的库(npm)确实很有意思,既有我们熟悉的enquirer
(用于命令行的提示),也有不熟悉的minimist
和kolorist
。 那么,后面这两者又是拿来干嘛的?下面,我们就来了解一番~minimist
minimist
是一个轻量级的用于解析命令行参数的工具。说起解析命令行的工具,我想大家很容易想到commander
。相比较commander
而言,minimist
则以轻取胜!因为它只有 32.4 kB,commander
则有 142 kB,即也只有后者的约 1/5。那么,下面我们就来看一下
minimist
的基础使用。例如,此时我们在命令行中输入:
那么,在
index.js
文件中可以使用minimist
获取到输入的myproject
参数:这里的
argv
是一个对象,对象中_
属性的值则是解析node index.js
后的参数所形成的数组。kolorist
kolorist
是一个轻量级的使命令行输出带有色彩的工具。并且,说起这类工具,我想大家很容易想到的就是chalk
。不过相比较chalk
而言,两者包的大小差距并不明显,前者为 49.9 kB,后者为 33.6 kB。不过kolorist
可能较为小众,npm 的下载量大大不如后者chalk
,相应地chalk
的 API 也较为详尽。同样的,下面我们也来看一下
kolorist
的基础使用。例如,当此时应用发生异常的时候,需要打印出红色的异常信息告知用户发生异常,我们可以使用
kolorist
提供的red
函数:又或者,可以使用
kolorist
提供的stripColors
来直接输出带颜色的字符串:逐步拆解、分析 create-app CLI 源码
了解过 CLI 相关知识的同学应该知道,我们通常使用的命令是在
package.json
文件的bin
中配置的。而create-app
CLI 对应的文件根目录下该文件的bin
配置会是这样:可以看到
create-app
命令则由这里注册生效,它指向的是当前目录下的index.js
文件。并且,值得一提的是这里注册了 2 个命令,也就是说我们还可以使用cva
命令来创建基于 Vite 的项目模版(想不到吧 😲)。而
create-app
CLI 实现的核心就是在index.js
文件。那么,下面我们来看一下index.js
中代码的实现~基础依赖引入
上面我们也提及了
create-app
CLI 引入了minimist
、enquire
、kolorist
等依赖,所以首先是引入它们:其中,
fs
和path
是 Node 内置的模块,前者用于文件相关操作、后者用于文件路径相关操作。接着就是引入minimist
、enquirer
和kolorist
,它们相关的介绍上面已经提及,这里就不重复论述~定义项目基础模版(颜色)和文件
从
/packages/create-app
目录中,我们可以看出create-app
CLI 为我们提供了 9 种项目基础模版。并且,在命令行交互的时候,每个模版之间的颜色各有不同,即 CLI 会使用kolorist
提供的颜色函数来为模版定义好对应的颜色:其次,由于
.gitignore
文件的特殊性,每个项目模版下都是先创建的_gitignore
文件,在后续创建项目的时候再替换掉该文件的命名(替换为.gitignore
)。所以,CLI 会预先定义一个对象来存放需要重命名的文件:定义文件操作相关的工具函数
由于创建项目的过程中会涉及和文件相关的操作,所以 CLI 内部定义了 3 个工具函数:
copyDir 函数
copyDir
函数用于将某个文件夹srcDir
中的文件复制到指定文件夹destDir
中。它会先调用fs.mkdirSync
函数来创建制定的文件夹,然后枚举从srcDir
文件夹下获取的文件名构成的数组,即fs.readdirSync(srcDir)
。其对应的代码如下:
copy 函数
copy
函数则用于复制文件或文件夹src
到指定文件夹dest
。它会先获取src
的状态stat
,如果src
是文件夹的话,即stat.isDirectory()
为true
时,则会调用上面介绍的copyDir
函数来复制src
文件夹下的文件到dest
文件夹下。反之,src
是文件的话,则直接调用fs.copyFileSync
函数复制src
文件到dest
文件夹下。其对应的代码如下:
emptyDir 函数
emptyDir
函数用于清空dir
文件夹下的代码。它会先判断dir
文件夹是否存在,存在则枚举该问文件夹下的文件,构造该文件的路径abs
,调用fs.unlinkSync
函数来删除该文件,并且当abs
为文件夹时,则会递归调用emptyDir
函数删除该文件夹下的文件,然后再调用fs.rmdirSync
删除该文件夹。其对应的代码如下:
CLI 实现核心函数
CLI 实现核心函数是
init
,它负责使用前面我们所说的那些函数、工具包来实现对应的功能。下面,我们就来逐点分析init
函数实现的过程:1. 创建项目文件夹
通常,我们可以使用
create-app my-project
命令来指定要创建的项目文件夹,即在哪个文件夹下:其中,
argv._[0]
代表create-app
后的第一个参数,root
是通过path.join
函数构建的完整文件路径。然后,在命令行中会输出提示,告述你脚手架(Scaffolding)项目创建的文件路径:当然,有时候我们并不想输入在
create-app
后输入项目文件夹,而只是输入create-app
命令。那么,此时tagertDir
是不存在的。CLI 则会使用enquirer
包的prompt
来在命令行中输出询问:你可以在这里输入项目文件夹名,又或者直接回车使用 CLI 给的默认项目文件夹名。这个过程对应的代码:
接着,CLI 会判断该文件夹是否存在当前的工作目录(
cwd
)下,如果不存在则会使用fs.mkdirSync
创建一个文件夹:反之,如果存在该文件夹,则会判断此时文件夹下是否存在文件,即使用
fs.readdirSync(root)
获取该文件夹下的文件:这里
existing
会是一个数组,如果此时数组长度不为 0,则表示该文件夹下存在文件。那么 CLI 则会询问是否删除该文件夹下的文件:你可以选择通过输入
y
或n
来告知 CLI 是否要清空该目录。并且,如果此时你输入的是n
,即不清空该文件夹,那么整个 CLI 的执行就会退出。这个过程对应的代码:2. 确定项目模版
在创建好项目文件夹后,CLI 会获取
--template
选项,即当我们输入这样的命令时:如果
--template
选项不存在(即undefined
),则会询问要选择的项目模版:由于,
TEMPLATES
中只是定义了模版的类型,对比起packages/create-app
目录下的项目模版文件夹命名有点差别(缺少template
前缀)。例如,此时template
会等于vue-ts
,那么就需要给template
拼接前缀和构建完整目录:所以,现在
templateDir
就会等于当前工作目录 +template-vue-ts
。3. 写入项目模版文件
确定完需要创建的项目的模版后,CLI 就会读取用户选择的项目模版文件夹下的文件,然后将它们一一写入此时创建的项目文件夹下:
由于通过
fs.readdirSync
函数返回的是该文件夹下的文件名构成的数组 ,所以这里会通过for of
枚举该数组,每次枚举会调用write
函数进行文件的写入。而
write
函数则接受两个参数file
和content
,其具备两个能力:对指定的文件
file
写入指定的内容content
,调用fs.writeFileSync
函数来实现将内容写入文件复制模版文件夹下的文件到指定文件夹下,调用前面介绍的
copy
函数来实现文件的复制write
函数的定义:并且,值得一提的是
targetPath
的获取过程,会针对file
构建完整的文件路径,并且兼容处理_gitignore
文件的情况。在写入模版内的这些文件后,CLI 就会处理
package.json
文件。之所以单独处理package.json
文件的原因是每个项目模版内的package.json
的name
都是写死的,而当用户创建项目后,name
都应该为该项目的文件夹命名。这个过程对应的代码会是这样:最后,CLI 会输出一些提示告诉你项目已经创建结束,以及告诉你接下来启动项目需要运行的命令:
结语
虽然 Vite 的
create-app
CLI 的实现仅仅只有 160 行的代码,但是它也较为全面地考虑了创建项目的各种场景,并做对应的兼容处理。简而言之,十分小而美。所以,我相信大家经过学习 Vite 的create-app
CLI 的实现,都应该可以随手甩出(实现)一个 CLI 的代码 😎 ~The text was updated successfully, but these errors were encountered: