这是我参与更文挑战的第 6 天,活动详情查看:更文挑战
写在前面:这是一个比较扯淡的用法,面对认为不合理的需求,建议还是和规则制定者沟通,了解他们的真实诉求,实在不行的话,能偷懒还是要偷点懒的。
那么故事开始。
前一段时间,某个技术群的群友提到:
公司规范组推广代码规范,要求代码都要配上注释,且注释量需要达到代码行数的 1/3 。现在求一个自动加注释的方案,先把这个事情搞过去。
如果说是在编写代码时加注释,成熟的 IDE 或者代码编辑器都是有插件或者代码模板可以完成类似工作。
以 vscode 为例,插件可以使用 koroFileHeader 或者 Document This。koro 甚至可以添加字符画
但如果说在已有代码上添加注释,可以想到的方案就是重构工具。
重构工具使开发人员能够在短时间内重构大型代码库。在某些情况下,开发人员可能会使用 IDE 来执行类或变量名的重构,但是,通常一次只能将其限定为一个文件。而重构工具允许进行全局查找和替换。
比较著名的 js 重构工具有 facebook 开发的 codemod 和 jscodeshift。这两者都基于 esprima。
值得一提的是,vue 和 react 在代码升级时,均编写了自己的重构工具 react-codemod 和 vue-codemod,两者均有 jscodeshift 的支持。
关于 jscodeshift,可以参考 jscodeshift 简易教程、代码重构利器 —— jscodeshift 和 使用重构件(Codemod)加速 JavaScript 开发和重构
jscodeshift 如何起到重构作用?
jscodeshift 解析 js ,将 js 内容解析成 AST 语法树,而后提供一些便利的操作接口,方便咱们对各个节点进行更改,比如说更改全部的属性名。
以官方提供的代码举例:
const j = require('jscodeshift');
j(jsContent)
.find(j.Identifier)
.replaceWith(
p => j.identifier(p.node.name.split('').reverse().join(''))
);
执行这段代码,可以将 console.log('123') 转换成 elosnoc.gol('123') 。
尝试解决问题吧
话说回来,上面群友提到的问题,同样可以使用 jscodeshift 解决。
解决步骤可以分为三步:
graph TD
代码分析 --> 编写转换代码 --> 执行转换
代码分析
首先需要将代码转化为 ast 树,有了 ast 树,就可以使用 jscodeshift 选择器对 path 进行查找操作。
有一个ast 在线分析工具,可以将代码转换为 ast。
比如说下图中,function 被解析为 FunctionDeclaration,foo 被解析为 id,x,y被解析为 params。
编写转换代码
根据 ast 树,我们可以查找到添加注释的位置,在对应位置添加预先编写的注释模板即可。
查找添加注释的位置
关于左侧的代码列表,我们期望在变量a上面、函数上面添加必要的注释。
通过观察右侧的 ast 树,我们可以先定位到 VariableDeclaration 和 FunctionDeclaration。定位方法如下:
export default function transformer(file, api) {
const j = api.jscodeshift;
const source = j(file.source)
source.find(j.VariableDeclaration) // 变量
source.find(j.FunctionDeclaration) // 函数
// ...其他代码
}
编写注释信息
为了看起来标准,我们用 jsdoc 格式编写注释。
下面是 jsdoc 格式注释样例:
既然如此,我们就按照这种样式仿写一个。代码中以 ${.*} 格式编写的对应真实的名称
/** The good ${varName}. */
export var lastColor = null;
/**
* A good function.
* @param {string} ${param1} - The good param, in suitable format.
* @param {string} ${param2} - The good param, in suitable format.
* @return {string} i don't know.
*/
export function blend(color1, color2) {}
之后我们构建注释。使用 commentBlock 方法构建块注释。
最后编写转换函数,将上面的注释方法放到对应位置。完整的代码清单如下:
export default function transformer(file, api) {
const j = api.jscodeshift;
const source = j(file.source)
// 变量
source.find(j.VariableDeclaration).forEach(p => {
const comments = p.node.comments = p.node.comments || [];
p.node.declarations.forEach(d => {
const comment = j.commentBlock(`The good ${d.id.name}.`, true, false); //生成行注释
comments.push(comment);
})
});
// 函数
source.find(j.FunctionDeclaration).forEach(p => {
const comments = p.node.comments = p.node.comments || [];
let commentArr = []
commentArr.push(`A good function named ${p.node.id.name}.`)
p.node.params.forEach(d => {
commentArr.push(`@param {string} ${d.name} - The first param, in suitable format.`)
})
commentArr.push(`@return {string} i don't know.\n `)
const comment = j.commentBlock(commentArr.map(c => `\n * ${c}`).join(''), true, false); //生成块注释
comments.push(comment);
});
return source.toSource();
}
运行结果参照这里
执行转换
转换代码编写完成,在执行之前,先安装本地运行环境
npm install -g jscodeshift
然后我们直接在控制台执行转换代码:
jscodeshift -t ./transformer.js ./test.js
控制台会输出转换结果。
你可以根据转换结果调整转换代码,最终达成全部转换的目的。
对语言的支持情况
js 文件天然支持,ts 文件官方也提供了可开启专用解析器的配置。react 的 jsx 和 tsx 文件也是可以的。
至于 vue 模板,我没有测试过,不过看 vue 官方也提供了转换工具,估计也是可行的吧。