Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AST #55

Open
Wscats opened this issue Jun 11, 2019 · 0 comments
Open

AST #55

Wscats opened this issue Jun 11, 2019 · 0 comments

Comments

@Wscats
Copy link
Owner

Wscats commented Jun 11, 2019

调试网站

AST测试网站
babel插件手册

babel

const template = require('@babel/template').default;
const generate = require("@babel/generator").default;

const ast = template.ast(`
// JS
var a = 'abcd'
`);
ast.kind = 'let';
ast.declarations[0].init.value = 1234;
console.log(ast.declarations[0].init);

console.log(generate(ast).code);

这段代码通过AST把var a = 'abcd'转化为let b = 1234

babel-plugins

babel插件开发,plugin字段配合visitor筛选代码实现转化,转换的时候,是插件开始起作用的时候,babel给我们提供了一个visitor的规范,让我们进入到这个流程中,我们可以通过visitor来定义我们的访问逻辑

visitor支持很多类型,比如VariableDeclarationFunctionDeclaration ImportDeclaration 等,具体可以从AST测试网站展开分析

const {
    transform
} = require("@babel/core");
module.exports = async (option, options) => {
    let {
        script,
        allScript,
        isExistScript,
        scriptLang,
        template,
        templateLang,
        templateComponentName,
        style,
        styleLang,
        isExistStyle
    } = option
    const result = await new Promise((resolve, reject) => {
        const defaultOption = {
            plugins: [
                require("@babel/plugin-proposal-class-properties"),
                {
                    visitor: {
                        ArrayExpression(path) {
                            console.log(path.node)
                        }
                    }
                }
            ],
            presets: [
                [
                    require("@babel/preset-react"),
                    {
                        "pragma": "h",
                    }
                ]
            ]
        }
        // comibine option
        const finalOptions = Object.assign({}, defaultOption, {
            ...options
        });
        transform(allScript, {
            ...finalOptions
        }, (err, result) => {
            if (err) {
                reject(err)
            } else {
                // console.log(result)
                resolve(result)
            }
        });
    })
    return result
}

访问者模式

可以在方法名用|来匹配多种不同的type,使用相同的处理函数

visitor: {
    "ClassExpression|VariableDeclaration"(path) {
        console.log(path.node.body)
    }
}

遍历

可以手动查找就不要遍历

path.node.params.forEach(function() {
      // ...
});

获取子节点的path

BinaryExpression(path) {
    path.get('left');
},
Program(path) {
    path.get('body.0');
}

获取父节点的path

findParent

获取对应的子节点的

path.find((path) => path.isObjectExpression());
path.find((path) => path.isReturnStatement()); //获取return的地方

babel-types

配合babel-types工具进行代码替换增加等

const t = require("babel-types");

用多节点替换单节点

ReturnStatement(path) {
  path.replaceWithMultiple([
    t.expressionStatement(t.stringLiteral("Is this the real life?")),
    t.expressionStatement(t.stringLiteral("Is this just fantasy?")),
    t.expressionStatement(t.stringLiteral("(Enjoy singing the rest of the song in your head)")),
  ]);
}

还可以替换代码,不过经过测试,它有局限性,官方文档也不建议使用

FunctionDeclaration(path) {
  path.replaceWithSourceString(`function add(a, b) {
    return a + b;
  }`);
}

技巧

利用visitor访问者模式获取函数片段,然后通过path.get(类型)一层一层定位到对应的子路径然后执行path.xxx改造代码片段,获取子属性可以先在AST测试网站先查找结果

visitor: {
    "ClassExpression"(path, { opts }) {
        // 筛选class myAbcAbc extends WeElement类
        if (path.node.superClass.name === 'WeElement' && path.get("body.body")) {
            console.log('---------------')
            path.get("body.body").forEach((ClassMethod) => {
                // 筛选render() {}函数
                if (ClassMethod.node.key.name === 'render') {
                    console.log(ClassMethod.type)
                    ClassMethod.remove()
                }
            })
        }
    }
}

loader里面获取文件的路径:

const filename = this.resourcePath;

踩过的坑

类里面的方法要使用 ClassMethod 匹配,不能使用 FunctionExpression 匹配。

参考网站

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant