首页文章关于

Babel-handbook插件

babelcompiler bundlertool

Babel作为source to source的转换编辑器(transpiler)转换过程主要分为三步:

  • parse: 通过parser把源码转换成抽象语法树(AST),词法分析根据规则拆分成各个token再通过语法分析将token递归组装成可用的AST
  • transform: 通过相应visitor函数遍历AST调用各种transform插件对AST进行操作修改
  • generate: 把转换后的AST生成对应结构的字符串并拼接还会并生成sourcemap

抽象语法树(AST)

AST节点组成

处理过程中每一步都涉及到创建或是操作抽象语法树,babel使用基于ESTree标准的ast

js
function square(n) {
return n * n;
}

解析后的ast如下:

json
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
params: [{
type: "Identifier",
name: "n"
}],
body: {
type: "BlockStatement",
body: [{
type: "ReturnStatement",
argument: {
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "n"
},
right: {
type: "Identifier",
name: "n"
}
}
}]
}
}

每个层级都有相同的结构:

json
{
type: "FunctionDeclaration",
id: {...},
params: [...],
body: {...}
}
json
{
type: "Identifier",
name: ...
}
json
{
type: "BinaryExpression",
operator: ...,
left: {...},
right: {...}
}

AST节点类型

节点类型包括:

  • Literals
  • Programs
  • Functions
  • Statements
  • Declarations
  • Misc
  • Expressions
  • Template Literals
  • Patterns
  • Patterns
  • Classes
  • Modules

具体构成及说明文档位于@babel/parser ast specification

AST节点属性

json
{
type: ...,
start: 0,
end: 38,
loc: {
start: {
line: 1,
column: 0
},
end: {
line: 3,
column: 1
}
},
...
}

js parser历史:

SpiderMonkey的AST标准esprima,后来形成了estree标准

accorn也是estree标准但是比esprima更快而且支持插件对语法进行扩展

eslint的espree源自esprima但是espree2.0基于accron重新实现也适用accorn的插件机制扩展语法

babel parser(babylon)在accorn的基础上对AST节点和属性做了扩展

转换过程

语法解析

通过@babel/parser将源码解析(词法分析、语法分析)为ast,具体options配置项决定产出形式

js
require('@babel/parser').parse("soucecode", {
sourceType: 'module',
plugins: [
'jsx'
]
})

AST遍历

通过@babel/traverse进行遍历ast节点进行增删改,在遍历过程中通过访问者模式(visitor)对指定的节点类型例如:FunctionDeclaration进行访问,同时可使用@babel/types提供的工具函数进行节点判断及生成

ts
interface traverse {
ast: AST
visitor: (path, state) => {}
}
js
traverse(ast, {
FunctionDeclaration: {
enter(path, state) {}, // 进入节点时调用
exit(path, state) {} // 离开节点时调用
}
})
// 只指定一个函数那就是enter阶段调用
traverse(ast, {
FunctionDeclaration(path, state) {} // 进入节点时调用
})
// 多个ast类型节点处理,通过"|"
// 通过别名指定离开各种 Declaration 节点时调用
traverse(ast, {
'FunctionDeclaration|VariableDeclaration'(path, state) {}
})

path记录了节点间相互关系的路径,同时提供了很多属性和方法方便遍历操作

path.scope包含了js中能生成作用域(模块、函数、块)等所形成的作用域链

state保存了在传递到对应节点是的options和file以及共享数据

转换后生成

使用@babel/generatorast生成代码字符串

ts
function (ast: Object, opts: Object, code: string): {code, map}
js
import generate from "@babel/generator";
const { code, map } = generate(ast, { sourceMaps: true })

同时可使用@babel/code-frame在生成过程中打印报错代码位置

js
import { codeFrameColumns } from "@babel/code-frame";
const rawLines = `class Foo {
constructor()
}`;
const location = { start: { line: 2, column: 16 } };
const result = codeFrameColumns(rawLines, location, {
/* options */
});
console.log(result);

@babel/generator通过对应AST生成方法进行生成

sourcemap通常用于从目标代码映射到源码位置,具体创建、消费、及源码节点格式可以查看文档

to be continued

Copyright © 2023, Daily learning records and sharing. Build by