AST-babel

19 3月

AST抽象语法树这篇文件已经解释的很详细了,建议先看一遍。

项目中,上线前想将去除注释后的代码交给eslint的CLIEngine内核,去检查是否含有es6代码。检查无误后才开始上线动作,实际的源码压缩混淆动作,CI系统会提供。

借用babel的功能很容易就能将源代码中的注释全部去除,babel转码需要经过三个阶段:parse,transform,generate,顾名思义,不赘述。顺便也介绍一下babel的这几个工具。

  • @babel/parser
  • @babel/traverse
  • @babel/types
  • @babel/generator

@babel/parser

parse阶段会进行词法分析(Lexical Analysis)和语法分析(Syntactic Analysis),里面会将code解析成各种token,最终将code解析成ast语法树。不了解概念也无妨,我们是API工程师。

@babel/parser(前身是babel v6.x版的babylon)是一个可以将JS代码转成AST的解析器。

babelParser.parse(code, [options]),将js转成ast,options见官网,可以配置一些例如从第几行开始解析,是否允许import/export等,也可以配置jsx,flow等plugin。

const babelParser = require('@babel/parser');
const removeComments = (code) => {
    const ast = babelParser.parse(code);
    ...
};

// Node {
//   type: "File",
//   start: 0,
//   end: 38,
//   loc: SourceLocation {...},
//   program: Node {...},
//   comments: [],
//   tokens: [...]
// }

@babel/traverse

transform阶段,会将ast语法树,增加/更新/删除节点,生成新的ast语法数。

@babel/traverse用于改变ast语法树的节点。

const babelParser = require('@babel/parser');
const babelTraverse = require('@babel/traverse');

const removeComments = (code) => {
    const ast = babelParser.parse(code);

    babelTraverse.default(ast, {
        enter(path) {
            ... // 这里操作ast的path
        },
    });
    ...
};

ast语法树中有很多node节点,各node节点间如何联系呢?用path对象,它表示两个node节点间的link关系。例如:

{
  type: "FunctionDeclaration",
  id: {
    type: "Identifier",
    name: "square"
  },
  ...
}

// 上面Identifier的path就是:
{
  "parent": {
    "type": "FunctionDeclaration",
    "id": {...},
    ....
  },
  "node": {
    "type": "Identifier",
    "name": "square"
  }
}

转换时最需要小心的是scope,因为JS的scope比较牛逼,防不胜防,一不小心就绑定到window对象上去了。

@babel/types

@babel/types是一个操作astT节点的lodash式函数库。用于构建,验证,转换ast节点。支持的API非常多,有两类isXXX,assertXXX比较常用(均支持传递第二个参数options,用来判断node节点里是否含有该属性)。这里用的removeComments居然在官网里没有…实际你打印出来,@babel/types里是有该方法的。

const babelParser = require('@babel/parser');
const babelTraverse = require('@babel/traverse');
const babelTypes = require('@babel/types');

const removeComments = (code) => {
    const ast = babelParser.parse(code);

    babelTraverse.default(ast, {
        enter(path) {
            babelTypes.removeComments(path.node);  // 去除所有注释
        },
    });
    ...
};

@babel-generator

generator阶段,根据最终的ast语法树生成code。

@babel-generator用于将ast转为js代码。

const babelParser = require('@babel/parser');
const babelTraverse = require('@babel/traverse');
const babelTypes = require('@babel/types');
const babelGenerate = require('@babel/generator');

const removeComments = (code) => {
    const ast = babelParser.parse(code);

    babelTraverse.default(ast, {
        enter(path) {
            babelTypes.removeComments(path.node);  // 去除所有注释节点
        },
    });

    return babelGenerate.default(ast).code;    // 返回去掉注释后的js代码
};

是不是很简单^_^

在线转换工具

发表评论

电子邮件地址不会被公开。 必填项已用*标注