
【从零到一手撕脚手架 | 第五节】自定义命令行下载cli工具
【从零到一手撕脚手架 | 第五节】自定义命令行下载cli工具
Hello大家好我是⛄,之前我们已经成功搭建了一套Vue3的快速开发模板,提高我们搭建新项目的效率,但是当我们的模板逐渐增多,如果依然使用
git clone
的方式去下载模板较为繁琐。为了解决这个问题,我们就可以自己去搭建一个命令行交互式的工具包去生成我们需要的模板。
- GitHub:
> - Vue3开发模板:LonelySnowman/sv3-template
- 自定义cli工具包:LonelySnowman/arceus-cli
- 官方文档:SV3-Family | Vue3
基础结构
- 我们这次的目标就是搭建一个类似于vue-cli,create-react-app等cli工具类似的工具包。要实现的核心功能就是使用命令行交互的效果去生成我们需要的Vue项目模板。
- 首先把项目文件结构创建一下,一步步教大家实现。
用到的依赖
建议大家可以预先去了解下这些依赖的用途和一些基础的使用方法。
Tip:大家注意
chalk
和log-symbols
都要安装4版本,因为高版本已经不支持commonjs
,但是我们最后输出的是commonjs
模块。
- 命令行交互
- commander:解析命令行指令
- ora:终端加载动画
- progress-estimator:终端加载条动画
- log-symbols:终端输出符号
- chalk:终端字体美化
- @inquirer/prompts:终端输入交互
- 打包工具
- rollup(打包工具有很多选择,webpack,vite,rollup,tsup...)
- 这里选用 rollup 是因为它相对更适合 npm 包的打包,并且之前没有使用过,尝试一下,大家也可以选用其他打包工具
- @rollup/plugin-node-resolve:支持rollup打包node.js模块
- @rollup/plugin-commonjs:支持rollup打包commonjs模块
- @rollup/plugin-json:支持rollup打包json文件
- rollup-plugin-typescript2:支持rollup打包ts文件
- @rollup/plugin-terser:压缩打包代码
- rollup-plugin-node-externals:使rollup自动识别外部依赖
- rollup(打包工具有很多选择,webpack,vite,rollup,tsup...)
配置打包命令
- 我们先解决打包的问题,安装好需要的依赖,然后按照下面的配置文件内容,新建一个
rollup.config.js
。
我们还需要在package.json
中配置一个打包命令。
- -c 指定 rollup 配置文件,--bundleConfigAsCjs 将配置转为 commonjs 执行。
编写指令
指令交互
- 这里带大家写一个
create
指令,在我们的入口文件src/index.ts
编写。 - 我们需要用到
commander
,可以帮助我们解析用户在命令行输入的指令。 - 这里会给大家讲解一些基础的用法,更详细的使用方式请查阅官方文档:commander.js。
首先初始化一个Command
对象,传入的参数作为我们的指令名称。
接下来我们就可以配置我们需要的命令了。
- 使用
version
可以实现最基础的查看版本的指令。
- 使用
command
与action
实现自定义指令。- command 为我们需要的命令名称。
- description 为命令添加描述。
- action 为指令触发执行的回调。
- argument 为我们命令需要的参数,[]包裹代表可选,<>包裹代表必填。
下面的示例就是我们编写好的指令,指令回调我们稍后实现,输入arceus update
会打印update command
,输入arceus create test
,会打印create test
。action 回调中会将 argument 中的参数传入。
- 解析指令
输出提示
- 在编写命令代码前我们先封装一个公共方法。
- 在
src/utils/log.ts
中封装一个带icon的输出提示。 - 我们需要用到
log-symbols
,他内置了 error,success,warning,info 对应的 icon ,并且帮我们兼容不支持 icon 的终端。并且后续我们需要用到的ora
作为加载动画,它也是用的log-symbols
进行提示,我们这里保持一致。(大家也可以自定义一些emoji
图标,效果也不错,就是需要自己兼容终端不支持emoji
的情况)
下载项目
我们先实现create
命令,可以让用户选择下载我们预设的模板。
- 在
src/command/create.ts
文件下编写create
命令核心代码。 - 导出一个可以传入项目名称的方法,如果用户直接传入了项目名称则让用户选择模板,否则需要先让用户输入项目名称。
- 这里我们用到了
@inquirer/prompts
,可以帮助我们让用户在终端进行输入或选择的操作,更多使用方法请查阅官方文档:inquirer.js。
- 在
src/utils/file.ts
中封装一个判断用户是否覆盖的公共方法。
- 然后我们就需要让用户选择我们的预设模板,这里在
src/constants.ts
保存我们拥有的模板,定义成map
的形式是方便我们根据key
获取项目的信息。 - 下载模板的方式有很多种,可以将模板文件保存在 SDK 中,使用 ejs 或者其他方法动态选择生成,使用 fs 模块写入,或者存放在 git 仓库中进行 clone,这里我们选用后者,其他方法大家可以自行探索。
- 这里我定义了 TemplateInfo 类型,大家可以根据自己的需要自行定义,需要存储项目名称,下载地址,描述,代码分支。
- 接下来我们就可以让用户选择需要的模板。
- 我们还需要实现我们刚刚使用过的
clone
方法,下载仓库中的模板。 - 我们在
src/utils/clone.ts
中实现下。- 这里我们用到,
simple-git
用于拉取 git 仓库,progress-estimator
设置预估git clone
的时间并展示进度条。
- 这里我们用到,
- 这里我就直接展示代码和注释了,思路都很简单。
- 至此,我们的
create
命令就编写完毕了,我们可以将其添加到src/index.ts
中去调用。
检测项目更新
- 当我们更新模板后,希望用户第一时间用到,可以在用户使用过程中添加一些更新提示。
在src/utils/npm.ts
中编写方法,用于获取npm包的信息及版本号。
npm包信息中包含了该包的最新版本,我们在这里直接引用即可。
然后对比版本号版本,判断是否需要更新,如需更新进行提示。
然后我们将这个判断更新的方法添加到create
方法中。
当我们发布新的版本,用户可以第一时间看到。
如何调用
- 我们已经完成了核心的代码逻辑,现在想要使用命令行去调用我们编写好的逻辑,我们可以先自己在本地执行测试,然后将其上传到 npm 就可以供他人使用了。
本地调试
- 我们之前已经配置好了 rollup 打包的脚本,接下来就可以执行
npm run build
,打包后的代码会输出到dist/index.js
中。 - 我们可以使用
node
在本地执行,先测试一下我们编写好的create
命令。
不出意外是可以看到我们写好的交互逻辑,如果有报错,大家可以根据对应的问题查询下,也可以给我留言。
发布npm包
- 本地调试没有问题后我们就可以将其发布在
npm
上。 - npm账号注册、登录等基础操作,这里就不过多赘述了,主要讲一下如何让发布的包能以
arceus
作为命令名调用。
需要我们修改一下package.json
文件,下面是一些必要的配置,都加上了注释,我们需要重点关注bin
这一项。
bin
中的配置是一个对象,需要有 "key" 和 "value"。- key 会被放置在 node_modules 的 .bin 目录中,value 是 key 对应需要执行的文件。
- 我们使用 npx arceus 就会调用我们的 bin/index.js。
- 当我们全局安装对应包的时候会放在全局的 node_modules 的 .bin 目录中,相当于添加了系统环境变量,这样我们就可以直接在终端中调用。
编写bin/index.js
需要在第一行加入#!/usr/bin/env node
,/usr/bin/env
就是告诉系统可以在PATH目录中查找,#!/usr/bin/env node
就是解决了不同的用户node路径不同的问题,可以让系统动态的去查找node来执行你的脚本文件。
然后我们就可以将npm包发布啦。
发布完成后我们就可以安装npm
全局包然后进行使用啦。
结语
到这里我们就解决了所有的问题,实现了一个简易的cli工具,做到了clone在git仓库中的固定模板。如果我们想实现动态模板就需要使用其他的技术,可以使用字符串插值或者ejs模板,根据用户的选择动态生成需要的模板代码。大家可以根据自己的需要自行拓展😀。
该项目已开源:LonelySnowman/arceus-cli,可以使用npm install arceus-cli -g
安装后使用arceus create
进行体验。
系列文章:
- 【从零到一手撕脚手架 | 第一节】配置基础项目结构 Vite + TypeScrpit + Vue3 初始化项目
- 【从零到一手撕脚手架 | 第二节】模块化封装 降低耦合度 封装 axios pinia router
- 【从零到一手撕脚手架 | 第三节】项目集成CommitLInt+ESLint+Prettier+StyleLint+LintStaged
- 【从零到一手撕脚手架 | 第四节】加速开发效率 使用plop生成开发模板 使用mock进行数据模拟
- 【从零到一手撕脚手架 | 第五节】自定义命令行下载cli工具
参考学习项目:
如果有任何不正确的地方请指正,我会及时更改。
更文不易,如果对你有帮助的话,请给我点个赞吧👍
关注我,后续文章不迷路⛄