Skip to content

带你玩转 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

image.png

三、选项配置

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);

但是结果并不是我们想要的那样,而是报了语法错误

image.png

然后我们再加一个配置

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);

发现可以正常打印结果了

image.png

其实@babel/parser在支持不同语法时,需要我们手动添加plugins来进行支持,但是我们开发项目并不会去添加各种语法插件,那 babel 是如何帮我们添加的呢?

我们打开node_modules/@babel目录,可以发现里面有一部分插件是这样的

image.png

我们简单看一下plugin-syntax-top-level-await 的实现:

image.png

源码实现非常简单,只是在 parser 选项添加了一个topLevelAwait,用于开启top-level-await

然后~

我们再来安装下@babel/preset-typescript, 发现它竟然帮助我们安装了一个plugin-syntax-typescript插件

image.png

image.png

源码一样非常简单,同时删除了 flow 和 jsx 语法支持

总结

  • @babel/parser基于 acron 做了进一步扩展,实现了很多语法
  • @babel/parser提供了语法支持
  • plugin-syntax-xxx 语法插件增加了@babel/parserplugins配置