阅读 2744

搞一个Node Cli来提升工作效率

为什么要搞

我在1月发表过一篇:从0搭建自动版本管理的Vue组件库,是一篇搭建组件库的todo类型的文章,包括组件库的搭建、配置、打包、发布。随着公司前端基建越来越完善,我们会产出越来越多的像这样的前端库,而且类型越来越多:组件库、js库、css库,每个库都需要去从零开始搞搭建配置打包发布,这种重复劳动显然是不合理的,毕竟懒才是第一生产力,那么有没有解决这个问题的好法子呢?于是我想到了Node Cli,安装更新简单、流程统一规范、后续功能可挖掘潜力大,取代了重复劳动,让前端组的同学有更多的时间focus on coding,大喊真香。

目标

在搞之前弄清楚目标可以有效避免做到一半遇到:我是谁?我在哪?我在干嘛?的问题,我们先来看看我们的cli需要解决哪些具体的问题,我例举几个我工作中遇到的问题:

  1. 按规则压缩构建产物:库除了要上传到npm以外,还需要上传到静态资源服务器以供一些老旧的项目直接通过标签引入,需要按照一定的路径规则将构建产物压缩成zip包上传(类似/lib/front/projectName/version/xxxx.umd.js),这样每个项目都要去搞一套打包前删zip包打包完成后生成zip包的逻辑。
  2. 版本控制:从0搭建自动版本管理的Vue组件库里给大家介绍过在项目中使用npm version对项目进行版本控制,每个项目都要搞一份,在复制第二份的时候我就忍不了了,得抽出来。
  3. 发布配置:每个项目都需要一份发布的配置,用于给发布平台执行,有可能需要手动修改里面的版本号避免发布失败。
  4. tag:我们上传静态资源服务器时是可以覆盖文件的,这样有利有弊,好处是库的发布效率很高,缺点是不够安全,需要一套机制来规避风险,为我们自动打tag,小于等于tag版本的库就老老实实升级布丁版本更新吧。
  5. 初始化库、添加模版组件:每次搭建库时都得从头开始配置webpack、项目结构、tsconfig、lint等,虽然可以复制现有的库做一下修枝剪叶,但哪有cli直接生成一份香。

总结一下,我们这个Node Cli需要解决:压缩构建产物、自动化版本控制、拉取标准发布配置、自动打tag、下载库模版或者组件模版。

以上是我遇到的问题,同学们最好还是结合自己的业务场景,看看Node Cli能不能帮你干掉一些重复性工作。

开搞

基础框架搭建

Node Cli的原理是使用npm i -g cli-name安装后把包装到全局的node_modules中,在命令行中执行指令后系统会按照node的环境变量去执行对应脚本。

在cli项目的package.json中加入:

{
    ...
    "bin": {
        "mycli": "./bin/mycli.js"
    }
    ...
}
复制代码

这样当其他同学全局安装时,就能直接使用mycli指令执行mycli.js这个脚本了。

我们本地开发时可以使用npm link将项目链接到包文件夹,这样就能方便调试,不用每次都重建。

接下来看一下mycli.js,在文件顶部添加#!/usr/bin/env node,目的是解决不同的用户node路径不同的问题,让系统动态的去查找node来执行这个脚本。

这里我们要依赖一些优秀的第三方包了,这里我选择Commander.js,它是node.js命令行接口的完整解决方案

#!/usr/bin/env node

const { program } = require('commander')

const { resolve } = require('path')

const res = (command) => resolve(__dirname, '../src/', command) // 读取脚本路径

const version = require('../package.json').version

program.version(version, '-v, --version') // 执行mycli -v 或 mycli --version的时候输出当前版本号

program.command('init').description('init lib').action(() => { // init脚本的描述与执行
    require(res('init'))
})

program.command('build').description('build lib').action(() => { // build脚本的描述与执行
    require(res('build'))
})

program.command('release').description('release lib').action(() => { // release脚本的描述与执行
    require(res('release'))
})

program.parse(process.argv) // 处理输出参数
复制代码

ok,到此为止cli的基本框架就搭好了,接下来就是丰富它的功能。

丰富功能

在功能越来越多之后,我们的cli本身也可能会遇到体量庞大代码结构复杂的问题,这时候我们可以将单独的功能独立打成npm包,由cli引入这些包,插件化方案可以参考云音乐前端技术团队的文章:Node CLI 工具的插件方案探索(向大佬学习)。

上文提到的需要解决的问题里,我将使用以下三个脚本来解决,主要说一下每个脚本实现的思路,同学们还是根据业务需要自己实现脚本,想象力空间还是比较大的。

  1. init: 下载库模版或者组件模版
  2. build: 压缩构建产物、自动化版本控制
  3. release: 拉取标准发布配置、自动打tag

先推荐几个第三方库,可以帮助我们更好更快的实现功能:

功能 说明
request http模块
inquirer 强大的用户命令行交互工具,vue-cli也用的它
shelljs 在node脚本中执行shell命令
chalk 脱离黑白cli,给输出加点颜色
ora 命令行的菊花图,执行耗时操作时我们不希望用户以为cli卡死了
compressing 用来压缩的
node-emoji 给命令行加一些emoji增加一些趣味(???)

init

解决问题:下载库模板和下载组件模板

我们可以把预设好的库的模板就放在我们cli的仓库里,或者放在专门的模板仓库里,然后通过gitlab的API去获取下载,在使用gitlab API之前需要去账号设置里搞来一份private_token,调用API的时候带上这个参数给gitlab做身份验证,然后利用API递归的读取gitlab仓库里的文件,对应的写入到当前目录下,就完成了库模板的下载,期间可以用ora搞点菊花图,用node-emoji多整几个绿色的勾勾增加一下用户体验。

build

解决问题:压缩构建产物、自动化版本控制

使用inquirer跟用户交互,可以向用户提问:使用哪种npm version升级策略?打包的项目类型?库的名称?多入口的库选择入口文件?打包输出路径?可选的执行脚本?

在cli中我们是可以通过require(resolve('pathTo/package.json'))去读取用户的package.json的,可以获取用户项目的名称、版本等信息,这些信息可以作为inquirer提问的默认值,毕竟每次打包都要用户输入的话体验太差了,一路回车火花带闪电肯定才是最爽的。既然可以获取到用户的package.json,那我们就可以做一些约定的配置,比如约定用户的package.json里config.cli这个对象就是专门给我们cli读取使用的参数对象,用户可以写一些定制化的配置。

我在项目中实践的方案是使用inquirer获得的项目信息去拼凑出vue或者webpack的打包命令(公司主要用vue),例如vue组件库的构建,最终会执行

npx vue-cli-service build --target lib --name name --dest path
复制代码

webpack构建会执行

webpack --output-path path
复制代码

在基础的构建命令之上可以从约定的config.cli中读取用户的配置,例如用户有不同环境打包的需要就可以添加env参数

打包后根据用户package.json的name、version等信息,把构建产物放进/lib/front/projectName/version/xxxx.umd.js然后用compressing压缩成zip包

release

解决问题: 拉取标准发布配置、自动打tag

拉取标准发布配置和init类似,都是去下载一份标准的配置,可以根据用户的package.json进行一些改动再写入。

自动打tag:我为release指令提供了一个可选参数tag,可以通过process.argv读取,读取到tag参数后,使用gitlab API为对应分支打一个tag,后续build和release都可以先检查这个tag版本和用户package.json的版本,提醒用户不要忘记升级版本。

...

cli可以做的事情还有很多,只要想象力足够并且业务有需要,能节省重复劳动时间,都可以集成进cli作为cli的功能使用。

总结

本文向大家介绍了Node Cli的使用场景和基本的框架搭建,提供了几个功能的实现思路。

主要是给出自己在公司前端基建推进过程中遇到的问题和解决问题的方案,并不是业界最佳方案,希望大家如果有更好的方案和我讨论,上述的方案不一定每个公司每个业务都通用,想要借鉴的同学还是建议从公司业务出发搞一份适合自己公司的cli。

我是suhangdev,欢迎与我交流前端相关话题, 邮箱17816876697@163.com,如果文章对你有帮助,请点赞支持噢,谢谢!