API
Babel 实际上是一组模块的集合。本节我们将探索一些主要的模块,解释它们是做什幺的以及如何使用它们。
注意:本节内容不是详细的 API 文档的替代品,正式的 API 文档将很快提供出来。
babylon
Babylon 是 Babel 的解析器。最初是 从Acorn项目fork出来的。Acorn非常快,易于使用,并且针对非标准特性(以及那些未来的标准特性) 设计了一个基于插件的架构。
首先,让我们安装它。
$ npm install --save babylon
先从解析一个代码字符串开始:
import * as babylon from "babylon";
const code = `function square(n) {
return n * n;
}`;
babylon.parse(code);
// Node {
// type: "File",
// start: 0,
// end: 38,
// loc: SourceLocation {...},
// program: Node {...},
// comments: [],
// tokens: [...]
// }
我们还能像下面这样传递选项给 parse()
方法:
babylon.parse(code, {
sourceType: "module", // default: "script"
plugins: ["jsx"] // default: []
});
sourceType
可以是 "module"
或者 "script"
,它表示 Babylon 应该用哪种模式来解析。 "module"
将会在严格模式下解析并且允许模块定义,"script"
则不会。
注意:
sourceType
的默认值是"script"
并且在发现import
或export
时产生错误。 使用scourceType: "module"
来避免这些错误。
由于 Babylon 使用了基于插件的架构,因此有一个 plugins
选项可以开关内置的插件。 注意 Babylon 尚未对外部插件开放此 API 接口,不排除未来会开放此API。
要查看完整的插件列表,请参见 Babylon README文件。.
babel-traverse
Babel Traverse(遍历)模块维护了整棵树的状态,并且负责替换、移除和添加节点。
运行以下命令安装:
$ npm install --save babel-traverse
我们可以和 Babylon 一起使用来遍历和更新节点:
import * as babylon from "babylon";
import traverse from "babel-traverse";
const code = `function square(n) {
return n * n;
}`;
const ast = babylon.parse(code);
traverse(ast, {
enter(path) {
if (
path.node.type === "Identifier" &&
path.node.name === "n"
) {
path.node.name = "x";
}
}
});
babel-types
Babel Types模块是一个用于 AST 节点的 Lodash 式工具库(译注:Lodash 是一个 JavaScript 函数工具库,提供了基于函数式编程风格的众多工具函数), 它包含了构造、验证以及变换 AST 节点的方法。 该工具库包含考虑周到的工具方法,对编写处理AST逻辑非常有用。
可以运行以下命令来安装它:
$ npm install --save babel-types
然后按如下所示来使用:
import traverse from "babel-traverse";
import * as t from "babel-types";
traverse(ast, {
enter(path) {
if (t.isIdentifier(path.node, { name: "n" })) {
path.node.name = "x";
}
}
});
Definitions(定义)
Babel Types模块拥有每一个单一类型节点的定义,包括节点包含哪些属性,什幺是合法值,如何构建节点、遍历节点,以及节点的别名等信息。
单一节点类型的定义形式如下:
defineType("BinaryExpression", {
builder: ["operator", "left", "right"],
fields: {
operator: {
validate: assertValueType("string")
},
left: {
validate: assertNodeType("Expression")
},
right: {
validate: assertNodeType("Expression")
}
},
visitor: ["left", "right"],
aliases: ["Binary", "Expression"]
});
Builders(构建器)
你会注意到上面的 BinaryExpression
定义有一个 builder
字段。.
builder: ["operator", "left", "right"]
这是由于每一个节点类型都有构造器方法builder,按类似下面的方式使用:
t.binaryExpression("*", t.identifier("a"), t.identifier("b"));
可以创建如下所示的 AST:
{
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "a"
},
right: {
type: "Identifier",
name: "b"
}
}
当打印出来之后是这样的:
a * b
构造器还会验证自身创建的节点,并在错误使用的情形下会抛出描述性错误,这就引出了下一个方法类型。
Validators(验证器)
BinaryExpression
的定义还包含了节点的字段 fields
信息,以及如何验证这些字段。
fields: {
operator: {
validate: assertValueType("string")
},
left: {
validate: assertNodeType("Expression")
},
right: {
validate: assertNodeType("Expression")
}
}
可以创建两种验证方法。第一种是 isX
。.
t.isBinaryExpression(maybeBinaryExpressionNode);
这个测试用来确保节点是一个二进制表达式,另外你也可以传入第二个参数来确保节点包含特定的属性和值。
t.isBinaryExpression(maybeBinaryExpressionNode, { operator: "*" });
这些方法还有一种断言式的版本,会抛出异常而不是返回 true
或 false
。.
t.assertBinaryExpression(maybeBinaryExpressionNode);
t.assertBinaryExpression(maybeBinaryExpressionNode, { operator: "*" });
// Error: Expected type "BinaryExpression" with option { "operator": "*" }
Converters(变换器)
[WIP]
babel-generator
Babel Generator模块是 Babel 的代码生成器,它读取AST并将其转换为代码和源码映射(sourcemaps)。
运行以下命令来安装它:
$ npm install --save babel-generator
然后按如下方式使用:
import * as babylon from "babylon";
import generate from "babel-generator";
const code = `function square(n) {
return n * n;
}`;
const ast = babylon.parse(code);
generate(ast, {}, code);
// {
// code: "...",
// map: "..."
// }
你也可以给 generate()
方法传递选项。.
generate(ast, {
retainLines: false,
compact: "auto",
concise: false,
quotes: "double",
// ...
}, code);
babel-template
babel-template 是另一个虽然很小但却非常有用的模块。 它能让你编写字符串形式且带有占位符的代码来代替手动编码, 尤其是生成的大规模 AST的时候。 在计算机科学中,这种能力被称为准引用(quasiquotes)。
$ npm install --save babel-template
import template from "babel-template";
import generate from "babel-generator";
import * as t from "babel-types";
const buildRequire = template(`
var IMPORT_NAME = require(SOURCE);
`);
const ast = buildRequire({
IMPORT_NAME: t.identifier("myModule"),
SOURCE: t.stringLiteral("my-module")
});
console.log(generate(ast).code);
var myModule = require("my-module");