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

6.9 KiB
Raw Blame History

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) 在当前路径对应节点之后插入一个或多个节点。

当前路径所对应的源代码

需求:打印当前路径所对应的源代码

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);

打印内容:

var a = 123;

打印当前路径所对应的某个节点信息

需求打印type、start、end、loc、sourceType、interpreter、body等信息

debugger

编写代码

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);

打印内容:

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值

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);

打印内容:

NumericLiteral
123

获取path的上一级路径path.parentPath;

需求:获取上一级路径节点类型

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);

打印内容:

当前节点类型:NumericLiteral
上层节点类型:VariableDeclarator

删除path使用remove方法

需求:删除变量值

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) => {});

打印内容:

var a;

替换path单路径可以使用replaceWith方法,多路径则使用replaceWithMultiple方法

需求把var a = 123; 修改成var a = 3 用replaceWith方法

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) => {
});

打印内容:

var a = 3;

注意点,必须加上&& path.node.value == 123判断,否则就是无限循环