如何编写一个babel插件

编写babel插件时最常使用的是库 @babel/core@babel/types

babel插件需要返回一个function,function内返回visitor。

module.exports = function() {
return {
    visitor: {
    // plugin code
    }
}
}
复制代码

visitor里我们可以编写针对各类 AST type 的处理方式,从而达到修改AST的效果,关于 babel 转换得到的各类 AST 究竟有哪些类型,可以在 这里 看到。

visitor 这个对象的key就是ast的类型,值就是处理ast的函数。

例如,遇到全等号的时候我们将全等号的两边的值换掉,可以这样写。

const babel = require('@babel/core');
const t = require('@babel/types');

const visitor = {
  BinaryExpression(path) {
    if (path.node.operator !== "===") return;
    path.node.left = t.identifier("huruji");
    path.node.right = t.identifier('grey');
  }
}

module.exports = function (babel) {
  return {
    visitor,
  }
}
复制代码

babel插件通过options来配置使用,如:

{
   plugins: [
      ["my-plugin", {
         "opt1": true,
         "opt2": false
      }]
   ]
}
复制代码

这里的options参数会传给我们visitor的每个处理函数的第二个参数state的opts

const visitor = {
  BinaryExpression(path, state) {
    if(state) {
       console.log(state.opts);
      // { opt1: true, opt2: false }
    }
  }
}

module.exports = function (babel) {
  return {
    visitor,
  }
}
复制代码

通过这个参数我们就可以更好地配置我们的 babel 插件。

插件基本的编写已经明朗,接下来看看插件最核心的功能,就是修改 AST,也就是对AST进行增删改。

在增删改前我们首先需要保证我们是真正处理了我们想要处理的代码,在AST中也就是各个node节点,我们可以通过 @babel/types 来判断,同时通过node节点属性来辅助我们判断

if (t.isNumericLiteral(node.left) && t.isNumericLiteral(node.right)) {
// plugin code
}

复制代码
BinaryExpression(path) {
  if (t.isIdentifier(path.node.left, { name: "n" })) {
    // ...
  }
}
复制代码

这样的检查功能上等价于

BinaryExpression(path) {
  if (
    path.node.left != null &&
    path.node.left.type === "Identifier" &&
    path.node.left.name === "n"
  ) {
    // ...
  }
}
复制代码

接下来就是对于AST的增删改了。

增加兄弟节点可以使用 insertBeforeinsertAfter 方法,使用babel插件手册的例子:

FunctionDeclaration(path) {
  path.insertBefore(t.expressionStatement(t.stringLiteral("Because I'm easy come, easy go.")));
  path.insertAfter(t.expressionStatement(t.stringLiteral("A little high, little low.")));
}
复制代码

删除一个节点使用 remove 方法即可:

path.move();
复制代码

替换节点使用 replaceWith 方法,依旧使用别人的例子:

BinaryExpression(path) {
  path.parentPath.replaceWith(
    t.expressionStatement(t.stringLiteral("Anyway the wind blows, doesn't really matter to me, to me."))
  );
}
复制代码

有时候需要对父级节点做处理,可以通过 path.parentPath 来访问父级节点

path.parentPath.remove();
复制代码

同时还有其他方法 path.findParentpath.findpath.getFunctionParentpath.getStatementParent 等。

有时候我们需要判断或者获取兄弟节点同样也行,比如:

  • path.inList 判断是否有兄弟节点

  • path.getSibling(index) 获取指定的节点

实践以下,下面可以看一个大佬写的插件:

var babel = require('babel-core');
var t = require('babel-types');

const visitor = {
  BinaryExpression(path) {
    const node = path.node;
    let result;
    // 判断表达式两边,是否都是数字
    if (t.isNumericLiteral(node.left) && t.isNumericLiteral(node.right)) {
      // 根据不同的操作符作运算
      switch (node.operator) {
        case "+":
          result = node.left.value + node.right.value;
          break
        case "-":
          result = node.left.value - node.right.value;
          break;
        case "*":
          result =  node.left.value * node.right.value;
          break;
        case "/":
          result =  node.left.value / node.right.value;
          break;
        case "**":
          let i = node.right.value;
          while (--i) {
            result = result || node.left.value;
            result =  result * node.left.value;
          }
          break;
        default:
      }
    }

    // 如果上面的运算有结果的话
    if (result !== undefined) {
      // 把表达式节点替换成number字面量
      path.replaceWith(t.numericLiteral(result));
    }
  }
};

module.exports = function (babel) {
  return {
    visitor
  };
}
复制代码

我自己动手实践一下,实现了一个去掉调试代码的babel插件, 地址在这里babel-plugin-no-debugging

refs:

github.com/jamiebuilds…

面试官: 你了解过Babel吗?写过Babel插件吗? 答: 没有。卒

理解 Babel 插件

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值