【源码阅读】打包之后,如何一键自动发布+代码提交?

443 阅读3分钟

先说下这篇文章的由来,纯属机缘巧合。

因为最近对之前搭了一个造轮子的ui库进行了重构,使用pnpm进行monorepo架构,把一些包单独提了出来。

因为涉及到多个包的管理,以及版本更新,需要一个包管理工具。

于是查看了pnpm官网找到了Changesets这个库。

用完之后,虽然是可以实现包版本管理,也能打发布日志,but(步骤有点多,而且也不能自动帮我提交到github上,也不能帮我打tag)

image.png

所以最后包版本管理还是用的release-it。

但是,release-it不会自动更新其他依赖的包版本,而且发布的时候也不会解析如下这种依赖。

  "@bubu-ui/hook": "workspace:*"

后来的解决方案是:workspace用*号代替(一直引用的最新版本),发布的时候通过脚本自动替换依赖版本。(原理也很简单,就是读一下根目录下的库文件,查找package文件进行依赖匹配,并获取最新版本)

方案刚实现没几天,看到了这个源码的内容,阅读了一下vue3的发布脚本,感觉还可以重构一版。

废话就不多说了,直接看源码。


我看源码的习惯是,先看package.json,根据脚本找入口文件。(其实就是执行scripts/release文件)

image.png

在release,其实就是执行了一个main函数。(上面这些辅助函数,以及工具用到的时候再解释作用)

image.png

选择版本

先看第一步,选中版本(这个红色是我本地的ts问题)

image.png

args是获取命令行输入的。

比如我们在终端输入命令test init,就可以在process.argv中获取到这些参数。因为参数解析会有各种格式,所以一般会用一些库来帮忙处理(minimist)。

prompt是库enquirer的方法,用于命令行交互,一般有输入框,选择,多选等。

比较值得一说的是choices选项的生成。(我把3个相关的方法都粘贴过来了)

const versionIncrements = [
  'patch',
  'minor',
  'major',
  ...(preId ? ['prepatch', 'preminor', 'premajor', 'prerelease'] : [])
]
const inc = i => semver.inc(currentVersion, i, preId)
versionIncrements.map(i => `${i} (${inc(i)})`).concat(['custom'])

semver这个库是用来管理npm版本的,它可以做版本大小比较,生成新版本。

image.png

这步执行完毕,结果如图。

image.png

运行测试

就几行代码,run执行的是execa方法。

execa库的作用就是执行脚本,这个相当于直接在终端上输入命令。

const run = (bin, args, opts = {}) =>
  execa(bin, args, { stdio: 'inherit', ...opts })
  
 await run(bin('jest'), ['--clearCache'])
 await run('pnpm', ['test', '--bail'])

step就是日志输入,使用的chalk,可以改变日志样式。

更新版本

  function updateVersions(version) {
      // 1. update root package.json
      updatePackage(path.resolve(__dirname, '..'), version)
      // 2. update all packages
      packages.forEach(p => updatePackage(getPkgRoot(p), version))
  }
  updateVersions(targetVersion)

先更新根目录的版本号,然后遍历packages下面的包。

那具体怎么更新呢?

1.先找到文件

2.读取文件内容

3.修改内容,并重新写入

  const pkgPath = path.resolve(pkgRoot, 'package.json')
  const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'))
  pkg.version = version
  updateDeps(pkg, 'dependencies', version)
  updateDeps(pkg, 'peerDependencies', version)
  fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n')

打包以及代码提交

后续的内容就是运行各种命令。

打包,打日志,代码提交。 image.png

其中有个新函数runIfNotDry。

const isDryRun = args.dry
const dryRun = (bin, args, opts = {}) =>
  console.log(chalk.blue(`[dryrun] ${bin} ${args.join(' ')}`), opts)
const runIfNotDry = isDryRun ? dryRun : run

从定义中也可以看出,dryRun就是输出日志,run就是执行脚本。

发布版本

对package文件做了一些判断。

然后就是执行发布命令。

image.png

命令的各个配置,可以查看一下npm官网

总结

我本来觉得,我之前写的通过cz-commitlint来提交,通过release-it来发布,也够用了。

现在看来,还是可以继续重构,实现一个命令走天下。

如果看完有收获,欢迎点赞、评论、分享支持。你的支持和肯定,是我写作的动力