Babel 是一个通用的多功能的 JavaScript 编译器。更确切地说是源码到源码的编译器,通常也叫做“转换编译器(transpiler)”。 意思是说你为 Babel 提供一些 JavaScript 代码,Babel 更改这些代码,然后返回给你新生成的代码。此外它还拥有众多模块可用于不同形式的静态分析。
Babel 的工作流可以用下面一张图来表示,代码首先经由 babylon
解析成抽象语法树(AST),后经一些遍历和分析转换(主要过程),最后根据转换后的 AST 生成新的常规代码。
~ npm init
package name: (ast) ast_zhangxiaofan
version: (1.0.0)
description: test
entry point: (test.js) index.js
test command:
git repository:
keywords: abc
author: zhangxiaofan
license: (ISC) Apache-2.0
About to write to /Users/**/**/**/ast/package.json:
{
"name": "ast_zhangxiaofan",
"version": "1.0.0",
"description": "test",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [
"abc"
],
"author": "zhangxiaofan",
"license": "Apache-2.0"
}
Is this OK? (yes) yes
# babel遍历器
npm install @babel/traverse --save
# code解析为ast
npm install @babel/parser --save
# ast操作相关工具类
npm install @babel/types --save
# ast还原为代码
npm install @babel/generator --save
Babel parser(以前是Babylon)是Babel中使用的JavaScript解析器。通过该模块来解析我们的代码生成AST抽象语法树
// ***** API *******
babelParser.parse(code, [options])
babelParser.parseExpression(code, [options])
//parse()将提供的内容解析code为整个ECMAScript程序,同时 parseExpression()尝试在考虑性能的情况下解析单个Expression。如有疑问,请使用.parse()
案例:
const code = `function square(n) {
return n * n;
}`;
let ast = require("@babel/parser").parse(code)
console.log(ast)
可以用来遍历更新@babel/parser生成的AST
案例:替换code中n 为 x
const {parse} = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const code = `function square(n) {
return n * n;
}`;
const ast = parse(code);
traverse(ast, {
enter(path) {
if (path.isIdentifier({ name: "n" })) {
path.node.name = "x";
}
}
});
生成代码
案例:
const fs = require('fs');
const {parse} = require("@babel/parser");
const traverse = require("@babel/traverse").default;
//引入模块
const generator = require("@babel/generator").default;
const code = `function square(n) {
return n * n;
}`;
const ast = parse(code);
traverse(ast, {
enter(path) {
if (path.isIdentifier({ name: "n" })) {
path.node.name = "x";
}
}
});
// AST --> 代码
const output = generator(ast)["code"]
console.log(output)
工具类,主要用途是在创建AST的过程中判断各种语法的类型
// ******* API *******
太多了不一一介绍了 详情见
https://babel.docschina.org/docs/en/babel-types
案例:
const {parse} = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const t = require("@babel/types");
const generator = require("@babel/generator").default;
const code = `function square(n) {
return n * n;
}`;
const ast = parse(code);
traverse(ast, {
enter(path) {
if (t.isIdentifier(path.node, { name: "n" })) {
path.node.name = "x";
}
}
});
var a = "this " + decodeURIComponent("encoded%20data");
console.log(a);
// this encoded data
{
"type": "Program",
"start": 0,
"end": 481,
"body": [
{
"type": "VariableDeclaration",
"start": 179,
"end": 394,
"declarations": [
{
"type": "VariableDeclarator",
"start": 183,
"end": 393,
"id": {
"type": "Identifier",
"start": 183,
"end": 187,
"name": "tips"
},
"init": {
"type": "ArrayExpression",
"start": 190,
"end": 393,
"elements": [
{
"type": "Literal",
"start": 194,
"end": 241,
"value": "Click on any AST node with a '+' to expand it",
"raw": "\"Click on any AST node with a '+' to expand it\""
},
{
"type": "Literal",
"start": 246,
"end": 330,
"value": "Hovering over a node highlights the corresponding location in the source code",
"raw": "\"Hovering over a node highlights the \\\n corresponding location in the source code\""
},
{
"type": "Literal",
"start": 335,
"end": 391,
"value": "Shift click on an AST node to expand the whole subtree",
"raw": "\"Shift click on an AST node to expand the whole subtree\""
}
]
}
}
],
"kind": "let"
},
{
"type": "FunctionDeclaration",
"start": 396,
"end": 480,
"id": {
"type": "Identifier",
"start": 405,
"end": 414,
"name": "printTips"
},
"expression": false,
"generator": false,
"async": false,
"params": [],
"body": {
"type": "BlockStatement",
"start": 417,
"end": 480,
"body": [
{
"type": "ExpressionStatement",
"start": 421,
"end": 478,
"expression": {
"type": "CallExpression",
"start": 421,
"end": 477,
"callee": {
"type": "MemberExpression",
"start": 421,
"end": 433,
"object": {
"type": "Identifier",
"start": 421,
"end": 425,
"name": "tips"
},
"property": {
"type": "Identifier",
"start": 426,
"end": 433,
"name": "forEach"
},
"computed": false,
"optional": false
},
"arguments": [
{
"type": "ArrowFunctionExpression",
"start": 434,
"end": 476,
"id": null,
"expression": true,
"generator": false,
"async": false,
"params": [
{
"type": "Identifier",
"start": 435,
"end": 438,
"name": "tip"
},
{
"type": "Identifier",
"start": 440,
"end": 441,
"name": "i"
}
],
"body": {
"type": "CallExpression",
"start": 446,
"end": 476,
"callee": {
"type": "MemberExpression",
"start": 446,
"end": 457,
"object": {
"type": "Identifier",
"start": 446,
"end": 453,
"name": "console"
},
"property": {
"type": "Identifier",
"start": 454,
"end": 457,
"name": "log"
},
"computed": false,
"optional": false
},
"arguments": [
{
"type": "BinaryExpression",
"start": 458,
"end": 475,
"left": {
"type": "TemplateLiteral",
"start": 458,
"end": 469,
"expressions": [
{
"type": "Identifier",
"start": 465,
"end": 466,
"name": "i"
}
],
"quasis": [
{
"type": "TemplateElement",
"start": 459,
"end": 463,
"value": {
"raw": "Tip ",
"cooked": "Tip "
},
"tail": false
},
{
"type": "TemplateElement",
"start": 467,
"end": 468,
"value": {
"raw": ":",
"cooked": ":"
},
"tail": true
}
]
},
"operator": "+",
"right": {
"type": "Identifier",
"start": 472,
"end": 475,
"name": "tip"
}
}
],
"optional": false
}
}
],
"optional": false
}
}
]
}
}
],
"sourceType": "module"
}
// 引入fs nodejs模块——fs模块:fs模块用于对系统文件及目录进行读写操作。
const fs = require('fs');
// 引入babel parser模块
const {parse} = require("@babel/parser");
// 引入babel travderse模块
const traverse = require("@babel/traverse").default;
// 引入babel types模块
const t = require("@babel/types");
// 引入babel generator模块
const generator = require("@babel/generator").default;
// 读取jscode.js文件内容
let jscode = fs.readFileSync("./jscode.js", {
encoding: "utf-8"
});
// 将jscode内容转换成AST抽象语法树
let ast = parse(jscode);
const visitor =
{
CallExpression(path) {
if (!t.isIdentifier(path.node.callee)
|| path.node.callee.name !== 'decodeURIComponent'
) {
// 需要是调用 decodeURIComponent函数
return;
}
if (path.node.arguments.length !== 1) {
return;
}
let decodeArg = path.node.arguments[0];
if (!t.isLiteral(decodeArg)) {
//参数需要是常量
return;
}
let replace = eval(path.toString());
path.replaceWith(t.stringLiteral(replace));
//触发父节点的树重新遍历
path.parentPath.visit();
},
BinaryExpression(path) {
if (!t.isLiteral(path.node.left)
|| !t.isLiteral(path.node.right)
) {
return;
}
let replace = eval(path.toString());
path.replaceWith(t.stringLiteral(replace));
}
};
traverse(ast, visitor);
// AST --> 代码
let {code} = generator(ast);
fs.writeFile('decode.js', code, (err) => {
});
var a = "this encoded data";
console.log(a);