为什么 escodegen 和 esprima 在我的源代码上生成括号包装器?

Zha*_* Yi 5 javascript esprima escodegen

我用来escodegen在我的声明中添加结束代码,如下所示。在leave方法中,我.toArray()在语句末尾附加了一个调用。

const esprima = require('esprima');
const estraverse = require('estraverse');
const escodegen = require('escodegen');

const ast = esprima.parse('db.find()');
let finished = false;
estraverse.traverse(ast, {
  leave: (node, parent) => {
    if (node.type === esprima.Syntax.ExpressionStatement && !finished) {
      finished = true;
      let statement = escodegen.generate(node);
      statement = `${statement.substring(0, statement.lastIndexOf(';'))}.toArray()`;
      const findAst = esprima.parse(statement);
      node.arguments = findAst.body[0].expression.arguments;
      node.callee = findAst.body[0].expression.callee;
      node.type = findAst.body[0].expression.type;
    }
  },
});

const generated = escodegen.generate(ast);
console.log('generated  code:', generated);
Run Code Online (Sandbox Code Playgroud)

上述代码的输出是:generated code: (db.find().toArray())。我不明白为什么它在我的源代码上加上括号。我的源代码有什么问题吗?

fon*_*kap 2

您生成的 AST 不正确。An 的ExpressionStatement形式为{type: "ExpressionStatement", expression... }

您正在修改您的ExpressionStatement,附加到它arguments,并且callee您正在将其更改type为 ( CallExpression)。这里:

  node.arguments = findAst.body[0].expression.arguments;
  node.callee = findAst.body[0].expression.callee;
  node.type = findAst.body[0].expression.type;
Run Code Online (Sandbox Code Playgroud)

结果是一个奇怪的 AST。

您可以简单地使用以下命令查看它:console.log('generated ast: %j', ast);

一个快速的解决方案是将提到的部件附加到它们所属的位置(到expression)。结果:

let finished = false;
estraverse.traverse(ast, {
    leave: (node, parent) => {
        if (node.type === esprima.Syntax.ExpressionStatement && !finished) {
        finished = true;
        let statement = escodegen.generate(node);
        statement = `${statement.substring(0, statement.lastIndexOf(';'))}.toArray()`;
        console.log(statement);
        const findAst = esprima.parse(statement);
        node.expression.arguments = findAst.body[0].expression.arguments;
        node.expression.callee = findAst.body[0].expression.callee;
        node.expression.type = findAst.body[0].expression.type;
        }
    },
});
Run Code Online (Sandbox Code Playgroud)

它将生成正确的 AST,输出预期的db.find().toArray();. 但我认为代码有点复杂并且做了太多工作,它解析db.find()然后生成代码并再次解析它。

此外,您还可以返回this.break()leave停止遍历。

以我的愚见,这很清楚:

var new_expr = {
        type: "CallExpression",
        callee: {
            type: "MemberExpression",
            computed: false,
            object: null,
            property: {
                type: "Identifier",
                name: "toArray"
            }
        },
        arguments: []
    };

const ast3 = esprima.parse('db.find()');
estraverse.traverse(ast3, {
    leave: function(node, parent) {
        if (node.type === esprima.Syntax.ExpressionStatement) {
            new_expr.callee.object = node.expression;
            node.expression = new_expr;
            return this.break();      
        }
    },
});
Run Code Online (Sandbox Code Playgroud)

希望这个对你有帮助。