手把手教你写几个实用的AST插件

背景

AST 是 非常有用的

上午一直在搬砖,下午听了小组一个老哥做的 AST分享 ,觉得做的很好。

为了加深印象,就写了篇总结,顺便分享给大家,希望能给朋友们一些 帮助和启发

可能小伙伴要问,AST真有这么厉害?

口说无凭, 且看几个 具体的案例

且不说:

  1. Vue => React
  2. React => Vue

的代码转换方法,

我们就看一个可以 无痛升级旧版React 的工具:

react-codemod

代码地址: https://github.com/reactjs/react-codemod

这个工具,功能十分强大,使用起来也很方便,只需要运行一行命令:

npx react-codemod <transform> <path> [...options]

这些功能的实现,无不借助了 AST

下面我们就进入今天的内容。

正文

本文的主要内容包括:

  • 1 理论: AST  基本概念
  • 2 实践: 使用 AST 实现一个代码转换工具, 把  var 转换成 let
  • 3 实践: 使用 AST 实现一个Eslint 插件,  禁用 console
  • 4 实践: 使用 AST 实现一个Babel插件,  过滤 Debugger

1. AST 基本概念

AST 是什么?

AST is a hierarchical program representation that presents source code structure according to the grammar of a programming language, each AST node corresponds to an item of a source code.

在计算机科学中, 抽象语法抽象语法树 其实是源代码的抽象语法结构的 树状表现形式

常用的浏览器就是通过 将js代码转化为抽象语法树 来进行下一步的分析等其他操作。

所以将js转化为抽象语法树更 利于程序的分析

AST 能做什么

  • 代码语法的检查

  • 代码风格的检查

  • 代码的格式化

  • 代码的高亮

  • 代码错误提示

  • 代码自动补全

  • 等等。

AST 三板斧

  • 生成AST

  • 遍历和更新AST

  • 将AST重新生成源码

为了便于理解, 我们看一个具体的例子。

这里顺便给大家介绍一个十分有用的网站:

https://astexplorer.net/

譬如:

声明, 变量, 类型等各种信息一应俱全。

而且这里也提供了各种插件模版供你选择:

十分的方便。

我们就根据这个例子, 我们做个小的 实践

2. 实践: 使用AST实现一个代码转换工具, 把 var 转换成 let

比如, 现在要重构一个老项目, 你要 把项目里的var 全部替换成let , 你会怎么做?

手动替换? 或者借助工具 一键替换?

现在就教你一招: 一键替换大法

首先还是要介绍一把大杀器:

jscodeshift

它是一个 Javscript Codemod 工具,官方对 Codemod 的解释是:

Codemod is a tool/library to assist you with large-scale codebase refactors that can be partially automated but still require human oversight and occasional intervention.

jscodeshift 也是基于 esprima 的,其 通过 path 可以很容易的在 AST 上遍历 node

现在我们就开始替换项目中的var.

首先,到 https://astexplorer.net 里面编写代码.

模板我们选: jscodeshift

官方自带的例子, 把变量名字反转:

我们现在要改变量, 这个工具很贴心的一点是可以高亮实时对照,

现在, 找到 kind === var 的对象, 替换成 let :

得到如下代码:

export default function transformer(file, api) {
  const j = api.jscodeshift;

  return j(file.source)
    .find(j.VariableDeclaration, { kind: 'var'})
    .forEach(path => {
      const letStatement = j.variableDeclaration('let', path.node.declarations)
      j(path).replaceWith(letStatement)
    })
    .toSource();
}

这样也可以:

path.node.kind = 'let'; // 传入的实际是一个引用

实际效果:

大功告成!

假如我项目里有几个文件也需要相同的操作:

简单安装:

sudo npm install -g jscodeshift

执行:

jscodeshift -t transform.js ./src/demo.js --dry --print

这里用了 --dry 和 --print

--dry 加上之后,不会立刻把新生成的代码覆盖源文件

--print 是打印出来看看

在实际项目里, 你需要在 独立的分支里 操作,新生成代码之后, 需要你 再检查检查 ,  review没有问题 之后才能合并。

3. 使用AST实现一个Eslint 插件, 禁用console

和上面的类似, 我们也可以做一个eslint 插件, 功能也很简单: 检查到使用console的时候就报错

期望达到的效果:

// Do not use console methods (at 1:9)
   console.log('haha')
// --------^

我们这次选择 babel-eslint 模版。

代码实现:

const disallowMethods = ["log", "info", "warn", "error", "dir"];
export default function(context) {
  return {
    Identifier(node) {
      const isConsoleMethod =
        disallowMethods.includes(node.name) &&
        node.parent.type === "MemberExpression" &&
        node.parent.object.name === "console";

      if (!isConsoleMethod) return;

      context.report({
        node,
        message: "Do not use console methods"
      });
    }
  };
}

实际效果:

简单有效。

不过你要是非要玩什么骚操作,比如自定义一个log, 那就没得搞了。

最后, 你可以把这段代码封装成一个完整的插件:

教你如何编写 Eslint 插件:

https://juejin.im/post/5d91be23f265da5ba532a07e

你可以自行实践。

4. 使用AST实现一个Babel插件, 过滤debugger

最后一个是过滤源代码中的debugger, Transform 我们选择 babelv7

这个插件,我们期望达到的效果是:

var a = 1
debugger
function test() {
  debugger
   a++
}
debugger

到:

var a = 1;

function test() {
  a++;
}

这也是一个十分有用的功能。

代码实现:

export default function (babel) {
    const {
        types: t
    } = babel;

    return {
        name: "ast-transform",
        visitor: {
            DebuggerStatement(path) {
                path.remove()
            }
        }
    };
}

实际效果:

符合预期。

总结

内容大概就是这么多,没什么难度,重在讲述 理论和入门

对AST还不熟练的同学, 希望这篇可以帮助到你。

如果你觉得这篇内容对你挺有启发,我想 邀请你帮我三个小忙:

  1. 点个「 在看 」,让更多的人也能看到这篇内容(喜欢不点在看,都是耍流氓 -_-)

  2. 关注我的官网  https:// m uyiy.cn ,让我们成为长期关系

  3. 关注公众号「 高级前端进阶 」,每周重点攻克一个前端面试重难点,公众号后台回复「面试题」 送你高级前端面试题。

我来评几句
登录后评论

已发表评论数()

相关站点

热门文章