2023-07-17 20:51:53 +08:00

268 lines
6.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# AST操作之API用法-path路径
## path 路径
`@babel/traverse`模块中path对象表示一个节点node在AST树中的位置提供了一些属性和方法
用于访问节点的属性、子节点、父节点、兄弟节点等并且可以对AST树进行修改。
**下面是一些常见的 path 对象的 API**
| api | 功能 |
|--------------------------|--------------------------------------------------------------------|
| path.node | 获取当前路径对应的节点。 |
| path.parent | 获取当前路径对应节点的父节点。 |
| path.parentPath | 获取当前路径对应节点的父路径。 |
| path.get(key) | 获取当前路径下指定属性名key对应的子路径。例如path.get("body") 获取当前路径下名为 "body" 的子路径。 |
| path.getSibling(index) | 获取当前路径对应节点的兄弟节点的路径。通过指定索引index可以获取相应的兄弟路径。 |
| path.getFunctionParent() | 获取当前路径对应节点的最近的函数父节点的路径。 |
| path.traverse(visitor) | 遍历当前路径下的所有子节点,并应用指定的 visitor。 |
| path.replaceWith(node) | 用指定的节点替换当前路径对应的节点。 |
| path.remove() | 从 AST 中移除当前路径对应的节点。 |
| path.insertBefore(nodes) | 在当前路径对应节点之前插入一个或多个节点。 |
| path.insertAfter(nodes) | 在当前路径对应节点之后插入一个或多个节点。 |
**当前路径所对应的源代码**
需求:打印当前路径所对应的源代码
```javascript
const fs = require('fs');
const {parse} = require("@babel/parser");
const traverse = require("@babel/traverse").default;
let encode_file = "./encode.js";
let js_code = fs.readFileSync(encode_file, {encoding: "utf-8"});
let ast = parse(js_code);
const visitor = {
VariableDeclaration(path)
{
console.log(path.toString());
},
}
traverse(ast, visitor);
```
打印内容:
```javascript
var a = 123;
```
**打印当前路径所对应的某个节点信息**
需求打印type、start、end、loc、sourceType、interpreter、body等信息
![debugger](./img/1.png)
编写代码
```javascript
const fs = require('fs');
const {parse} = require("@babel/parser");
const traverse = require("@babel/traverse").default;
let encode_file = "./encode.js";
let js_code = fs.readFileSync(encode_file, {encoding: "utf-8"});
let ast = parse(js_code);
const visitor = {
Program(path)
{
console.log(path.node.type);
console.log(path.node.start);
console.log(path.node.end);
console.log(path.node.loc);
console.log(path.node.sourceType);
console.log(path.node.interpreter);
console.log(path.node.body);
console.log(path.node.directives);
},
}
traverse(ast, visitor);
```
打印内容:
```javascript
Program
0
12
SourceLocation {
start: Position { line: 1, column: 0, index: 0 },
end: Position { line: 1, column: 12, index: 12 },
filename: undefined,
identifierName: undefined
}
script
null
[
Node {
type: 'VariableDeclaration',
start: 0,
end: 12,
loc: SourceLocation {
start: [Position],
end: [Position],
filename: undefined,
identifierName: undefined
},
declarations: [ [Node] ],
kind: 'var'
}
]
[]
```
**判断path是什么type使用path.isXXX 这个方法**
需求遍历所有节点输出节点类型为NumericLiteral的value值
```javascript
const fs = require('fs');
const {parse} = require("@babel/parser");
const traverse = require("@babel/traverse").default;
let encode_file = "./encode.js";
let js_code = fs.readFileSync(encode_file, {encoding: "utf-8"});
let ast = parse(js_code);
const visitor = {
enter(path)
{
if(path.isNumericLiteral())
{
console.log(path.type);
console.log(path.node.value);
}
},
}
traverse(ast, visitor);
```
打印内容:
```javascript
NumericLiteral
123
```
**获取path的上一级路径path.parentPath;**
需求:获取上一级路径节点类型
```javascript
const fs = require('fs');
const {parse} = require("@babel/parser");
const traverse = require("@babel/traverse").default;
let encode_file = "./encode.js";
let js_code = fs.readFileSync(encode_file, {encoding: "utf-8"});
let ast = parse(js_code);
const visitor = {
enter(path)
{
if(path.isNumericLiteral())
{
console.log('当前节点类型:'+path.type);
console.log('上层节点类型:'+path.parentPath.type);
}
},
}
traverse(ast, visitor);
```
打印内容:
```javascript
当前节点类型:NumericLiteral
上层节点类型:VariableDeclarator
```
**删除path使用remove方法**
需求:删除变量值
```javascript
const fs = require('fs');
const {parse} = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const generator = require("@babel/generator").default;
let encode_file = "./encode.js";
let js_code = fs.readFileSync(encode_file, {encoding: "utf-8"});
let ast = parse(js_code);
const visitor = {
enter(path)
{
if(path.isNumericLiteral())
{
path.remove()
}
},
}
traverse(ast, visitor);
// 写入文件
let {code} = generator(ast);
console.log(code)
fs.writeFile('decode.js', code, (err) => {});
```
打印内容:
```javascript
var a;
```
**替换path单路径可以使用`replaceWith`方法,多路径则使用`replaceWithMultiple`方法**
需求把var a = 123; 修改成var a = 3 用replaceWith方法
```javascript
const fs = require('fs');
const {parse} = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const generator = require("@babel/generator").default;
let encode_file = "./encode.js";
let js_code = fs.readFileSync(encode_file, {encoding: "utf-8"});
let ast = parse(js_code, {
sourceType: 'module',
});
const visitor = {
enter(path) {
if (path.isNumericLiteral() && path.node.value == 123) {
path.replaceWith({type: "NumericLiteral", value: 3});
}
},
}
traverse(ast, visitor);
// 写入文件
let {code} = generator(ast);
console.log(code)
fs.writeFile('decode.js', code, (err) => {
});
```
打印内容:
```javascript
var a = 3;
```
注意点,必须加上`&& path.node.value == 123`判断,否则就是无限循环