如何快速给已有的代码加注释

2,345 阅读4分钟

这是我参与更文挑战的第 6 天,活动详情查看:更文挑战

写在前面:这是一个比较扯淡的用法,面对认为不合理的需求,建议还是和规则制定者沟通,了解他们的真实诉求,实在不行的话,能偷懒还是要偷点懒的。

那么故事开始。


前一段时间,某个技术群的群友提到:

公司规范组推广代码规范,要求代码都要配上注释,且注释量需要达到代码行数的 1/3 。现在求一个自动加注释的方案,先把这个事情搞过去。

如果说是在编写代码时加注释,成熟的 IDE 或者代码编辑器都是有插件或者代码模板可以完成类似工作。

以 vscode 为例,插件可以使用 koroFileHeader 或者 Document This。koro 甚至可以添加字符画

但如果说在已有代码上添加注释,可以想到的方案就是重构工具。

重构工具使开发人员能够在短时间内重构大型代码库。在某些情况下,开发人员可能会使用 IDE 来执行类或变量名的重构,但是,通常一次只能将其限定为一个文件。而重构工具允许进行全局查找和替换。

比较著名的 js 重构工具有 facebook 开发的 codemodjscodeshift。这两者都基于 esprima

值得一提的是,vue 和 react 在代码升级时,均编写了自己的重构工具 react-codemodvue-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 被解析为 FunctionDeclarationfoo 被解析为 idx,y被解析为 params

image.png

编写转换代码

根据 ast 树,我们可以查找到添加注释的位置,在对应位置添加预先编写的注释模板即可。

查找添加注释的位置

关于左侧的代码列表,我们期望在变量a上面、函数上面添加必要的注释。 通过观察右侧的 ast 树,我们可以先定位到 VariableDeclarationFunctionDeclaration。定位方法如下:

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 格式注释样例

image.png

既然如此,我们就按照这种样式仿写一个。代码中以 ${.*} 格式编写的对应真实的名称

/** 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

控制台会输出转换结果。

image.png

你可以根据转换结果调整转换代码,最终达成全部转换的目的。

对语言的支持情况

js 文件天然支持,ts 文件官方也提供了可开启专用解析器的配置。react 的 jsx 和 tsx 文件也是可以的。

至于 vue 模板,我没有测试过,不过看 vue 官方也提供了转换工具,估计也是可行的吧。