Почему escodegen и esprima генерируют упаковку скобок в моем исходном коде?

Я использую escodegen добавить конечный код в моем заявлении, как показано ниже. В методе отпуска я добавляю .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);

Вывод вышеуказанного кода: generated code: (db.find().toArray()), Я не понимаю, почему это заключает в скобки мой исходный код. Что-то не так в моем исходном коде?

1 ответ

Решение

Вы генерируете неправильный AST. ExpressionStatement имеет форму {type: "ExpressionStatement", expression... },

Вы модифицируете свой ExpressionStatementприкрепляя к нему arguments а также callee и вы меняете его typeCallExpression). Вот:

  node.arguments = findAst.body[0].expression.arguments;
  node.callee = findAst.body[0].expression.callee;
  node.type = findAst.body[0].expression.type;

В результате странный АСТ.

Вы можете увидеть это просто с помощью: 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;
        }
    },
});

Это сгенерирует правильный 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();      
        }
    },
});

Я надеюсь, что вы найдете это полезным.

Другие вопросы по тегам