本文参加了由公众号@若川视野 发起的每周源码共读活动,点击了解详情一起参与。
我们在平时 coding 的时候,会在本地 git 仓库切换 tags,或者 git 仓库切换 tags。那么我们如何获取 tags 呢?
本次要学习的源码 remote-git-tags
会告诉我们。
那么此次学习,我们将收益到:
- Node 加载采用什么模块
- 获取 git 仓库所有 tags 的原理
- 学会调试源码
- 学会面试高频考点
promisify
的原理和实现
使用该库获取 tags
import remoteGitTags from 'remote-git-tags'
console.log(await remoteGitTags('https://github.com/foxhsx/static-blog.git'))
// =>
源码
首先,我们从名称就可以得知,这个库的作用就是:从远程仓库获取所有的标签。
其次,我们一般使用 git 命令来直接获取仓库的所有 tags:
$ git ls-remote --tags [repo_url]
事实上,此仓库的实现原理也是需要通过上述命令来实现
应用场景:
- 看有哪些包依赖的这个包(没想到场景)
在
npm-check-updates
库中,它会将我们的 package.json 中的依赖项升级到最新版本,忽略指定的版本
- 获取到所有 tags 信息之后,进行 tag 切换或者选定 tag 进行版本发布等,比如微信小程序版本
package.json
我们说,看源码先看 package.json
。
{
// 指定 Node 以什么模块加载,缺省时默认是 commonjs
"type": "module",
"exports": "./index.js",
// 指定 nodejs 版本
"engines": {
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
},
"scripts": {
"test": "xo && ava"
}
}
在 Node 13 版本之前,一直都是采用的 CommonJS 模块机制,而在13之后,则添加来对标准 ES6 模块的支持。
要告知 Node 要加载的是什么模块,最简单的方法就是将信息编码到不同的扩展名中。
.mjs
结尾的文件,是采用 ES6 的模块来加载cjs
结尾的文件,是采用 CommonJS 的模块来加载js
结尾的文件,默认是使用 CommonJS。- 如果在
package.json
中,有type: module
的属性,则会使用 ES6 模块 - 如果
type: commonjs
或者没有package.json
,都是会默认使用 commonjs 模块加载
- 如果在
调试源码
我们需要克隆源码到本地并通过编辑器打开,找到 package.json
中的 test
命令。
当鼠标停留在 test
命令上时,会出现如图所示的 运行脚本
和 调试脚本
的选项,我们选择调试。
关于 VSCode 如何调试 Node.j,如下图:
调试如下:
调试的时候需要注意,在 Node 环境下进行调试:
- 文件中的花括号不能有空格
- 需要使用单引号
- 需要使用 tab 进行缩进-
主文件的代码如下:
import {promisify} from 'node:util';
import childProcess from 'node:child_process';
const execFile = promisify(childProcess.execFile);
export default async function remoteGitTags(repoUrl) {
const {stdout} = await execFile('git', ['ls-remote', '--tags', repoUrl]);
const tags = new Map();
for (const line of stdout.trim().split('\n')) {
const [hash, tagReference] = line.split('\t');
const tagName = tagReference
.replace(/^refs/tags//, '')
.replace(/^{}$/, '');
tags.set(tagName, hash);
}
return tags;
}
可以看到在第7行,就使用了我们开始说的查看仓库所有 tags 命令的对应字段,Node 会帮我们去执行该命令,并将得到的信息以 Map 的数据形式返回。
我们在源码中,要使用 Node 原生库中的一些工具,所以这里的写法也是一目了然:
node:util
promisify
在 Node 中,promisify
函数将 callback 的形式转为了 promise
的形式。
Nodejs 天生异步,回调函数的第一个参数是错误信息,也就是错误优先。如果不采用 promise 的写法,很容易造成回调地狱的场景出现。所以更优的实现是封装一个 promisify 的通用函数,来满足大多数场景。
function promisify(original) {
function fn(...args) {
return new Promise((resolve, reject) => {
args.push((err, ...values) => {
if (err) {
return reject(err)
}
resolve(values)
})
Reflect.apply(original, this, args)
})
}
return fn
}
总结
remote-git-tags
原理:
使用 Node.js 的子进程 child_process
模块的 execFile
方法执行 git ls-remote --tags [repo_url]
,获取到对应仓库所有的 tags 和对应 hash 值存放在 Map 对象中。
本次的源码学习,让我更加清楚如何有条理的、更高效的进行源码阅读,正如文章中所说:
- 看源码前先看
package.json
文件 - 循序渐进
- 借助调试
- 理清主线
- 查阅资料
- 总结记录
在 package.json
中有透出大量的信息给我们,我们可以根据一些字段知道要运行该库,应该需要什么、依赖什么。比如文章中列举的几个重要字段:
- type:指定 Node 以什么模式加载
- engines:指定环境及其版本
- scripts:这个属性大家最熟悉不过了,里面会存放一些执行脚本命令
而在 VSCode 中进行调试也是异常的简单:
- 可提前在文件中打好断点(左侧代码行数点一下即可)
- 在
package.json
中的scripts
属性上面有一个灰色的「调试」字段,点击即可进行调试