上一篇:babel 源码分析 一 入口
接着上篇,我们继续分析 babel 如何将原始代码转化为 AST 。
上篇中执行 normalizeFile 函数时最终执行的是下面的 parse 方法。
// babel-parser/src/index.js
export function parse(input: string, options?: Options): File {
if (options?.sourceType === "unambiguous") {
// some code
} else {
// 🌵🌵🌵 执行这里 🌵🌵🌵
return getParser(options, input).parse();
}
}
沿用上篇中的命令,这里的 options 对应就是
{
sourceType: 'module',
sourceFileName: '/Users/9527/Desktop/babel/src/index.js',
plugins: [
'classProperties',
'classPrivateProperties',
'classPrivateMethods',
'numericSeparator',
'logicalAssignment',
'nullishCoalescingOperator',
'optionalChaining',
'jsonStrings',
'optionalCatchBinding',
'asyncGenerators',
'objectRestSpread',
'exportNamespaceFrom',
'dynamicImport'
]
}
接着执行 getParser 方法
function getParser(options: ?Options, input: string): Parser {
let cls = Parser;
if (options?.plugins) {
validatePlugins(options.plugins); // 校验插件
cls = getParserClass(options.plugins);
}
// 🌵🌵🌵 执行这里 🌵🌵🌵
return new cls(options, input);
}
在 getParserClass 方法中,会将我们的插件和 parse 中内置的插件进行对比,如果没有匹配到就返回空数组,下面是 parse 内置的一些插件:
const mixinPlugins = {
estree,
jsx,
flow,
typescript,
v8intrinsic,
placeholders
};
这里没有匹配到,所以返回的还是 Parser,这个类拥有一条很长的继承链,如下
Parser
-> StatementParser
-> ExpressionParser
-> LValParser
-> NodeUtils
-> UtilParser
-> Tokenizer
-> ParserError
-> CommentsParser
-> BaseParser
// 对应的目录结构
├── parser
│ ├── base.js
│ ├── comments.js
│ ├── error-codes.js
│ ├── error-message.js
│ ├── error.js
│ ├── expression.js
│ ├── index.js
│ ├── lval.js
│ ├── node.js
│ ├── statement.js
│ └── util.js
每一个类都单独做了一些事情,然后传递给下个类,就像我们平常请假需要层层审批一样,这种模式也被称为责任链模式。
为了方便理解接下来的讲解,我们写一段最简单的 ES6 代码,看下 parse 是如何解析的,假如我们待解析的代码为:
let age = 18
那么运行入口文件中执行 getParser(options, input).parse() 时会运行下面代码:
parse(): File {
this.enterInitialScopes();
const file = this.startNode();
const program = this.startNode();
this.nextToken();
file.errors = null;
// 🌵🌵🌵 执行这里 🌵🌵🌵
this.parseTopLevel(file, program);
file.errors = this.state.errors;
return file;
}
生成了 file 和 program 两个节点,然后将这两个节点传入到 parseTopLevel 中执行。
parseTopLevel(file: N.File, program: N.Program): N.File {
// 🌵🌵🌵 执行这里 🌵🌵🌵
file.program = this.parseProgram(program);
file.comments = this.state.comments;
if (this.options.tokens) file.tokens = this.tokens;
return this.finishNode(file, "File");
}
首先是处理 program 节点,这个节点的 body 会存放我们书写代码生成的节点,不过此时还未挂载 body 暂时不存在,节点目前结构如下:
// program 节点
Node {
type: '',
start: 0,
end: 0,
loc: SourceLocation {
start: Position { line: 1, column: 0 },
end: undefined,
filename: undefined,
identifierName: undefined
},
range: undefined,
leadingComments: undefined,
trailingComments: undefined,
innerComments: undefined,
extra: undefined,
sourceType: 'module',
interpreter: null
}
接着执行 parseProgram 方法:
parseProgram(program, end = types.eof, sourceType = this.options.sourceType) {
program.sourceType = sourceType;
program.interpreter = this.parseInterpreterDirective();
// 🌵🌵🌵 执行这里 🌵🌵🌵
this.parseBlockBody(program, true, true, end);
if (this.inModule && !this.options.allowUndeclaredExports && this.scope.undefinedExports.size > 0) {
for (const [name] of Array.from(this.scope.undefinedExports)) {
const pos = this.scope.undefinedExports.get(name);
this.raise(pos, ErrorMessages.ModuleExportUndefined, name);
}
}
return this.finishNode(program, "Program");
}
将 program 节点传入到 parseBlockBody 中向下执行。
parseBlockBody(node, allowDirectives, topLevel, end, afterBlockParse) {
const body = node.body = [];
const directives = node.directives = [];
// 🌵🌵🌵 执行这里 🌵🌵🌵
this.parseBlockOrModuleBlockBody(body, allowDirectives ? directives : undefined, topLevel, end, afterBlockParse);
}
此时会给节点添加 body 属性,因为我们代码不可能只有一行,所有使用数组存放,然后继续执行 parseBlockOrModuleBlockBody 方法。
parseBlockOrModuleBlockBody(body, directives, topLevel, end, afterBlockParse) {
const oldStrict = this.state.strict;
let hasStrictModeDirective = false;
let parsedNonDirective = false;
while (!this.match(end)) {
// 🌵🌵🌵 执行这里 🌵🌵🌵
const stmt = this.parseStatement(null, topLevel);
if (directives && !parsedNonDirective) {
if (this.isValidDirective(stmt)) {
const directive = this.stmtToDirective(stmt);
directives.push(directive);
if (!hasStrictModeDirective && directive.value.value === "use strict") {
hasStrictModeDirective = true;
this.setStrict(true);
}
continue;
}
parsedNonDirective = true;
this.state.strictErrors.clear();
}
body.push(stmt);
}
if (afterBlockParse) {
afterBlockParse.call(this, hasStrictModeDirective);
}
if (!oldStrict) {
this.setStrict(false);
}
this.next();
}
这里使用 while 来循环判断,如果没有结束就继续执行 parseStatement 方法。
parseStatement(context, topLevel) {
if (this.match(types.at)) {
this.parseDecorators(true);
}
// 🌵🌵🌵 执行这里 🌵🌵🌵
return this.parseStatementContent(context, topLevel);
}
传入参数给 parseStatementContent 方法继续执行:
parseStatementContent(context, topLevel) {
let starttype = this.state.type;
const node = this.startNode();
let kind;
if (this.isLet(context)) {
starttype = types._var;
kind = "let";
}
switch (starttype) {
case types._break:
case types._continue:
return this.parseBreakContinueStatement(node, starttype.keyword);
case types._function:
if (this.lookaheadCharCode() === 46) break;
if (context) {
if (this.state.strict) {
this.raise(this.state.start, ErrorMessages.StrictFunction);
} else if (context !== "if" && context !== "label") {
this.raise(this.state.start, ErrorMessages.SloppyFunction);
}
}
return this.parseFunctionStatement(node, false, !context);
case types._const:
case types._var:
kind = kind || this.state.value;
if (context && kind !== "var") {
this.raise(this.state.start, ErrorMessages.UnexpectedLexicalDeclaration);
}
// 🌵🌵🌵 执行这里 🌵🌵🌵
return this.parseVarStatement(node, kind);
// ... code ...
default:
{
if (this.isAsyncFunction()) {
if (context) {
this.raise(this.state.start, ErrorMessages.AsyncFunctionInSingleStatementContext);
}
this.next();
return this.parseFunctionStatement(node, true, !context);
}
}
}
const maybeName = this.state.value;
const expr = this.parseExpression();
if (starttype === types.name && expr.type === "Identifier" && this.eat(types.colon)) {
return this.parseLabeledStatement(node, maybeName, expr, context);
} else {
return this.parseExpressionStatement(node, expr);
}
}
这里通过首先通过 this.startNode() 生成了新的节点,然后通过 this.isLet(context) 判断是否 let 声明,我们之前假设待代码是 let age = 18 , 所以会匹配到 types._var,然后执行 parseVarStatement 方法。
parseVarStatement(node, kind) {
this.next();
this.parseVar(node, false, kind);
this.semicolon();
return this.finishNode(node, "VariableDeclaration");
}
这个方法会解析变量,添加末尾分号等操作,最后调用 finishNode 执行后会返回一个变量声明节点,结构如下:
{
"type":"VariableDeclaration",
"start":0,
"end":18,
"loc":{
"start":{
"line":1,
"column":0
},
"end":{
"line":1,
"column":18
}
},
"declarations":[
{
"type":"VariableDeclarator",
"start":4,
"end":17,
"loc":{
"start":{
"line":1,
"column":4
},
"end":{
"line":1,
"column":17
}
},
"id":{
"type":"Identifier",
"start":4,
"end":8,
"loc":{
"start":{
"line":1,
"column":4
},
"end":{
"line":1,
"column":8
},
"identifierName":"name"
},
"name":"name"
},
"init":{
"type":"NumericLiteral",
"start":11,
"end":17,
"loc":{
"start":{
"line":1,
"column":11
},
"end":{
"line":1,
"column":17
}
},
"extra":{
"rawValue":123123,
"raw":"123123"
},
"value":123123
}
}
],
"kind":"let"
}
之后就是一路 return,然后在 parseBlockOrModuleBlockBody 中将返回的节点添加到 body 中,因为我们只有一行代码,所以继续执行 this.next() 时也会 return 出栈。
至此 parse 解析完成。