还在手动git add, git commit ? 自动化发布node脚本来拯救你!(搭建react组件库2 --- node发布脚本)

4,088 阅读6分钟

前言

兄弟们,这个东西是真的真的好用,为什么需要自动化发布脚本呢? 如果你们目前的项目是这样push代码的,就有大问题:

git add -A
git commit -m "xxx"
git push
  • 首先,你push版本前是不是要手动修改package.json里的版本号?是不是很麻烦?
  • 其次,代码如果要打tag,你是不是要手动git tag vXXX还要git push origin tag把tag单独推到git仓库?
  • 还有,你commit不符合angular规范咋办(比如修改bug是'fix:' 开头, 新增功能是'feat:' 开头)
  • 还有,修改了commit之后如何自动生成changelog?
  • 最重要的是你如果自己写bash 脚本,如何保证你代码的拓展性,比如我要加减一个流程,你必须面向过程编码去改bash脚本,而不能轻易的抽象出一个可配置化的流程脚本。

项目地址,开源不易,跪谢start

github.com/lio-mengxia…

我后面还会出在很多开源组件库的基础上封装一套自己的react组件库和各种工具轮子,不论这些你是否用得到(我自己写的@mx-design/cli来启动项目真的很好用,有时候跑别的项目的demo都是用的这个自己造的轮子)我觉得一起学习交流的意义还是很大的

看看这个npm包咋样

这是我自己写的一个可配置化的node发布脚本,先看看效果:

我在自己项目的根目录建立一个scripts文件夹,下面建立一个release.ts 脚本

import defaultRelaseProcess from '@mx-design/release';
defaultRelaseProcess();

package.json

  "scripts": {
    // 不用担心ts-node依赖,@mx-design/release包会帮你安装
    "release": "ts-node ./scripts/release.ts"
  },
   "devDependencies": {
    "@mx-design/release": "2.2.19",
  },

然后命令行界面到你的项目根目录,输入:

npm run release

然后你的命令行界面会显示如下: 1、询问你要升级的版本

34182EEF-6E27-41B9-AE4F-41C6AFEFB6E7.png

看到了吧,你可以自由选择是主版本升级,还是次版本,或者其他版本,都是符合semver规范的。

2、选择后会让你填写commit信息,如果你的commit的信息不符合angular提交规范,会报错提示你不符合规范,并且终止脚本。(整体交互很舒服,比如你git push的时候,我们会显示正在push loading,push成功,就会覆盖当时正在push的文字,颜色标绿并显示对钩的图标)

image.png

交互loading如下:

image.png

如果命令执行错误,显示如下(国内github环境非常差,经常git push失败,用vpn还是一样的):

109CAAC1-709A-4686-86A9-AF64D960E984.png

git push成功效果如下:

C74EFAE8-C49A-447F-8E14-093B2ACBD947.png

3、这里release的主要流程完成如下:

4E008797-7E12-4619-B39A-A3C8E131B4C1.png

流程包括:

  • 选择升级版本 ->
  • 修改package.json的版本号 ->
  • 填写commit信息(校验格式,不通过终止) ->
  • 推送到代码仓库(推送失败会帮你把package.json里的版本回退) ->
  • 生成changeLog ->
  • build打包(这里执行的是npm run build 所以你的scripts里必须有build命令,要不执行失败) ->
  • 发布包(执行的 npm publish --access=public 命令) ->
  • 打tag并且推送到git仓库

自定义流程

上面几个流程是这个包自带流程顺序,你可以配自己的顺序,

import { getNextVersion, gitPush, setChangelog, build, publishNpm, addTag, compose } from '@mx-design/release';

// getNextVersion 获取下个版本号函数
// updateVersion 修改版本号 返回值是回退版本号的函数
// gitPush push代码函数
// setChangelog 设置changeLog函数
// build 执行npm run build函数
// publish 执行npm publish函数
// addTag 打tag函数
// compose函数组合器

// 使用方法,这些函数顺序可以自己换位置,或者增删减
const middle = [getNextVersion, updateVersion, gitPush, setChangelog, build, publishNpm, addTag];

function execRelease() {
  compose(middle);
}

寻求共建 + 源码分析

本项目随时欢迎任何有兴趣的同学加入,评论区写自己github账号,或者issue上留言,我会第一时间加你到核心开发成员列表。

这个项目还缺少一些东西,比如eslint、stylelint校验,自定义changelog位置,增加单元测试,加入git workflow 等等,我没那么多时间做,因为今年还有很多任务排着呢。。。

源码分析

目录含义:

- 根目录
 - src
   - addTag 
       - index.ts // 打tag函数
   - build
       - build.ts // build项目函数
   - config // 配置文件夹
       - functions 工具函数
       - contstants 常量
   - gitPush
       - index.ts // push到git函数
   - publishNpm
       - index.ts // publish 整个项目的函数
   - selectNextVersion
       - index.ts 选择下个版本号函数
   - setChangeLog
       - 打changeLog函数
 package.json
 tsconfig.json
 ... 普通文件没啥好讲的

核心中间件执行器compose,这个函数的灵感来源于koa框架,说白了就是一个中间件组合函数,并且能共享数据,代码如下,后面我们会举个例子,来看它的执行机制:

export function compose(middleware) {
  // 共享数据仓库,每个函数都可以往里面加数据,所有函数都可以访问到里面的数据
  const otherOptions = {}; 
  // 函数发射器,一个函数执行完毕调用next(),就执行下一个函数
  function dispatch(index, otherOptions) {
    // 每次从middleware中间件里面选取一个函数执行,直到执行了全部函数
    if (index == middleware.length) return;
    const currMiddleware = middleware[index];
    // 执行函数,传入了一下一个函数的dispatch,已经更新共享数据
    currMiddleware((addOptions) => {
      dispatch(++index, { ...otherOptions, ...addOptions });
    }, otherOptions).catch((error) => {
      console.log('💣 发布失败,失败原因:', error);
    });
  }
  dispatch(0, otherOptions);
}

middleware写法

// 获取版本号函数,固定参数第一个是next,表示调用middleware里下一个函数
const getNextVersion = async (next) => {
  // _selectNextVersion会查看你package.json里的version参数是啥版本
  const nextVersion = await _selectNextVersion();
  // 缓存老的package.json数据,为了后面可以回退版本有老版本数据做准备
  const originPackageJson = getOriginPackageJson();
  // next传入的数据都会放到共享数据仓库,下面的函数都可以拿到这些数据
  next({
    nextVersion,
    originVersion: originPackageJson.version,
    originPackageJson,
  });
};

// 更新版本号方法
const updateVersion = async (next, otherOptions) => {
  if (!otherOptions?.nextVersion) {
    throw new Error('请传入package.json新版本号');
  }
  // 返回一个回退版本号的方法,上面的函数共享的originPackageJson数据就是给他用的
  // basicCatchError如果命令执行失败,就打印失败原因,并且返回false
  const backVersionFn = await _updateVersion(otherOptions.nextVersion, otherOptions.originPackageJson).catch(
    basicCatchError,
  );
  // 把回退版本的函数共享出来,给下面需要用的地方自己调用
  next({ backVersionFn });
};

案例说明

compose([getNextVersion, updateVersion])

首先compose会调用

  • dispatch(0, otherOptions)
  • 0代表compose传参的middleware数组里的第一项
  • otherOptions代表共享数据,目前是空对象

接着, 第一个函数调用

  • getNextVersion((addOptions) => { dispatch(++index, { ...otherOptions, ...addOptions }); }, otherOptions);
  • 看第一个参数,我们是这么写的
  • const getNextVersion = async (next) => {
  • 也就是说第一个参数next调用后,实际上是调用dispatch函数,dispatch函数的参数index + 1,就调用了middleware数组里,第二个函数了
  • dispatch第二个参数就是把next想共享的数据发下去

好了源码分析完了,我今年主要是任务是写react pc和移动端组件库,之前写了cli打包和开发的脚手架,文章如下: juejin.cn/post/707518…

这个项目也是,想当核心贡献者,直接留言,把github账号说下,第一时间加你进来,多谢点个star哦 现在完成了发布脚本,接着就是搭建组件库展示网站了,也是自己写,没有用现成的dumi或者storybook。