Appearance
带你玩转 babel 工具链(一)@babel/parser
一、前言
@babel/parser
作为 babel 最核心的库,是我们学习 babel 最重要的一部分,对于后面插件的学习都很有帮助。本文将通过各种示例,帮大家理解 babel-laoder 在 babel 工具链中的作用。
二、基础示例
首先我们先学习下如何使用@babel/parser
下面是一个简单用法
js
require("@babel/parser").parse("let a = 1", {
sourceType: "module",
});
require("@babel/parser").parse("let a = 1", {
sourceType: "module",
});
上面的代码执行后,就会返回 ast 节点,我们可以通过AST explorer这个站点查看 AST
三、选项配置
js
require("@babel/parser").parse("let a = 1", {
sourceType: "module",
});
require("@babel/parser").parse("let a = 1", {
sourceType: "module",
});
那 parser 除了上面的sourceType
还有哪些参数呢?
下面我列出了所有的参数说明。方便大家理解
选项 | 说明 | 简介 |
---|---|---|
allowImportExportEverywhere | 默认情况下,导入和导出声明只能出现在程序的顶层。如果将此选项设置为 true,则可以在允许语句的任何位置使用它们 | 允许任何地方写 import |
allowAwaitOutsideFunction | 默认情况下,仅允许在异步函数内部或在启用 topLevelAwait 插件时,在模块的顶级范围内使用 await,可以将该值设置为 true | 允许顶层 await |
allowReturnOutsideFunction | 默认情况下,函数外的 return 语句会引发错误。将此设置为 true,不会报错 | 允许函数外面写 return |
allowSuperOutsideMethod | 默认情况下,在类和方法之外不允许使用。将此设置为 true,不会报错 | 允许其他地方写 super |
allowUndeclaredExports | 默认情况下,导出未在当前模块作用域中声明的标识符将引发错误。设置为 true 后将不会报错 | 允许导出一个未声明的变量 |
attachComment | 默认情况下,Babel 将注释附加到相邻的 AST 节点。如果此选项设置为 false,则不会附加注释。当输入代码有许多注释时,它可以提供高达 30%的性能改进@babel/eslint 解析器将为您设置它。不建议在 Babel transform 中使用 attachComment:false,因为这样做会删除输出代码中的所有注释 | 是否保留注释 |
createParenthesizedExpressions | 当此选项设置为 true 时,如果给表达式节点包了一层圆括号,会被保留,如果设置为 false,表达式的括号不会保留 | 是否保留包裹表达式的圆括号 |
errorRecovery | 默认情况下,Babel 在发现一些无效代码时总是抛出错误。当此选项设置为 true 时,它将存储解析错误并尝试继续解析无效的输入文件。生成的 AST 将具有一个 errors 属性,表示所有解析错误的数组。请注意,即使启用此选项,@babel/parser 也可能抛出不可恢复的错误 | 是否出现错误后,不停止解析 |
plugins | 包含要启用的插件的数组 | 插件数组 |
sourceType | 指示应在其中解析代码的模式。可以是“script”、“module”或“unambiguous”之一。默认为“script”。“unambiguous”将使@babel/parser 根据 ES6 导入或导出语句的存在尝试猜测。带有 ES6 导入和导出的文件被视为“module”,否则为“script”。 | 解析代码模式,推荐 unambiguous |
sourceFilename | 将输出 AST 节点与其源文件名关联。从多个输入文件的 AST 生成代码和源映射时非常有用 | ast 节点携带当前解析的文件名称 |
startColumn | 默认情况下,解析的代码被视为从第 1 行第 0 列开始。您可以提供一个列编号,以供选择。用于与其他源工具集成。 | 可以选择从哪一列开始解析 |
startLine | 默认情况下,解析的代码被视为从第 1 行第 0 列开始。您可以提供一个行号,以供选择。用于与其他源工具集成。 | 可以选择从哪一行开始解析 |
strictMode | 默认情况下,ECMAScript 代码仅在“use strict”时解析为 strict;指令存在,或者如果解析的文件是 ECMAScript 模块。将此选项设置为 true 以始终在严格模式下解析文件 | 解析的文件都会加上 use strict |
ranges | 向每个节点添加范围属性:[node.start,node.end] | 给 ast 节点添加 range |
tokens | 将所有已解析的令牌添加到文件节点上的令牌属性 | 为 File Ast 节点上的 tokens 属性,添加所有 token |
其中,大家注意一下plugins
这个属性配置,babel 语法插件很多都通过这个字段实现。
四、babel 是如何按需支持不同语法的?
@babel/parser 内部实现了所有可配置的语法,例如 typescript、top-level-await,本文只讨论如何配置并支持各种语法
首先我们有如下代码, 我们写了一段typescript代码
, 并用 parser 编译
js
const parser = require("@babel/parser");
const ast = parser.parse(
`
const a: number = 1
`,
{}
);
f;
console.log(ast);
const parser = require("@babel/parser");
const ast = parser.parse(
`
const a: number = 1
`,
{}
);
f;
console.log(ast);
但是结果并不是我们想要的那样,而是报了语法错误
然后我们再加一个配置
diff
const parser = require('@babel/parser')
const ast = parser.parse(`
const a: number = 1
`, {
+ plugins: ['typescript']
})
console.log(ast);
const parser = require('@babel/parser')
const ast = parser.parse(`
const a: number = 1
`, {
+ plugins: ['typescript']
})
console.log(ast);
发现可以正常打印结果了
其实@babel/parser
在支持不同语法时,需要我们手动添加plugins
来进行支持,但是我们开发项目并不会去添加各种语法插件,那 babel 是如何帮我们添加的呢?
我们打开node_modules/@babel目录
,可以发现里面有一部分插件是这样的
我们简单看一下plugin-syntax-top-level-await
的实现:
源码实现非常简单,只是在 parser 选项添加了一个topLevelAwait
,用于开启top-level-await
。
然后~
我们再来安装下@babel/preset-typescript
, 发现它竟然帮助我们安装了一个plugin-syntax-typescript
插件
源码一样非常简单,同时删除了 flow 和 jsx 语法支持
总结
@babel/parser
基于 acron 做了进一步扩展,实现了很多语法@babel/parser
提供了语法支持plugin-syntax-xxx
语法插件增加了@babel/parser
的plugins
配置