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

如何基于AST转换生成JavaScript源映射?,javascript,abstract-syntax-tree,source-maps,esprima,Javascript,Abstract Syntax Tree,Source Maps,Esprima,假设我将JavaScript文件的内容从状态a转换为状态B 如何制作附带的源地图?我正在使用esprima和estravese(estraverse.replace)遍历一个AST(我有与初始AST相对应的源映射)并将其转换为另一个AST(但我没有生成的源映射) 我怎样才能得到那张原始地图 编辑:我正在使用和进行AST转换。我的转换如下所示: module.exports = { type: 'replace', // or traverse enter(node, pare

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

如何制作附带的源地图?我正在使用
esprima
estravese
(estraverse.replace)遍历一个AST(我有与初始AST相对应的源映射)并将其转换为另一个AST(但我没有生成的源映射)

我怎样才能得到那张原始地图

编辑:我正在使用和进行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) {
        //
    }

}

对于您编写的每个树转换,您必须在源映射上编写相应的转换

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

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

社区中用于此目的的两个工具:


什么是“源地图”?。。。aha:Sourcemaps定义:嗨,Ira,谢谢你的初步回答。我更新了我的问题,以显示我的AST转换是什么样子的。有什么建议吗?我会读那篇HTML5的文章!我希望有一个易于使用的工具…你必须用我建议的例程调用来替换节点更新。只有在最原始的树操作中点击,才能实现一个简单的跟踪机制来更新sourcemap。如果使用已有的(复杂)调用,那么sourcemap更改将非常复杂,您可能需要手工编写代码。没有容易解决的问题。(任何“易于使用的工具”都必须已经做到了这一点;你想要一个已经内置在esprima中的易于使用的工具,但你已经找不到了)。检查一下,看起来我找到了一个可以做到这一点的工具!它接受AST变换并生成源映射。巧合的是,自述文件中的第一个示例展示了如何使用esprima/estraverse实现这一点。很酷!发现另一个惊人的工具,由JS社区的一位知名成员提供: