【AST 基础】一、AST 的 parse & generator & traverse & visitor 组件

parse & generator 组件

parse

作用是 代码 转化为 AST 结构

解析网站:https://astexplorer.net/

const parser = require("@babel/parser"); // 需要导入

let ast = parser.parse(js_code) // 代码转化为 ast 结构,与网站上一样
let ast1 = parser.parse(js_code,{
    sourceType:"module", // 如果有 import export 关键字,需要使用该参数
}) 
console.log(JSON.stringify(ast, null, 2))

generator

作用是 AST结构 转化为 代码

let code = generator(ast).code

let code1 = generator(ast, {
    retainLines: false, // 默认 false ,是否输出与源代码相同的行号
    comments: false, // 默认 true,是否保留注释
    compact: true // 默认 false,是否压缩代码
}).code

traverse & visitor 组件

traverse 用来遍历 AST结构 的节点,简单点说就是把 所有节点都运行一遍

traverse 使用的 深度优先 策略

// 导入
const traverse = require("@babel/traverse").default; // 遍历节点
const generator = require("@babel/generator").default // AST 转换为 代码

// 相关操作
let visitor = {}
visitor.FunctionExpression = function (path) {
    console.log("mmmmm")
}
traverse(ast,visitor)
/*
源码有两个 函数节点 所以输出两次
    mmmm
    mmmm
 */

visitor 三种写法

按照自己的 喜好 选择,最常用的 visitor2

let visitor1 = {
    FunctionExpression: function (path){
        console.log('...')
    }
}

let visitor2 = {
    FunctionExpression(path){
        console.log('...')
    }
}

let visitor3 = {
    FunctionExpression: {
        enter(path){
            console.log('进入 -> 函数节点')
        },
        exit(path){
            console.log('退出 <- 函数节点')
        }
    }
}
traverse(ast,visitor3)

visitor 组合写法

使用 | 组合不同的类型

let visitor1 = {
    'FunctionExpression|BinaryExpression': function (path){
        console.log('...')
    }
}

let visitor2 = {
    'FunctionExpression|BinaryExpression'(path){
        console.log('...')
    }
}

let visitor3 = {
    'FunctionExpression|BinaryExpression': {
        enter(path){
            console.log('进入 -> 节点')
        },
        exit(path){
            console.log('退出 <- 节点')
        }
    }
}
traverse(ast,visitor3)

使用 多个函数 处理 节点,会按照函数顺序 执行

function fun1(path) {
    console.log('11')
}

function fun2(path) {
    console.log('22')
}

let visitor = {
    FunctionExpression: {
        enter: [fun1, fun2]
    }
}
traverse(ast,visitor)

traverse 并非必须从头遍历

// 修改函数 第一个参数为 x ,并修改函数内 所有用了 该参数的地方
const updateParamNameVisitor = {
    Identifier(path) {
        if (path.node.name === this.paramName) {
            path.node.name = "x" 
        }
    }
}
const visitor = {
    FunctionExpression(path) { // 遍历函数节点
        const paramName = path.node.params[0].name
        // 内部循环
        path.traverse(updateParamNameVisitor, {  
            paramName // 向 子循环 传递参数
        })
    }
}

traverse(ast, visitor)
发表评论 / Comment

用心评论~