基于 Babel 8 源码分析
2026-03-25 | 技术深度解读
第一部分:Babel 概述
第二部分:Parser 解析器
第三部分:Traverse 遍历器
第四部分:Generator 与实战
Babel 是 JavaScript 编译器,将 ES6+ 代码转换为向后兼容版本。
核心功能
核心包
┌─────────────────────────────────────────┐
│ Babel 编译器架构 │
├─────────────────────────────────────────┤
│ ┌─────────────────────────────────────┐│
│ │ @babel/parser ││
│ │ 源码 → AST (抽象语法树) ││
│ └─────────────────────────────────────┘│
│ ↓ │
│ ┌─────────────────────────────────────┐│
│ │ @babel/traverse ││
│ │ AST 遍历 + 插件转换 ││
│ │ • NodePath 路径操作 ││
│ │ • Scope 作用域管理 ││
│ └─────────────────────────────────────┘│
│ ↓ │
│ ┌─────────────────────────────────────┐│
│ │ @babel/generator ││
│ │ AST → 目标代码 + SourceMap ││
│ └─────────────────────────────────────┘│
└─────────────────────────────────────────┘
1. Parse (解析) - 将源码解析为 AST
2. Transform (转换) - 遍历 AST 并应用插件
3. Generate (生成) - 将 AST 生成目标代码
// babel-core/src/transform.ts
const transformRunner = gensync(function* transform(
code: string,
opts?: InputOptions | null,
): Handler<FileResult | null> {
// 1. 加载配置
const config: ResolvedConfig | null = yield* loadConfig(opts);
if (config === null) return null;
// 2. 执行转换
return yield* run(config, code);
});
| 模块 | 依赖 | 职责 |
|---|---|---|
| @babel/core | 所有模块 | 协调编译流程 |
| @babel/parser | 无 | 词法+语法分析 |
| @babel/traverse | @babel/types | AST 遍历 |
| @babel/generator | @babel/types | 代码生成 |
| @babel/types | 无 | AST 工具函数 |
@babel/parser 将 JavaScript 源码解析为 AST。
解析阶段
支持特性
// babel-parser/src/tokenizer/index.ts
export default abstract class Tokenizer extends CommentsParser {
isLookahead: boolean;
tokens: (Token | N.Comment)[] = [];
constructor(options: OptionsWithDefaults, input: string) {
super();
this.state = new State(); // 解析状态
this.state.init(options);
this.input = input; // 源码字符串
this.length = input.length;
this.comments = [];
this.isLookahead = false;
}
// 移动到下一个 token
next(): void {
this.checkKeywordEscapes();
if (this.optionFlags & OptionFlags.Tokens) {
this.pushToken(new Token(this.state));
}
this.nextToken();
}
}
// babel-parser/src/tokenizer/types.ts
export const enum TokenType {
// 字面量
num, bigint, string, regexp,
// 标识符
name,
// 关键字
break, case, catch, continue, debugger,
default, delete, do, else, export,
extends, finally, for, function, if,
import, instanceof, new, return, super,
switch, this, throw, try, typeof,
var, void, while, with, yield,
// 运算符
eq, assign, incDec, bang, tilde,
question, colon, logicalOR, logicalAND,
// 标点符号
parenL, parenR, bracketL, bracketR,
braceL, braceR, semi, comma, dot,
}
// 跳过空白和注释
skipSpace(): void {
loop: while (this.state.pos < this.length) {
const ch = this.input.charCodeAt(this.state.pos);
switch (ch) {
case charCodes.space:
case charCodes.tab:
++this.state.pos;
break;
case charCodes.lineFeed:
++this.state.pos;
++this.state.curLine;
this.state.lineStart = this.state.pos;
break;
case charCodes.slash:
// 处理注释
if (next === charCodes.asterisk) {
this.skipBlockComment("*/");
} else if (next === charCodes.slash) {
this.skipLineComment(2);
}
break;
}
}
}
// 读取下一个 token
nextToken(): void {
this.skipSpace(); // 跳过空白
this.state.start = this.state.pos; // 记录起始位置
if (!this.isLookahead) {
this.state.startLoc = this.state.curPosition();
}
if (this.state.pos >= this.length) {
this.finishToken(tt.eof); // 文件结束
return;
}
// 根据字符码获取 token
this.getTokenFromCode(
this.codePointAtPos(this.state.pos)
);
}
getTokenFromCode(code: number): void {
switch (code) {
case charCodes.dot:
this.readToken_dot();
return;
case charCodes.leftParenthesis:
++this.state.pos;
this.finishToken(tt.parenL);
return;
case charCodes.quotationMark:
case charCodes.apostrophe:
this.readString(code);
return;
case charCodes.digit0:
case charCodes.digit1:
// ... 更多数字
this.readNumber(false);
return;
default:
if (isIdentifierStart(code)) {
this.readWord(code);
}
}
}
// 读取标识符或关键字
readWord(firstCode?: number): void {
const word = this.readWord1(firstCode);
const type = keywordTypes.get(word); // 检查是否关键字
if (type !== undefined) {
// 是关键字
this.finishToken(type, tokenLabelName(type));
} else {
// 是普通标识符
this.finishToken(tt.name, word);
}
}
// 读取标识符内容(处理 Unicode 转义)
readWord1(firstCode?: number): string {
this.state.containsEsc = false;
let word = "";
// ... 处理标识符字符
return word + this.input.slice(chunkStart, this.state.pos);
}
readNumber(startsWithDot: boolean): void {
const start = this.state.pos;
let isFloat = false, isBigInt = false;
// 读取整数部分
this.readInt(10);
// 读取小数部分
let next = this.input.charCodeAt(this.state.pos);
if (next === charCodes.dot) {
++this.state.pos;
this.readInt(10);
isFloat = true;
}
// 读取指数部分
if (next === charCodes.uppercaseE || next === charCodes.lowercaseE) {
next = this.input.charCodeAt(++this.state.pos);
if (next === charCodes.plusSign || next === charCodes.dash) {
++this.state.pos;
}
this.readInt(10);
isFloat = true;
}
// BigInt 后缀
if (next === charCodes.lowercaseN) {
++this.state.pos;
isBigInt = true;
}
}
readString(quote: number): void {
const { str, pos, curLine, lineStart } = readStringContents(
quote === charCodes.quotationMark ? "double" : "single",
this.input,
this.state.pos + 1, // 跳过引号
this.state.lineStart,
this.state.curLine,
this.errorHandlers_readStringContents_string,
);
this.state.pos = pos + 1; // 跳过结束引号
this.state.lineStart = lineStart;
this.state.curLine = curLine;
this.finishToken(tt.string, str);
}
// 示例:解析变量声明
parseVarStatement(): N.VariableDeclaration {
const startLoc = this.state.startLoc;
const kind = this.state.value; // var/let/const
const declarations: N.VariableDeclarator[] = [];
do {
const decl = this.parseVarDeclarator();
declarations.push(decl);
} while (this.eat(tt.comma));
this.semicolon();
return this.finishNode(
this.startNodeAt(startLoc),
{
type: "VariableDeclaration",
kind,
declarations,
}
);
}
@babel/traverse 遍历和操作 AST 节点。
核心功能
核心概念
// babel-traverse/src/index.ts
function traverse<S>(
parent: t.Node,
opts: TraverseOptions<S>,
scope?: Scope | null,
state?: S,
parentPath?: NodePath,
visitSelf?: boolean,
): void {
if (!parent) return;
if (!opts.noScope && !scope) {
if (parent.type !== "Program" && parent.type !== "File") {
throw new Error(
"You must pass a scope and parentPath unless " +
"traversing a Program/File."
);
}
}
// 展开访问者
visitors.explode(opts as Visitor);
// 执行遍历
traverseNode(parent, opts as ExplodedVisitor, scope, state, parentPath);
}
// 访问者对象定义
interface Visitor<S = t.Node> {
// 进入节点时调用
Identifier?(path: NodePath<t.Identifier>, state: S): void;
// 退出节点时调用
Identifier?: {
enter?(path: NodePath<t.Identifier>, state: S): void;
exit?(path: NodePath<t.Identifier>, state: S): void;
};
// 通用访问者
enter?(path: NodePath, state: S): void;
exit?(path: NodePath, state: S): void;
}
// 使用示例
traverse(ast, {
Identifier(path) {
console.log(path.node.name);
},
FunctionDeclaration: {
enter(path) { /* 进入函数 */ },
exit(path) { /* 退出函数 */ }
}
});
// babel-traverse/src/path/index.ts
class NodePath {
parent: t.Node; // 父节点
hub: HubInterface; // Hub 实例
context: TraversalContext; // 遍历上下文
scope: Scope; // 作用域
contexts: TraversalContext[] = [];
state: any = null;
opts: ExplodedTraverseOptions;
// 标志位
@bit(REMOVED) accessor removed = false;
@bit(SHOULD_STOP) accessor shouldStop = false;
@bit(SHOULD_SKIP) accessor shouldSkip = false;
// 节点信息
parentPath: NodePath | null = null;
container: t.Node | t.Node[] | null = null;
key: string | number | null = null;
node: t.Node | null = null;
type: t.Node["type"] | null = null;
}
| 属性 | 类型 | 描述 |
|---|---|---|
| node | t.Node | 当前 AST 节点 |
| parent | t.Node | 父节点 |
| parentPath | NodePath | 父节点路径 |
| scope | Scope | 当前作用域 |
| key | string|number | 在父节点中的键 |
| container | Node|Node[] | 包含当前节点的容器 |
// 节点查询方法
getOpposite(): NodePath; // 获取对立节点
getCompletionRecords(): NodePath[]; // 获取完成记录
getSibling(key): NodePath; // 获取兄弟节点
getPrevSibling(): NodePath; // 获取前一个兄弟
getNextSibling(): NodePath; // 获取后一个兄弟
getAllNextSiblings(): NodePath[]; // 获取所有后续兄弟
getAllPrevSiblings(): NodePath[]; // 获取所有前序兄弟
// 节点获取
get(key): NodePath | NodePath[]; // 获取子节点
getAssignmentIdentifiers(): Record;
getBindingIdentifiers(): Record;
// 替换节点
replaceWith(node: t.Node): void {
// 替换为单个节点
}
replaceWithMultiple(nodes: t.Node[]): void {
// 替换为多个节点
}
replaceInline(nodes: t.Node | t.Node[]): void {
// 智能替换
}
replaceWithSourceString(code: string): void {
// 用源码字符串替换
}
// 删除节点
remove(): void {
// 从 AST 中移除当前节点
this.removed = true;
}
// 查找祖先
findParent(callback): NodePath | null;
find(callback): NodePath | null;
getFunctionParent(): NodePath | null;
getStatementParent(): NodePath | null;
// 遍历子节点
traverse(visitor: Visitor, state?: any): void {
traverse(this.node, visitor, this.scope, state, this);
}
// 路径位置
getPathLocation(): string {
const parts = [];
let path = this;
do {
let key = path.key;
if (path.inList) key = `${path.listKey}[${key}]`;
parts.unshift(key);
} while ((path = path.parentPath));
return parts.join(".");
}
// babel-traverse/src/scope/index.ts
class Scope {
uid: number; // 唯一标识
path: NodePath; // 关联的路径
block: t.Pattern | t.Scopable; // 作用域块
bindings: Record; // 绑定
globals: Record; // 全局变量
labels: Map; // 标签
data: Record; // 自定义数据
// 全局变量列表
static globals = [...globalsBuiltinLower, ...globalsBuiltinUpper];
// 上下文变量
static contextVariables = ["arguments", "undefined", "Infinity", "NaN"];
}
// 绑定类型
type BindingKind =
| "var" // var 声明
| "let" // let 声明
| "const" // const 声明
| "module" // 模块导入
| "hoisted" // 函数提升
| "param" // 函数参数
| "local" // 本地绑定
| "unknown"; // 未知类型
// Binding 类
class Binding {
identifier: t.Identifier; // 标识符
scope: Scope; // 所属作用域
path: NodePath; // 声明路径
kind: BindingKind; // 绑定类型
constant: boolean; // 是否常量
references: number; // 引用次数
referencePaths: NodePath[]; // 引用路径
constantViolations: NodePath[]; // 常量违规
}
// 生成唯一标识符
generateUid(name = "temp"): string {
name = toIdentifier(name).replace(/^_+/, "").replace(/\d+$/g, "");
let uid;
let i = 0;
do {
uid = `_${name}`;
if (i >= 11) uid += i - 1;
else if (i >= 9) uid += i - 9;
else if (i >= 1) uid += i + 1;
i++;
} while (this.hasBinding(uid) || this.hasGlobal(uid));
return uid;
}
// 获取绑定
getBinding(name: string): Binding | undefined;
// 检查绑定
hasBinding(name: string): boolean;
// 重命名
rename(oldName: string, newName?: string): void;
// 收集作用域信息的访问者
const collectorVisitor: Visitor<CollectVisitorState> = {
Declaration(path) {
// 处理声明
const parent = path.scope.getFunctionParent()
|| path.scope.getProgramParent();
parent.registerDeclaration(path);
},
ReferencedIdentifier(path, state) {
// 记录引用
state.references.push(path);
},
AssignmentExpression(path, state) {
// 记录赋值
state.assignments.push(path);
},
Function(path) {
// 注册函数参数
for (const param of path.get("params")) {
path.scope.registerBinding("param", param);
}
}
};
@babel/generator 将 AST 转换回代码字符串。
核心功能
配置选项
// babel-generator/src/index.ts
export function generate(
ast: t.Node,
opts: GeneratorOptions = {},
code?: string | Record,
): GeneratorResult {
// 规范化选项
const format = normalizeOptions(code, opts, ast);
// 创建 SourceMap
const map = opts.sourceMaps ? new SourceMap(opts, code) : null;
// 创建 Printer
const printer = new Printer(
format,
map,
(ast as any).tokens,
typeof code === "string" ? code : null,
);
// 生成代码
return printer.generate(ast);
}
interface GeneratorResult {
code: string;
map: EncodedSourceMap | null;
decodedMap: DecodedSourceMap | undefined;
rawMappings: Mapping[] | undefined;
}
class Printer {
constructor(
format: Format,
map: SourceMap | null,
tokens: t.Token[] | null,
code: string | null
) {
this.format = format;
this.map = map;
this.tokens = tokens;
this.code = code;
this._indent = 0;
this._endsWithWord = false;
}
// 生成代码
generate(ast: t.Node): GeneratorResult {
this.print(ast);
return {
code: this._buf.toString(),
map: this.map?.get(),
decodedMap: this.map?.getDecoded(),
rawMappings: this.map?.getRawMappings()
};
}
// 打印节点
print(node: t.Node): void {
// 根据节点类型调用对应的打印方法
}
}
interface Format {
// 注释
auxiliaryCommentBefore?: string;
auxiliaryCommentAfter?: string;
shouldPrintComment?(comment: string): boolean;
comments: boolean;
// 格式
compact?: boolean | "auto";
minified?: boolean;
concise?: boolean;
retainLines?: boolean;
retainFunctionParens?: boolean;
// 缩进
indent: {
adjustMultilineComment: boolean;
style: string; // " " 或 ""
};
// 字符串转义
jsescOption: {
quotes: "double" | "single";
wrap: boolean;
minimal: boolean;
};
}
class SourceMap {
constructor(opts: GeneratorOptions, code: string | Record) {
this._opts = opts;
this._rawMappings = [];
this._gen = new GenMapping({
sourceRoot: opts.sourceRoot,
});
}
// 添加映射
mark(
generated: Position,
original: Position,
name?: string
): void {
addMapping(this._gen, {
generated,
source: this._opts.sourceFileName!,
original,
name
});
}
// 获取编码后的 map
get(): EncodedSourceMap {
return toEncodedMap(this._gen);
}
}
@babel/types 提供 AST 节点的工具函数。
节点构建器
节点验证器
| 类别 | 节点类型 |
|---|---|
| 字面量 | StringLiteral, NumberLiteral, BooleanLiteral, NullLiteral |
| 标识符 | Identifier, PrivateName |
| 表达式 | BinaryExpression, CallExpression, ArrowFunctionExpression |
| 语句 | ExpressionStatement, VariableDeclaration, FunctionDeclaration |
| 声明 | VariableDeclarator, FunctionDeclaration, ClassDeclaration |
| 模式 | ObjectPattern, ArrayPattern, RestElement |
// babel-types/src/builders/generated/index.ts
export function identifier(name: string): t.Identifier {
return {
type: "Identifier",
name
};
}
export function binaryExpression(
operator: string,
left: t.Expression,
right: t.Expression
): t.BinaryExpression {
return {
type: "BinaryExpression",
operator,
left,
right
};
}
export function functionDeclaration(
id: t.Identifier | null,
params: t.Pattern[],
body: t.BlockStatement,
generator?: boolean,
async?: boolean
): t.FunctionDeclaration {
return { type: "FunctionDeclaration", id, params, body, generator, async };
}
// babel-types/src/validators/generated/index.ts
export function isIdentifier(
node: t.Node | null | undefined,
opts?: object
): node is t.Identifier {
if (!node) return false;
if (node.type !== "Identifier") return false;
return !opts || shallowEqual(node, opts);
}
export function isBinaryExpression(
node: t.Node | null | undefined,
opts?: object
): node is t.BinaryExpression {
if (!node) return false;
if (node.type !== "BinaryExpression") return false;
return !opts || shallowEqual(node, opts);
}
// 通用类型检查
export function is(type: string, node: t.Node, opts?: object): boolean {
return isIdentifier(node) || isStringLiteral(node) || ...;
}
// babel-types/src/converters
export function toExpression(node: t.Node): t.Expression {
if (isExpression(node)) return node;
if (isStatement(node)) return expressionStatement(node);
throw new Error("Cannot convert node to expression");
}
export function toIdentifier(name: string): string {
// 转换为有效标识符
name = name.replace(/[^a-zA-Z0-9$_]/g, "_");
if (/^\d/.test(name)) name = "_" + name;
return name;
}
export function valueToNode(value: any): t.Node {
if (value === undefined) return identifier("undefined");
if (value === null) return nullLiteral();
if (typeof value === "string") return stringLiteral(value);
if (typeof value === "number") return numericLiteral(value);
if (typeof value === "boolean") return booleanLiteral(value);
if (Array.isArray(value)) return arrayExpression(value.map(valueToNode));
if (typeof value === "object") {
return objectExpression(
Object.entries(value).map(([k, v]) =>
objectProperty(identifier(k), valueToNode(v))
)
);
}
}
// babel-core/src/transform.ts
export const transform: Transform = function transform(
code,
optsOrCallback,
maybeCallback?
) {
let opts, callback;
if (typeof optsOrCallback === "function") {
callback = optsOrCallback;
opts = undefined;
} else {
opts = optsOrCallback;
callback = maybeCallback;
}
if (callback === undefined) {
throw new Error(
"Starting from Babel 8.0.0, the 'transform' function " +
"expects a callback. Use 'transformSync'."
);
}
beginHiddenCallStack(transformRunner.errback)(code, opts, callback);
return null;
};
export function transformSync(...args) {
return beginHiddenCallStack(transformRunner.sync)(...args);
}
// babel-core/src/transformation/index.ts
export function* run(
config: ResolvedConfig,
code: string,
ast?: t.File | t.Program | null,
): Handler<FileResult> {
// 1. 规范化文件
const file = yield* normalizeFile(
config.passes,
normalizeOptions(config),
code,
ast,
);
// 2. 执行转换
try {
yield* transformFile(file, config.passes);
} catch (e) {
e.message = `${opts.filename ?? "unknown file"}: ${e.message}`;
throw e;
}
// 3. 生成代码
let outputCode, outputMap;
if (opts.code !== false) {
({ outputCode, outputMap } = generateCode(config.passes, file));
}
return { metadata, options, ast, code: outputCode, map: outputMap };
}
function* transformFile(file: File, pluginPasses: PluginPasses) {
const async = yield* isAsync();
for (const pluginPairs of pluginPasses) {
const visitors = [];
const passes = [];
for (const plugin of pluginPairs.concat([loadBlockHoistPlugin()])) {
const pass = new PluginPass(file, plugin.key, plugin.options, async);
passes.push(pass);
visitors.push(plugin.visitor!);
}
// 调用 pre 钩子
for (const [plugin, pass] of passPairs) {
if (plugin.pre) yield* fn.call(pass, file);
}
// 合并访问者并遍历
const visitor = traverse.visitors.merge(visitors, passes);
traverse(file.ast.program, visitor, file.scope);
// 调用 post 钩子
for (const [plugin, pass] of passPairs) {
if (plugin.post) yield* fn.call(pass, file);
}
}
}
// babel-core/src/transformation/plugin-pass.ts
class PluginPass {
file: File;
key: string;
options: object;
scope: Scope;
constructor(file: File, key: string, options: object, async: boolean) {
this.file = file;
this.key = key;
this.options = options;
this.scope = file.scope;
}
// 添加辅助函数
addHelper(name: string): t.Identifier {
return this.file.addHelper(name);
}
// 添加导入
addImport(
source: string,
nameHint?: string
): t.Identifier | null {
return this.file.addImport(source, nameHint);
}
// 生成唯一标识符
generateUidIdentifier(name?: string): t.Identifier {
return this.scope.generateUidIdentifier(name);
}
}
// 源码
const x = 1 + 2;
// AST
{
type: "VariableDeclaration",
kind: "const",
declarations: [{
type: "VariableDeclarator",
id: {
type: "Identifier",
name: "x"
},
init: {
type: "BinaryExpression",
operator: "+",
left: { type: "NumericLiteral", value: 1 },
right: { type: "NumericLiteral", value: 2 }
}
}]
}
// 箭头函数 AST
const add = (a, b) => a + b;
// AST 节点
{
type: "VariableDeclaration",
declarations: [{
type: "VariableDeclarator",
id: { type: "Identifier", name: "add" },
init: {
type: "ArrowFunctionExpression",
params: [
{ type: "Identifier", name: "a" },
{ type: "Identifier", name: "b" }
],
body: {
type: "BinaryExpression",
operator: "+",
left: { type: "Identifier", name: "a" },
right: { type: "Identifier", name: "b" }
},
async: false,
expression: true
}
}]
}
// 插件:将箭头函数转为普通函数
export default function arrowFunctionPlugin() {
return {
visitor: {
ArrowFunctionExpression(path) {
const { node } = path;
// 创建普通函数表达式
const fn = t.functionExpression(
null, // 匿名函数
node.params,
t.blockStatement([
t.returnStatement(node.body)
]),
node.generator,
node.async
);
// 替换节点
path.replaceWith(fn);
}
}
};
}
// 插件:将 let 声明转为 var
export default function letToVarPlugin() {
return {
visitor: {
VariableDeclaration(path) {
if (path.node.kind === "let") {
// 克隆节点并修改 kind
path.replaceWith(
t.variableDeclaration(
"var",
path.node.declarations
)
);
}
}
}
};
}
// 转换前
let x = 1;
// 转换后
var x = 1;
// 插件:移除 console.log
export default function removeConsolePlugin() {
return {
visitor: {
CallExpression(path) {
const callee = path.node.callee;
// 检查是否是 console.log
if (
t.isMemberExpression(callee) &&
t.isIdentifier(callee.object, { name: "console" }) &&
t.isIdentifier(callee.property, { name: "log" })
) {
// 移除整个语句
path.parentPath.remove();
}
}
}
};
}
// 转换前
console.log("debug info");
const x = 1;
// 转换后
const x = 1;
Parser 优化
Traverse 优化
Generator 优化
整体优化
1. 使用 NodePath 而非直接操作节点
2. 利用 Scope 管理变量绑定
3. 使用 @babel/types 构建节点
4. 避免在遍历中修改集合
5. 使用 path.skip() 控制遍历
// ❌ 错误:直接修改节点
path.node.body.push(newNode);
// ✅ 正确:使用 NodePath API
path.pushContainer("body", newNode);
// ✅ 使用 scope 生成唯一变量
const temp = path.scope.generateUidIdentifier("temp");
核心要点
扩展阅读
🟡 Babel - JavaScript 编译器的艺术