关于@babel/parser,@babel/generator,@babel/traverse, @babel/types使用
最近需要写一个脚本对项目代码进行修改故将研究进度记录在这里。
这三个关于babel的库都从属于@babel/core,所以@babel/core就可以使用这三个库。
参考文档:babel中文文档 (这三个库在babel文档中工具分类中)。
以下全部基于@babel/core version:7.13.8
@babel/parser
Babel Parser(以前为Babylon)是Babel中使用的JavaScript解析器
- 默认启用最新的ECMAScript版本(ES2020)
- 支持JSX, Flow, Typescript
babelParser.parse(code, [options])
使用方式如下:
import * as babelParser from '@babel/parser';
const code = 'const a = 1;';
babelParser.parse(code);
参数解释,通过@babel/parse的声明文件来解释
export function parse(
input: string,//需要解析代码字符串
options?: ParserOptions //可选参数,在解析过程中一些配置项 关于配置项内容在下面会讲解
): import("@babel/types").File; //返回是@babel/types 里面的File类型这个在下面介绍@babel/types时候会讲到
//关于ParserOptions 参数声明如下
export interface ParserOptions {
/**
* By default, import and export declarations can only appear at a program's top level.
* 默认情况下,导入和导出声明只能出现在程序的顶层
* Setting this option to true allows them anywhere where a statement is allowed.
* 将此选项设置为true允许在任何允许语句的地方使用。
*/
allowImportExportEverywhere?: boolean;
/**
* By default, await use is not allowed outside of an async function.
* 默认情况下,不允许在异步函数之外使用await。
* Set this to true to accept such code.
* 将此设置为true以接受这样的代码。
*/
allowAwaitOutsideFunction?: boolean;
/**
* By default, a return statement at the top level raises an error.
* 默认情况下,顶层的return语句会引发错误。
* Set this to true to accept such code.
* 将此设置为true以接受这样的代码。
*/
allowReturnOutsideFunction?: boolean;
/**
* By default, super use is not allowed outside of class and object methods.
* 默认情况下,不允许在类和对象方法之外super
* Set this to true to accept such code.
* 将此设置为true以接受这样的代码。
*/
allowSuperOutsideMethod?: boolean;
/**
* By default, exported identifiers must refer to a declared variable.
* 默认情况下,导出的标识符必须引用声明的变量。
* Set this to true to allow export statements to reference undeclared variables.
* 将此设置为true以允许导出语句引用未声明的变量。
*/
allowUndeclaredExports?: boolean;
/**
* By default, Babel always throws an error when it finds some invalid code.
* 默认情况下,Babel在发现无效代码时总是抛出错误。
* When this option is set to true, it will store the parsing error and
* try to continue parsing the invalid input file.
* 当该选项设置为true时,它将存储解析错误和尝试继续解析无效的输入文件
*/
errorRecovery?: boolean;
/**
* Indicate the mode the code should be parsed in.
* 指出代码应该被解析的模式。
* Can be one of "script", "module", or "unambiguous". Defaults to "script".
* 可以是“script”、“module”或“unambiguous”之一。默认为“script”。
* "unambiguous" will make @babel/parser attempt to guess, based on the presence
* of ES6 import or export statements.
* “unambiguous”将使@babel/parser尝试猜测,基于存在ES6导入或导出语句的
* Files with ES6 imports and exports are considered "module" and are otherwise "script".
* 带有ES6导入和导出的文件被认为是“module”,否则就是“script”。
*/
sourceType?: "script" | "module" | "unambiguous";
/**
* Correlate output AST nodes with their source filename.
* 将输出AST节点与其源文件名关联起来。
* Useful when generating code and source maps from the ASTs of multiple input files.
* 在从多个输入文件的ast生成代码和源代码映射时很有用。
*/
sourceFilename?: string;
/**
* By default, the first line of code parsed is treated as line 1.
* 默认情况下,第一行代码被解析为第1行。
* You can provide a line number to alternatively start with.
* 您可以提供一个行号作为开头。
* Useful for integration with other source tools.
* 对于与其他源工具的集成很有用。
*/
startLine?: number;
/**
* Array containing the plugins that you want to enable.
* 包含要启用的插件的数组
*/
plugins?: ParserPlugin[];
/**
* Should the parser work in strict mode.
* 解析器是否在严格模式下工作
* Defaults to true if sourceType === 'module'. Otherwise, false.
* 如果sourceType === 'module'默认为true。否则,假的。
*/
strictMode?: boolean;
/**
* Adds a ranges property to each node: [node.start, node.end]
* 是否向每个节点添加一个ranges属性:[node.start, node.end]
ranges?: boolean;
/**
* Adds all parsed tokens to a tokens property on the File node.
* 将所有已解析的标记添加到File节点上的tokens属性。
*/
tokens?: boolean;
/**
* By default, the parser adds information about parentheses by setting
* 默认情况下,解析器通过设置来添加关于圆括号的信息
* `extra.parenthesized` to `true` as needed.
* When this option is `true` the parser creates `ParenthesizedExpression`
* 当该选项为“true”时,解析器会创建`ParenthesizedExpression`
* AST nodes instead of using the `extra` property.
*/
createParenthesizedExpressions?: boolean;
}
关于options
首先解释一下AST :Abstract syntax tree 的缩写翻译过来就是抽象语法树 至于抽象语法书的结构是如何的会在@babel/types,babel有提供一个在线查看网站https://astexplorer.net/
其次关于options的定义是我自己翻译如果有错误反应指正,再次强调注意版本,我是直接取的@babel/parser的声明文件,如果不太理解可以去学习一下ts的声明文件相关的知识。
options的相关都有很明确的解释我这里只额外说两项:
- sourceType 它有三个配置项分别是:“script” | “module” | “unambiguous”,我们经常用的话一般用"script"和"module",而他们两在使用的时候怎么区分存在import/export我们就用"module"反之就是保持默认的"script",而“unambiguous”是表示我们以ES6的语法导入导出。
SyntaxError: ‘import’ and ‘export’ may appear only with ‘sourceType: “module”’
项目开发中遇到类似报错就表示我们sourceType配置错误
- plugins 表示我们在解析的过程需要使用哪些插件,比如我们解析的是typeScript代码,那么我们就需要配置plugins: [‘typeScript’],至于支持的插件类型我继续以@babel/parse的声明文件来展示。
export type ParserPlugin =
| "asyncGenerators"
| "bigInt"
| "classPrivateMethods"
| "classPrivateProperties"
| "classProperties"
| "classStaticBlock"
| "decimal"
| "decorators"
| "decorators-legacy"
| "doExpressions"
| "dynamicImport"
| "estree"
| "exportDefaultFrom"
| "exportNamespaceFrom" // deprecated
| "flow"
| "flowComments"
| "functionBind"
| "functionSent"
| "importMeta"
| "jsx"
| "logicalAssignment"
| "importAssertions"
| "moduleStringNames"
| "nullishCoalescingOperator"
| "numericSeparator"
| "objectRestSpread"
| "optionalCatchBinding"
| "optionalChaining"
| "partialApplication"
| "pipelineOperator"
| "placeholders"
| "privateIn"
| "throwExpressions"
| "topLevelAwait"
| "typescript"
| "v8intrinsic"
| ParserPluginWithOptions;
export type ParserPluginWithOptions =
| ["decorators", DecoratorsPluginOptions]
| ["pipelineOperator", PipelineOperatorPluginOptions]
| ["recordAndTuple", RecordAndTuplePluginOptions]
| ["flow", FlowPluginOptions];
到这里我们,我们已经了解了@babel/parse中parse方法的使用,当然@babel/parse 里面不止parse一种方法,但是我在项目中基本只用了parse所以着重看了一下关于parse的api ,至于其他的方法,大家可以按需去研究一下。
@babel/types
其实讲这个主要是想讲一下AST即抽象语法树。
我们上面讲到parse的返回结果是一个抽象语法树。那么这个抽象语法是什么样的呢。怎么去理解这个抽象语法树。
通过上面@babel/parse 的声明文件中可以看到parse的返回值是@babel/types中File类型。
那我们来用parse解析一段代码看一下能拿到什么或者可以通过在线模拟来分析一下得到抽象语法树是什么样的。
假如我们有这样一段代码:
const a = 123;
然后我们通过parse解析得到了AST长什么样呢?
{
"type": "File",
"start": 0,
"end": 15,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 2,
"column": 0
}
},
"errors": [],
"program": {
"type": "Program",
"start": 0,
"end": 15,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 2,
"column": 0
}
},
"sourceType": "module",
"interpreter": null,
"body": [
{
"type": "VariableDeclaration",
"start": 0,
"end": 14,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 14
}
},
"declarations": [
{
"type": "VariableDeclarator",
"start": 6,
"end": 13,
"loc": {
"start": {
"line": 1,
"column": 6
},
"end": {
"line": 1,
"column": 13
}
},
"id": {
"type": "Identifier",
"start": 6,
"end": 7,
"loc": {
"start": {
"line": 1,
"column": 6
},
"end": {
"line": 1,
"column": 7
},
"identifierName": "a"
},
"name": "a"
},
"init": {
"type": "NumericLiteral",
"start": 10,
"end": 13,
"loc": {
"start": {
"line": 1,
"column": 10
},
"end": {
"line": 1,
"column": 13
}
},
"extra": {
"rawValue": 123,
"raw": "123"
},
"value": 123
}
}
],
"kind": "const"
}
],
"directives": []
},
"comments": []
}
是不是觉得有点看不懂 那我们去掉一些影响我们的代码再来看一下
{
"type": "File",//类型是一个文件
"errors": [],
"program": {
"type": "Program",//程序
"sourceType": "module",
"interpreter": null,
"body": [//代码
{
"type": "VariableDeclaration",//声明一个变量语句
"declarations": [//声明一个变量语句的具体内容
{
"type": "VariableDeclarator",//声明语句
"id": {
"type": "Identifier",//一个变量
"name": "a"//变量名字是‘a’
},
"init": {
"type": "NumericLiteral",//是一个数字类型
"value": 123//值是123
}
}
],
"kind": "const"//使用const 声明
}
],
"directives": []
},
"comments": []
}
这样分析之后我们可以通过一个AST复原一个完整的代码已经这个代码在我们文件的第几行(位置信息是我们去掉觉得影响我们分析的东西)。
对于这个可用的json我们可以看到很多地方都有type这个type类型可以在https://www.babeljs.cn/docs/babel-types这个文档里面查看各自的含义。
那么我们拿到这个AST之后该怎么使用呢, AST怎么转化为我们的code代码呢?接着往下看。
@babel/generator
@babel/generator 是将AST转化为code代码的方式,理论上它应该是最后一步,但是这里我们却先讲它,因为它比较简单就是一个转化过程。
import * as babelParser from '@babel/parser';
import generate from '@babel/generator';
const code = 'const a = 1;';
babelParser.parse(code);
const transformCode = generate(this.ast).code;
console.log(transformCode);
//得到的结果应该是 const a = 1;
从上面的示例可以看到genrate只是一个将AST转化为code的一个函数。很好理解。
稍微看一下源码中关于generate的声明
/**
* Turns an AST into code, maintaining sourcemaps, user preferences, and valid output.
* @param ast - the abstract syntax tree from which to generate output code.
* @param opts - used for specifying options for code generation.
* @param code - the original source code, used for source maps.
* @returns - an object containing the output code and source map.
*/
export default function generate(ast: t.Node, opts?: GeneratorOptions, code?: string | { [filename: string]: string; }): GeneratorResult;
export interface GeneratorResult {
code: string;
map: {
version: number;
sources: string[];
names: string[];
sourceRoot?: string;
sourcesContent?: string[];
mappings: string;
file: string;
} | null;
}
export interface GeneratorOptions {
/**
* Optional string to add as a block comment at the start of the output file.
*/
auxiliaryCommentBefore?: string;
/**
* Optional string to add as a block comment at the end of the output file.
*/
auxiliaryCommentAfter?: string;
/**
* Function that takes a comment (as a string) and returns true if the comment should be included in the output.
* By default, comments are included if `opts.comments` is `true` or if `opts.minifed` is `false` and the comment
* contains `@preserve` or `@license`.
*/
shouldPrintComment?(comment: string): boolean;
/**
* Attempt to use the same line numbers in the output code as in the source code (helps preserve stack traces).
* Defaults to `false`.
*/
retainLines?: boolean;
/**
* Should comments be included in output? Defaults to `true`.
*/
comments?: boolean;
/**
* Set to true to avoid adding whitespace for formatting. Defaults to the value of `opts.minified`.
*/
compact?: boolean | "auto";
/**
* Should the output be minified. Defaults to `false`.
*/
minified?: boolean;
/**
* Set to true to reduce whitespace (but not as much as opts.compact). Defaults to `false`.
*/
concise?: boolean;
/**
* The type of quote to use in the output. If omitted, autodetects based on `ast.tokens`.
*/
quotes?: "single" | "double";
/**
* Used in warning messages
*/
filename?: string;
/**
* Enable generating source maps. Defaults to `false`.
*/
sourceMaps?: boolean;
/**
* The filename of the generated code that the source map will be associated with.
*/
sourceMapTarget?: string;
/**
* A root for all relative URLs in the source map.
*/
sourceRoot?: string;
/**
* The filename for the source code (i.e. the code in the `code` argument).
* This will only be used if `code` is a string.
*/
sourceFileName?: string;
/**
* Set to true to run jsesc with "json": true to print "\u00A9" vs. "©";
*/
jsonCompatibleStrings?: boolean;
}
@babel/traverse
后续补充。。。。待续