AST
# 调试网站

[AST测试网站](https://astexplorer.net/)
[babel插件手册](https://github.com/jamiebuilds/babel-handbook/blob/master/translations/zh-Hans/plugin-handbook.md#toc-replacing-a-node)

# babel

```js
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`支持很多类型，比如`VariableDeclaration`，`FunctionDeclaration `，`ImportDeclaration `等，具体可以从[AST测试网站](https://astexplorer.net/)展开分析
```js
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`，使用相同的处理函数
```js
visitor: {
    "ClassExpression|VariableDeclaration"(path) {
        console.log(path.node.body)
    }
}
```

### 遍历

可以手动查找就不要遍历
```js
path.node.params.forEach(function() {
      // ...
});
```

获取子节点的`path`
```js
BinaryExpression(path) {
    path.get('left');
},
Program(path) {
    path.get('body.0');
}
```
获取父节点的`path`
```js
findParent
```
获取对应的子节点的
```js
path.find((path) => path.isObjectExpression());
path.find((path) => path.isReturnStatement()); //获取return的地方
```

# babel-types

配合`babel-types`工具进行代码替换增加等
```js
const t = require("babel-types");
```
用多节点替换单节点
```js
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)")),
  ]);
}
```
还可以替换代码，不过经过测试，它有局限性，官方文档也不建议使用
```js
FunctionDeclaration(path) {
  path.replaceWithSourceString(`function add(a, b) {
    return a + b;
  }`);
}
```

# 技巧

利用`visitor`访问者模式获取函数片段，然后通过`path.get(类型)`一层一层定位到对应的子路径然后执行`path.xxx`改造代码片段，获取子属性可以先在[AST测试网站](https://astexplorer.net/)先查找结果
```js
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里面获取文件的路径：
```js
const filename = this.resourcePath;
```

# 踩过的坑

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

# 参考网站

- [通过开发 Babel 插件理解抽象语法树（AST）](https://www.tuicool.com/articles/rMFRF32)