Babel API
参考
- https://www.babeljs.cn/docs/babel-parser
- https://juejin.cn/book/6946117847848321055/section/6946578914764390434?enter_from=course_center&utm_source=course_center#heading-1 (需要付费)
- babel github https://github.com/babel/babel
我们知道 babel 的编译流程分为三步:parse、transform、generate,每一步都暴露了一些 api 出来。
-
parse
阶段有@babel/parser
,功能是把源码转成 AST -
transform
阶段有@babel/traverse
,可以遍历 AST,并调用visitor
函数修改 AST,- 修改 AST 自然涉及到 AST 的判断、创建、修改等,这时候就需要
@babel/types
了 - 当需要批量创建 AST 的时候可以使用
@babel/template
来简化 AST 创建逻辑。
- 修改 AST 自然涉及到 AST 的判断、创建、修改等,这时候就需要
-
generate
阶段会把 AST 打印为目标代码字符串,同时生成sourcemap
,需要@babel/generator
包 -
中途遇到错误想打印代码位置的时候,使用
@babel/code-frame
包 -
babel
的整体功能通过@babel/core
提供,基于上面的包完成 babel 整体的编译流程,并应用 plugin 和 preset。
主要学习的就是
-
@babel/parser
-
@babel/traverse
-
@babel/generator
-
@babel/types
-
@babel/template
这五个包的 api 的使用。
这些包的 api 都可以在文档里查看:
@babel/parser
下面的介绍都可以在官方文档中看到
Babel 分析器(前身为 Babylon)是 Babel 中使用的 JavaScript 分析器。
默认启用最新的 ECMAScript 版本(ES2020)。
支持 JSX、Flow 和 Typescript。
支持实验性语言提案(接受任何至少stage-0的 PR)。
它提供了有两个 api:
-
babelParser.parse(code, [options])
parse()
将提供的代码作为整个 ECMAScript 程序进行解析
-
babelParser.parseExpression(code, [options])
parseExpression()
返回的 AST 根节点是是 Expression(表达式的 AST),粒度不同。
详细的 options 可以查看文档。其实主要分为两类,一是 parse 的内容是什么,二是以什么方式去 parse
parse 的内容是什么:
-
plugins
: 指定jsx、typescript、flow 等插件来解析对应的语法 -
allowXxx
: 指定一些语法是否允许,比如函数外的 await、没声明的 export等 -
sourceType
: 指定是否支持解析模块语法,有 module、script、unambiguous 3个取值:module
:解析es module
语法script
:不解析es module
语法unambiguous
:根据内容是否有import
和export
来自动设置module
还是script
const parser = require('@babel/parser')
const code = `interface Shape {
name: string;
width: number;
height: number;
color?: string;
}
function area(shape : Shape) {
var area = shape.width * shape.height;
return "I'm " + shape.name + " with area " + area + " cm squared";
}
console.log( area( {name: "rectangle", width: 30, height: 15} ) );`
const ast = parser.parse("code", {
sourceType: 'unambiguous',
plugins: ['typescript']
});
console.log(JSON.stringify(ast))
当你使用在线astexplorer
的时候,这里也同样支持 parser options
的设置
@babel/traverse
文档地址:https://www.babeljs.cn/docs/babel-traverse
parse
出的 AST 由 @babel/traverse
来遍历和修改,babel traverse
包提供了 traverse
方法:
traverse
方法
下面是 traverse
方法的类型声明
declare const traverse: {
<S>(parent: Node, opts: TraverseOptions<S>, scope: Scope | undefined, state: S, parentPath?: NodePath): void;
(parent: Node, opts?: TraverseOptions, scope?: Scope, state?: any, parentPath?: NodePath): void;
visitors: typeof visitors;
verify: typeof visitors.verify;
explode: typeof visitors.explode;
cheap: (node: Node, enter: (node: Node) => void) => void;
node: (
node: Node,
opts: TraverseOptions,
scope?: Scope,
state?: any,
path?: NodePath,
skipKeys?: Record<string, boolean>,
) => void;
clearNode: (node: Node, opts?: RemovePropertiesOptions) => void;
removeProperties: (tree: Node, opts?: RemovePropertiesOptions) => Node;
hasType: (tree: Node, type: Node["type"], denylistTypes?: string[]) => boolean;
cache: typeof cache;
};
前面两个参数是比较常用的,parent
指定要遍历的 AST
节点,opts
指定 visitor
函数。babel
会在遍历 parent
对应的 AST
时调用相应的 visitor
函数。
visitor
函数
export interface VisitNodeObject<S, P extends Node> {
enter?: VisitNodeFunction<S, P>; // 进入节点执行
exit?: VisitNodeFunction<S, P>; // 离开节点执行
}
traverse(ast, {
enter(path) {
console.log('enter')
},
exit(path) {
console.log('exit')
},
})
$ node app.js
enter
enter
enter
exit
exit
exit
这里我们输出遍历节点的type会更清晰
traverse(ast, {
enter(path) {
console.log('enter',path.type)
},
exit(path) {
console.log('exit',path.type)
},
})
$ node app.js
enter Program
enter ExpressionStatement
enter Identifier
exit Identifier
exit ExpressionStatement
exit Program
基本可以看出很像一个栈数据结构的感觉,先进后出
先从最外面的节点
Program enter
然后
enter
内部子节点ExpressionStatement
继续
enter
到最内层的Identifier
,然后在反向
exit
先退出Identifier
,然后
exit
ExpressionStatement
最后
exit
Program
此外,我们可以针对语法树中的特定节点类型,比如下面是针对 Program
节点的
traverse(ast, {
Program: {
enter(path) {
console.log('enter', path.type)
},
exit(path) {
console.log('exit', path.type)
},
},
})
$ node app.js
enter Program
exit Program
可以看到其他只进行了 Program
节点的遍历
如果只存在一个函数的话,那么默认就是 enter
阶段的调用
traverse(ast, {
Program(path) {}
})
这里可以从源码中看到https://github.com/babel/babel/blob/main/packages/babel-types/src/traverse/traverse.ts
enter
时调用是在**遍历当前节点的子节点** 前
调用,exit
时调用是**遍历完当前节点的子节点** 后
调用。
visitor
函数的参数 - path
AST 是棵树,遍历过程中肯定是有个路径的,path 就记录了这个路径
在整个过程中,祖先信息(包括父节点、父节点的键以及父节点的索引)被保存在一个ancestors
数组中,并传递给每个处理程序。这个就是path
记录了遍历过的所有节点!
// 定义TraversalAncestors类型,用于存储祖先信息
export type TraversalAncestors = Array<{
node: t.Node; // 祖先节点
key: string; // 祖先节点的键
index?: number; // 如果祖先节点是数组类型,那么这个字段表示当前节点在数组中的索引
}>;
// 定义TraversalHandler类型,用于描述一个处理程序
export type TraversalHandler<T> = (
this: undefined,
node: t.Node, // 当前节点
parent: TraversalAncestors, // 祖先信息
state: T, // 用户提供的状态对象
) => void;
// 定义TraversalHandlers类型,用于描述一组处理程序
export type TraversalHandlers<T> = {
enter?: TraversalHandler<T>; // 预处理程序
exit?: TraversalHandler<T>; // 后处理程序
};
// 实现一个通用的AST遍历器
export default function traverse<T>(
node: t.Node, // 要遍历的节点
handlers: TraversalHandler<T> | TraversalHandlers<T>, // 处理程序或者一组处理程序
state?: T, // 用户提供的状态对象
): void {
if (typeof handlers === "function") {
handlers = { enter: handlers }; // 如果handlers只是一个函数,则将其转化为{ enter: handlers }
}
const { enter, exit } = handlers; // 获取处理程序
traverseSimpleImpl(node, enter, exit, state, []); // 调用内部函数进行实际的遍历操作
}
function traverseSimpleImpl<T>(
node: any, // 当前节点
enter: Function | undefined, // 预处理程序
exit: Function | undefined, // 后处理程序
state: T | undefined, // 用户提供的状态对象
ancestors: TraversalAncestors, // 祖先信息
) {
const keys = VISITOR_KEYS[node.type]; // 获取当前节点类型的所有子节点键名
if (!keys) return; // 如果没有子节点键名,则直接返回
if (enter) enter(node, ancestors, state); // 如果有预处理程序,则调用预处理程序
for (const key of keys) { // 对每个子节点键进行遍历
const subNode = node[key];
if (Array.isArray(subNode)) { // 如果子节点是数组类型
for (let i = 0; i < subNode.length; i++) { // 遍历数组中的每个元素
const child = subNode[i];
if (!child) continue; // 如果元素为null或undefined,则跳过
ancestors.push({ // 将祖先信息推入栈顶
node,
key,
index: i,
});
traverseSimpleImpl(child, enter, exit, state, ancestors); // 递归地遍历元素
ancestors.pop(); // 弹出栈顶的祖先信息
}
} else if (subNode) { // 如果子节点是非数组类型
ancestors.push({ // 将祖先信息推入栈顶
node,
key,
});
traverseSimpleImpl(subNode, enter, exit, state, ancestors); // 递归地遍历子节点
ancestors.pop(); // 弹出栈顶的祖先信息
}
}
if (exit) exit(node, ancestors, state); // 如果有后处理程序,则调用后处理程序
}
path 有很多属性和方法,比如记录父子、兄弟等关系的:
path.node
指向当前 AST 节点path.parent
指向父级 AST 节点path.getSibling
、path.getNextSibling
、path.getPrevSibling
获取兄弟节点path.find
从当前节点向上查找节点path.get
、path.set
获取 / 设置属性的 path
还有作用域相关的:
path.scope
获取当前节点的作用域信息
判断 AST 类型的:
path.isXxx
判断当前节点是不是 xx 类型path.assertXxx
判断当前节点是不是xx
类型,不是则抛出异常
增删改 AST 的:
path.insertBefore
、path.insertAfter
插入节点path.replaceWith
、path.replaceWithMultiple
、replaceWithSourceString
替换节点path.remove
删除节点
跳过遍历的:
path.skip
跳过当前节点的子节点的遍历path.stop
结束后续遍历
visitor
函数的参数 - state
这个很容易理解,节点之间是有传输数据的需求的。不同状态下可能会做不同的处理,这就是为什么这个参数叫做 state
。
源码中是有递归传递state
的,并且为 enter or exit
的回调参数
插件会通过 state
传递 options
和 file
信息,我们也可以通过 state
存储一些遍历过程中的共享数据。
@babel/types
This module contains methods for building ASTs manually and for checking the types of AST nodes.
该模块包含手动构建 AST 和检查 AST 节点类型的方法。
举例来说,如果要创建IfStatement就可以调用
t.ifStatement(test, consequent, alternate);
而判断节点是否是 IfStatement 就可以调用 isIfStatement 或者 assertIfStatement
t.isIfStatement(node, opts);
t.assertIfStatement(node, opts);
opts 可以指定一些属性是什么值,增加更多限制条件,做更精确的判断。
t.isIdentifier(node, { name: "paths" })
isXxx
和 assertXxx
看起来很像,但是功能不大一样:isXxx
会返回 boolean
,而 assertXxx
则会在类型不一致时抛异常。
所有的 AST 的 build
、assert
的 api 可以在 babel types 文档中查。
@babel/template
提供了代码直接创建为AST树的能力
通过上面@babel/types
创建 AST 还是比较麻烦的,要一个个的创建然后组装,如果 AST 节点比较多的话需要写很多代码,这时候就可以使用 @babel/template
包来批量创建。
const template = require('@babel/template').default
const code = template(`
function add(a, b) {
return a + b;
}
`)()
console.log(code)
输出
{
"type": "FunctionDeclaration",
"params": [
{
"type": "Identifier",
"name": "a"
},
{
"type": "Identifier",
"name": "b"
}
],
"generator": false,
"async": false,
"id": {
"type": "Identifier",
"name": "add"
},
"body": {
"type": "BlockStatement",
"directives": [],
"body": [
{
"type": "ReturnStatement",
"argument": {
"type": "BinaryExpression",
"operator": "+",
"left": {
"type": "Identifier",
"name": "a"
},
"right": {
"type": "Identifier",
"name": "b"
}
}
}
]
}
}
https://babeljs.io/docs/babel-template
这个包有这些 api
:
template
模板会根据解析结果返回单个语句或语句数组,上面演示过了!
template.smart
这与默认模板 API 相同,根据解析结果返回单个节点或节点数组。(下面是个输出节点数组的例子)
const code = template.smart(`
function add(a, b) {
return a + b;
}
add(1,2)
`)()
console.log(JSON.stringify(code))
输出
[
{
"type": "FunctionDeclaration",
"params": [
{
"type": "Identifier",
"name": "a"
},
{
"type": "Identifier",
"name": "b"
}
],
"generator": false,
"async": false,
"id": {
"type": "Identifier",
"name": "add"
},
"body": {
"type": "BlockStatement",
"directives": [],
"body": [
{
"type": "ReturnStatement",
"argument": {
"type": "BinaryExpression",
"operator": "+",
"left": {
"type": "Identifier",
"name": "a"
},
"right": {
"type": "Identifier",
"name": "b"
}
}
}
]
}
},
{
"type": "ExpressionStatement",
"expression": {
"type": "CallExpression",
"callee": {
"type": "Identifier",
"name": "add"
},
"arguments": [
{
"type": "NumericLiteral",
"value": 1,
"extra": {
"rawValue": 1,
"raw": "1"
}
},
{
"type": "NumericLiteral",
"value": 2,
"extra": {
"rawValue": 2,
"raw": "2"
}
}
]
}
}
]
template.statement
返回单个语句节点,如果结果不是单个语句,则抛出异常。(例如下面输出是
IfStatement
)
const template = require('@babel/template').default
const code = template.statement(`
if(a){
console.log(a)
}
`)()
console.log(JSON.stringify(code))
输出
{
"type": "IfStatement",
"test": {
"type": "Identifier",
"name": "a"
},
"consequent": {
"type": "BlockStatement",
"directives": [],
"body": [
{
"type": "ExpressionStatement",
"expression": {
"type": "CallExpression",
"callee": {
"type": "MemberExpression",
"object": {
"type": "Identifier",
"name": "console"
},
"property": {
"type": "Identifier",
"name": "log"
},
"computed": false
},
"arguments": [
{
"type": "Identifier",
"name": "a"
}
]
}
}
]
},
"alternate": null
}
如果不为单个语句
const template = require('@babel/template').default
const code = template.statement(`
if(a){
console.log(a)
}
if(b){
console.log(b)
}
`)()
console.log(JSON.stringify(code))
执行的时候就会抛出异常 Found multiple statements but wanted one
$ node app.js
E:\better\babel_go\node_modules\@babel\template\lib\builder.js:64
throw err;
^
Error: Found multiple statements but wanted one
at E:\better\babel_go\node_modules\@babel\template\lib\formatters.js:35:11
at Object.unwrap (E:\better\babel_go\node_modules\@babel\template\lib\formatters.js:16:14)
template.statements
如其名,返回一个语句节点数组。(上面那个报错的拿这个就OK啦!)
const template = require('@babel/template').default
const code = template.statements(`
if(a){
console.log(a)
}
if(b){
console.log(b)
}
`)()
console.log(JSON.stringify(code))
输出
[
{
"type": "IfStatement",
"test": {
"type": "Identifier",
"name": "a"
},
"consequent": {
"type": "BlockStatement",
"directives": [],
"body": [
{
"type": "ExpressionStatement",
"expression": {
"type": "CallExpression",
"callee": {
"type": "MemberExpression",
"object": {
"type": "Identifier",
"name": "console"
},
"property": {
"type": "Identifier",
"name": "log"
},
"computed": false
},
"arguments": [
{
"type": "Identifier",
"name": "a"
}
]
}
}
]
},
"alternate": null
},
{
"type": "IfStatement",
"test": {
"type": "Identifier",
"name": "b"
},
"consequent": {
"type": "BlockStatement",
"directives": [],
"body": [
{
"type": "ExpressionStatement",
"expression": {
"type": "CallExpression",
"callee": {
"type": "MemberExpression",
"object": {
"type": "Identifier",
"name": "console"
},
"property": {
"type": "Identifier",
"name": "log"
},
"computed": false
},
"arguments": [
{
"type": "Identifier",
"name": "b"
}
]
}
}
]
},
"alternate": null
}
]
template.expression
返回表达式节点的AST
const template = require('@babel/template').default
const code = template.expression(`
a = 1
`)()
console.log(JSON.stringify(code))
输出
{
"type": "AssignmentExpression",
"operator": "=",
"left": {
"type": "Identifier",
"name": "a"
},
"right": {
"type": "NumericLiteral",
"value": 1,
"extra": {
"rawValue": 1,
"raw": "1"
}
},
"extra": {
"parenthesized": true,
"parenStart": 0
}
}
template.program
返回模板的 Program 节点。
const template = require('@babel/template').default
const code = template.program(`
let a = 0
if(a === 0){
a = 1
}else{
a = 2
}
console.log(a)
`)()
console.log(JSON.stringify(code))
输出
{
"type": "Program",
"sourceType": "module",
"interpreter": null,
"directives": [],
"body": [
{
"type": "VariableDeclaration",
"kind": "let",
"declarations": [
{
"type": "VariableDeclarator",
"id": {
"type": "Identifier",
"name": "a"
},
"init": {
"type": "NumericLiteral",
"value": 0,
"extra": {
"rawValue": 0,
"raw": "0"
}
}
}
]
},
{
"type": "IfStatement",
"test": {
"type": "BinaryExpression",
"operator": "===",
"left": {
"type": "Identifier",
"name": "a"
},
"right": {
"type": "NumericLiteral",
"value": 0,
"extra": {
"rawValue": 0,
"raw": "0"
}
}
},
"consequent": {
"type": "BlockStatement",
"directives": [],
"body": [
{
"type": "ExpressionStatement",
"expression": {
"type": "AssignmentExpression",
"operator": "=",
"left": {
"type": "Identifier",
"name": "a"
},
"right": {
"type": "NumericLiteral",
"value": 1,
"extra": {
"rawValue": 1,
"raw": "1"
}
}
}
}
]
},
"alternate": {
"type": "BlockStatement",
"directives": [],
"body": [
{
"type": "ExpressionStatement",
"expression": {
"type": "AssignmentExpression",
"operator": "=",
"left": {
"type": "Identifier",
"name": "a"
},
"right": {
"type": "NumericLiteral",
"value": 2,
"extra": {
"rawValue": 2,
"raw": "2"
}
}
}
}
]
}
},
{
"type": "ExpressionStatement",
"expression": {
"type": "CallExpression",
"callee": {
"type": "MemberExpression",
"object": {
"type": "Identifier",
"name": "console"
},
"property": {
"type": "Identifier",
"name": "log"
},
"computed": false
},
"arguments": [
{
"type": "Identifier",
"name": "a"
}
]
}
}
]
}
使用字符串占位符
const template = require('@babel/template').default
const generator = require("@babel/generator").default
const { identifier,stringLiteral } = require('@babel/types')
const buildRequire = template(`
var IMPORT_NAME = require(SOURCE);
`);
const ast = buildRequire({
IMPORT_NAME: identifier("cunwang"),
SOURCE: stringLiteral("cunwang"),
});
console.log(generator(ast).code)
or 使用这样 %%XXX%%
占位符
var %%IMPORT_NAME%% = require(%%SOURCE%%);
这里我们使用 generator
来打印AST 生成的代码,更直观的感受字符串占位符的效果!
var cunwang = require("cunwang");
不使用占位符,使用es6模板字符串代替
刚开始我就在想为什么不能直接使用模板字符串呢?其实是
template
这个不支持内部嵌入外部通过模板字符串引入的变量!
const template = require('@babel/template').default
const generator = require('@babel/generator').default
const { identifier, stringLiteral } = require('@babel/types')
const IMPORT_NAME = 'cunwang',
SOURCE = 'cunwang'
const buildRequire = template(`
var ${IMPORT_NAME} = require(${SOURCE});
`)
console.log(generator(buildRequire).code)
如果想使用模板字符串,那种方式更方便 通过简单的方法将字符串解析为 AST
,则可以使用 .ast
版本的模板。
const template = require('@babel/template').default
const generator = require('@babel/generator').default
const { identifier, stringLiteral } = require('@babel/types')
const IMPORT_NAME = 'cunwang',
SOURCE = 'cunwang'
const buildRequire = template.ast(`
var ${IMPORT_NAME} = require(${SOURCE});
`)
console.log(generator(buildRequire).code)
这些方法同样也接受一些 options 具体可见文档 https://babeljs.io/docs/babel-template#options
下面是一个保留注释的配置options
const template = require('@babel/template').default
const generator = require('@babel/generator').default
const { identifier, stringLiteral } = require('@babel/types')
const IMPORT_NAME = 'cunwang',
SOURCE = 'cunwang'
const buildRequire = template.ast(
`
// 注释
var ${IMPORT_NAME} = require(${SOURCE});
`,
{
preserveComments: true, // 保留注释
}
)
console.log(generator(buildRequire).code)
输出
Administrator@□□□□ MINGW64 /e/better/babel_go
$ node app.js
// 注释
var cunwang = require(cunwang);
@babel/generator
Turns an AST into code.
这个提供了一些将AST转换为目标代码字符串的能力(上面有使用过)
export default function generate(
ast: t.Node,
opts?: GeneratorOptions,
code?: string | { [filename: string]: string },
): GeneratorResult;
-
第一个参数是要打印的 AST。
-
第二个参数是 options,指定打印的一些细节,比如通过 comments 指定是否包含注释,通过 minified 指定是否包含空白字符。
-
第三个参数当多个文件合并打印的时候需要用到文档
可以看到,虽然转为AST设置了保留注释,但是generator生成code字符串因为配置了不保留注释,所以注释仍然被干掉了~
配置了 minified
代码可以去掉的空白字符也被干掉了!
生成sourcemap
const generate = require('@babel/generator').default
const { parse } = require('@babel/parser')
const a = 'var a = 1;'
const b = 'var b = 2;'
const astA = parse(a, { sourceFilename: 'a.js' })
const astB = parse(b, { sourceFilename: 'b.js' })
const ast = {
type: 'Program',
body: [].concat(astA.program.body, astB.program.body),
}
const { code, map } = generate(
ast,
{ sourceMaps: true },
{
'a.js': a,
'b.js': b,
}
)
console.log(map)
{
version: 3,
file: undefined,
names: [ 'a', 'b' ],
sourceRoot: undefined,
sources: [ 'a.js', 'b.js' ],
sourcesContent: [ 'var a = 1;', 'var b = 2;' ],
mappings: 'AAAA,IAAIA,CAAC,GAAG,CAAC;ACAT,IAAIC,CAAC,GAAG,CAAC'
}
@babel/code-frame
https://babeljs.io/docs/babel-code-frame
babel 的报错一半都会直接打印错误位置的代码,而且还能高亮,
我们打印错误信息的时候也可以用,就是 @babel/code-frame
这个包。
const { codeFrameColumns } = require('@babel/code-frame')
// 原始 code 字符串
const rawLines = `class Foo {
constructor() {
console.log("hello");
}
}`
// 指定位置
const location = {
start: { line: 2, column: 17 },
end: { line: 4, column: 3 },
}
const result = codeFrameColumns(rawLines, location, {
/* options */
highlightCode: true, // 高亮代码
})
console.log(result)
执行效果
Options
highlightCode |
boolean默认为 false,切换语法高亮显示代码的 JavaScript 终端。 |
---|---|
linesAbove |
行以上number , defaults to 2 . 提示的地方上面展示几行代码(调整在错误上方显示的行数。) |
linesBelow |
number,默认为 3 (调整显示在错误下方的行数。) |
forceColor |
boolean , defaults to false .启用此选项可强制将代码以 JavaScript 语法高亮显示(适用于非终端);覆盖 highlightCode。 |
message |
string , 字符串,否则不显示输入一个字符串,该字符串将在代码中高亮显示的位置旁边内嵌显示(如果可能)。如果无法内嵌显示,则将置于代码框架上方。 |
const { codeFrameColumns } = require('@babel/code-frame')
// 原始 code 字符串
const rawLines = `class Foo {
constructor() {
console.log("hello");
}
}`
// 指定位置
const location = {
start: { line: 2, column: 17 },
end: { line: 4, column: 3 },
}
const result = codeFrameColumns(rawLines, location, {
/* options */
highlightCode: true, // 高亮代码
message: 'Some custome error!',
})
console.log(result)
Administrator@□□□□ MINGW64 /e/better/babel_go
$ node app.js
1 | class Foo {
> 2 | constructor() {
| ^
> 3 | console.log("hello");
| ^^^^^^^^^^^^^^^^^^^^^^^^^
> 4 | }
| ^^^ Some custome error!
5 | }
@babel/core
https://babeljs.io/docs/babel-core
babel 的功能就是通过前面讲的包实现的(@babel/parser、@babel/traverse、@babel/generaotr、@babel/types、@babel/template
等)
babel 基于这些包来实现编译、插件、预设等功能的包就是 @babel/core
。完成整个编译流程,从源码到目标代码,生成 sourcemap
。实现 plugin
和 preset
的调用。
看一些API,注意下面有一些API的过期提示 例如 babel.transform
这种只能在babel 6 版本使用
In Babel 6, this method was synchronous and
transformSync
did not exist. For backward-compatibility, this function will behave synchronously if no callback is given. If you’re starting with Babel 7 and need synchronous behavior, please usetransformSync
since this backward-compatibility will be dropped in Babel 8.在 Babel 6 中,该方法是同步的,而 transformSync 并不存在。为了向后兼容,如果没有给出回调,本函数将同步运行。如果您使用的是 Babel 7,并且需要同步行为,请使用 transformSync,因为这种向后兼容性将在 Babel 8 中取消。
注意:不带 sync、async 的 api 已经被标记过时了,也就是 transformXxx 这些,后续会删掉,不建议用
直接用
transformXxxSync
和transformXxxAsync
。也就是明确是同步还是异步。
如果用了错误的版本会报错!
@babel/core
支持 plugin
和 preset
,一般我们配置的都是对象的格式,其实也有一个 api
来创建,也就是 createConfigItem
:
createConfigItem(value, options) // configItem
评论区