- 本文参加了由公众号@若川视野 发起的每周源码共读活动, 点击了解详情一起参与。
- 这是源码共读的第3期,链接:juejin.cn/post/708498…
先说下这篇文章的由来,纯属机缘巧合。
因为最近对之前搭了一个造轮子的ui库进行了重构,使用pnpm进行monorepo架构,把一些包单独提了出来。
因为涉及到多个包的管理,以及版本更新,需要一个包管理工具。
于是查看了pnpm官网找到了Changesets这个库。
用完之后,虽然是可以实现包版本管理,也能打发布日志,but(步骤有点多,而且也不能自动帮我提交到github上,也不能帮我打tag)
所以最后包版本管理还是用的release-it。
但是,release-it不会自动更新其他依赖的包版本,而且发布的时候也不会解析如下这种依赖。
"@bubu-ui/hook": "workspace:*"
后来的解决方案是:workspace用*号代替(一直引用的最新版本),发布的时候通过脚本自动替换依赖版本。(原理也很简单,就是读一下根目录下的库文件,查找package文件进行依赖匹配,并获取最新版本)
方案刚实现没几天,看到了这个源码的内容,阅读了一下vue3的发布脚本,感觉还可以重构一版。
废话就不多说了,直接看源码。
我看源码的习惯是,先看package.json,根据脚本找入口文件。(其实就是执行scripts/release文件)
在release,其实就是执行了一个main函数。(上面这些辅助函数,以及工具用到的时候再解释作用)
选择版本
先看第一步,选中版本(这个红色是我本地的ts问题)
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版本的,它可以做版本大小比较,生成新版本。
这步执行完毕,结果如图。
运行测试
就几行代码,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')
打包以及代码提交
后续的内容就是运行各种命令。
打包,打日志,代码提交。
其中有个新函数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文件做了一些判断。
然后就是执行发布命令。
命令的各个配置,可以查看一下npm官网。
总结
我本来觉得,我之前写的通过cz-commitlint来提交,通过release-it来发布,也够用了。
现在看来,还是可以继续重构,实现一个命令走天下。
如果看完有收获,欢迎点赞、评论、分享支持。你的支持和肯定,是我写作的动力。