如何基于AST转换生成JavaScript源图?

时间:2017-01-22 09:10:20

标签: javascript abstract-syntax-tree source-maps esprima

假设我将JavaScript文件的内容从状态A转换为状态B.

我如何制作随附的源图?我正在使用esprimaestravese(estraverse.replace)遍历AST(我有源图对应于初始AST)并将其转换为另一个AST(但我没有得到结果) sourcemap)。

我如何获得该源图?

编辑:我正在使用esprimaestraverse进行AST转换。我的转变看起来像这样:

module.exports = {

    type: 'replace', // or traverse

    enter(node, parent) {

        if (
            node.type == 'ExpressionStatement'
            && parent.type == 'Program'
            && node.expression.type == 'CallExpression'
            && node.expression.callee.name == 'module'
        ) {
            // rename `module` to `define`
            node.expression.callee.name = 'define'

            // The dependency object (the `{a:'./a', b:'./b'}` in `module({a:'./a', b:'./b'}, function(imports) {})`) will be...
            const dependenciesObjectExpression = node.expression.arguments[0]

            // ...converted into an array of paths (the `['./a', './b']` in `define(['./a', './b'], function(a,b) {})`), and...
            const dependencyPathLiterals =
                dependenciesObjectExpression.properties.map(prop => prop.value)

            // ...the dependency names will be converted into parameters of the module body function (the `a,b` in `define(['./a', './b'], function(a,b) {})`).
            const dependencyNameIdentifiers =
                dependenciesObjectExpression.properties.map(prop => prop.key)

            // set the new define call's arguments
            node.expression.arguments[0] = {
                type: 'ArrayExpression',
                elements: dependencyPathLiterals,
            }
            node.expression.arguments[1].params = dependencyNameIdentifiers

            return node
        }

        // if we see `imports.foo`, convert to `foo`
        if (
            node.type == 'MemberExpression'
            && node.object.type == 'Identifier'
            && node.object.name == 'imports'
        ) {
            return {
                type: 'Identifier',
                name: node.property.name,
            }
        }
    },

    leave(node, parent) {
        //
    }

}

1 个答案:

答案 0 :(得分:2)

对于您编写的每个树转换,您已在源地图上编写了相应的转换。

因为树变换本质上是任意的,所以相应的源变换也是任意的。同样,复杂的树转换将导致相应复杂的源映射转换。

实现这一点的一种方法可能是共同选择(我假设存在这些)树转换操作DeleteNode,ReplaceNode,ReplaceChildWithIdentifier,ReplaceChildWithLiteral,ReplaceChildWithOperator。仅使用这些操作,您仍然可以进行任意树更改。通过修改这些操作来更新源映射(每个都执行非常特定于源映射的内容),您应该“免费”获得更新的源映射。显然,除非使用这些原语实现,否则不能使用其他树修改操作。

社区中的一些工具用于此目的: