面向模板生成的 cli 应该如何进行技术设计

在前端开发的过程中,我们经常会用到各种 cli,vue-create、 create-reat-app 等,在业务项目中,面向需要创建多个同类型仓库的需求,也需要开发对应的 cli 工具来实现,本文按照使用流程,介绍 cli 工具的技术设计和相关思考。

安装 cli

cli 本身的 npm包 可以全局安装和使用,但是全局安装会带来维护上的问题,使用者如果使用的是老版本的 cli,可能遇到有 bug 的版本,基于此考量,在文档中需要提示用户使用 npx

1
npx xx-cli init

npx 使用时会将包安装到一个临时目录,使用完成后将其删除

模板仓库

创建模板需要拉取模板仓库,假设我们有这样一个模板库列表

1
2
3
4
- foo
- bar
- baz
- common-utils

模板库中的 foo、bar、baz 仓库(模板仓库)同时依赖的 common-utils(通用工具函数),此时通常的解决方案是将这四个仓库做成一个 monorepo,这样 common-utils 中内容发生变更时,依赖了该内容的仓库就会将自己的 package.json 中的依赖进行升级。

模板的拉取

模板的拉取可选的方案有两种:git pull or npm install
这两种方式在处理模板内容拉取上各有利弊,开源项目中 react 使用的是 git pull 的方式,yeoman 使用 npm install 的形式处理模板仓库的拉取,个人看来,通过 git pull 这种方式会将 git 仓库管理 与 模板版本 相绑定,更适合对 git 仓库本身的管理就比较严格的项目;使用 npm install 则可以将仓库管理与版本发布相隔离,权限控制的颗粒度可以做的更细,同时,创建模板时使用 npm install 不需要下载非目标模板需要的文件,对于模板仓库维护在一个仓库内的项目比较友好,最后,基于 npm 包的管理方式有明确的「版本」概念,后续可以增加基于版本的模板安装功能。综上,此处推荐使用 npm install,下文中的方案延续此技术选择。

模板列表

项目初始化时,通常需要维护一个列表,用于用户选择初始化那种类型的模板,这个模板列表也可以维护在 monorepo 中而非 cli 原始项目,这样新增模板与 cli 本身的维护就可以做到解耦了,使用时仅需通过 unpkg 等工具从 npm 源获取对应文件即可。

1
2
3
4
5
- package-list
- foo
- bar
- baz
- common-utils

初始化

初始化完成后就需要考虑 git 的问题了,有些业务中,通过模板创建的项目是相对独立的,有些则是会把这些项目统一管理到一个项目,此时需要增加对 git 的判断逻辑:

  • 递归父目录,如果父目录中有 .git 文件夹,则代表是 git 仓库,不需要进行 git init
  • 递归到 / 根目录之后依然没有找到,则说明当前仓库自身就是一个仓库,可以直接执行 git init

另外,如果执行了 git init,需要为用户执行一次 git commit ,因为通常用户在初始化好一个项目之后自己也应该将对应的改动进行 commit,否则无法直接看出首次提交里某一处的变更是模板本身的定义还是单独对模板的修改

依赖安装

部分 cli 的模板仓库中是没有 lock 文件的,例如 create-react-app ,这样做的好处是可以由用户自行选择 npm 包管理方案,但是对于业务团队来说,通常会约定统一使用 npm / yarn,此时对于模板仓库也可以将 lock 文件添加进去,这样的好处是不需要担心某个依赖包的升级对模板可用性的影响

运行

安装好之后,下一步是运行 dev 命令开始开发,这个 dev 命令可以选择在全局安装一个 cli 的包,也可以选择在模板中内置一个运行 dev 流程的包,推荐选择后者,因为前者会存在老版本升级不及时 和 不同版本 cli init 的仓库 dev 对模板代码的依赖可能不同的问题

参考