AST(抽象语法树)是对JS代码抽象语法结构的表示,Babel可以帮我们把代码解析成AST,方便我们进行转换,操作,生成。社区的 eslint,代码转换工具 taro 等,都离不开 AST 进行操作。下面整理下 AST 常见的使用方法。

创建 AST

Babel 提供了 @babel/parserparse 方法进行代码字符串解析,可以创建 AST 对象。

const parser = require('@babel/parser');
const code = `const a = 5;`;
const ast = parser.parse(code, { sourceType: "module" });

遍历 AST

有了 AST 对象之后,就可以使用 @babel/traverse 库进行遍历,该库方便我们在不同类型节点进行操作。

比如我们要把代码 const a = 5; 中的 a 替换成 b,可以这样操作:

const traverse = require('@babel/traverse').default;
traverse(ast, {
  Identifier(path) {
    // 替换name
    path.node.name = 'b';
  }
});

或者 const a = 1 + 2; 替换成 const a = 3;

const traverse = require('@babel/traverse').default;
const t = require('@babel/types');
traverse(ast, {
  BinaryExpression(path) {
    // 替换name
    path.replaceWith(t.numericLiteral(3));
  }
});

生成新的代码

有了修改后的 AST 对象,我们可以使用 @babel/generator 库生成新的代码。

const generator = require('@babel/generator').default;
const { code } = generator(ast);
console.log(code); // const a = 3;

下面是完整的代码示例

const parser = require('@babel/parser');
const traverse = require('@babel/traverse').default;
const generator = require('@babel/generator').default;
const code = 'const a = 5;';
const ast = parser.parse(code, { sourceType: 'module' });
traverse(ast, {
  Identifier(path) {
    // 替换name
    path.node.name = 'b';
  }
});
const { code: output } = generator(ast);
console.log(output); // const b = 5;

我们也可以自己通过 @babel/template 创建模板字符串生成代码,通过占位符来注入动态变量。

const proxyAst = template.ast(
  `
  new Proxy($0, {
    get: function(target, key) {
      return target[key] || 0;
    }
  })
`,
  {
    placeholderPattern: /^\$\d+/,
    preserveComments: true
  }
);
const objectStr = 'People';
proxyAst.expression.arguments[0].name = objectStr;
const ProxyCode = generator(proxyAst).code;
console.log(ProxyCode); // new Proxy(People, { get: function(target, key) { return target[key] || 0; } })