使用babel检查和添加代码

前提了解:

babel是干啥用的?

babel就是个js编译器,可以用来实现代码检查、代码生成、自定义语法等功能。

AST又是什么,长什么样子?

ast是用来表示代码结构的一种树结构,可以借助网页AST Explorer输入代码查看对应的ATS树。

正片开始:

步骤一:源代码解析成AST抽象语法树

使用@babel/parser将源代码解析成AST树

const parser = require('@babel/parser')

const originCode = fs.readFileSync(path, 'utf-8')
const ast = parser.parse(oldCode, {
            sourceType: 'unambiguous',
            plugins: ['jsx', 'decorators-legacy', 'classProperties', 'typescript']
        })

参数详细说明:官网文档babel-parser

步骤二:对AST树进行遍历

使用@babel/traverse对AST树进行遍历

const traverse = require('@babel/traverse')

traverse.default(ast, visitor) // visitor为对ast节点处理的babel插件

遍历的每个节点都会丢给visitor去处理

步骤三:编写babel插件visitor

代码检查例如

const visitor_check = {
    // 标签里属性的类型为JSXAttribute会触发执行
    JSXAttribute(path) {
        if (_targetEvent.includes(path.node.name.name)) {
            const expressionBody = safeProp(path, 'node', 'value', 'expression', 'body', 'body') || []
            const flag = expressionBody.some(
                element => safeProp(element, 'expression', 'callee', 'name') === 'autoTrack'
            )
            if (!flag) {
                console.log('缺少埋点->', _curPath)
                process.exit(1)
            }
        }
    }
}

找到目标所在位置,然后判断。

代码添加例如

const types = require('@babel/types')

const visitor_add = {
    JSXAttribute(path) {
        if (_targetEvent.includes(path.node.name.name)) {
            const expressionBody = safeProp(path, 'node', 'value', 'expression', 'body', 'body') || []
            const flag = expressionBody.some(
                element => safeProp(element, 'expression', 'callee', 'name') === 'autoTrack'
            )
            if (!flag) {
                const expressionStatement = types.expressionStatement(
                    types.callExpression(types.identifier('autoTrack'), [
                        types.stringLiteral('autoTrack_event'),
                        types.stringLiteral('autoTrack_label')
                    ])
                )
                path.node.value?.expression?.body?.body?.unshift(expressionStatement) &&
                    _addImportPathList.push(_curPath)
            }
        }
    }
}

找到要添加的位置,通过@babel/types调用节点类型同名的方法创建该类型的节点,鼠标放到名字上会有提示传入什么样的参数。创建后加进去即可。

最后一步:将此时的AST树生成目标代码

const generator = require('@babel/generator')

const newCode = generator.default(ast)
fs.writeFileSync(path, newCode.code)
childProcess.execSync(`npx prettier --write ${path}`) // 把格式还原

通过@babel/generator将ast转变成代码,ok,万事大吉。

猜你喜欢

转载自blog.csdn.net/qq_43119912/article/details/127525214