Могу ли я помешать Вавилону пройти код, вставленный плагином?

Я создаю плагин, который вставляет enterFunction() перед каждым существующим вызовом функции, вызывая path.insertBefore. Поэтому мой код преобразуется из:

myFunction();

Для того, чтобы:

enterFunction();
myFunction();

Проблема в том, что когда я вставляю узел Babel, он снова пересекает вставленный узел. Здесь вывод журнала:

'CallExpression', 'myFunction'
'CallExpression', 'enterFunction'

Как я могу помешать enterFunction войти в enterFunction вызова enterFunction и его детей?

Это код, который я использую для моего плагина Babel:

function(babel) {
    return {
        visitor: {
            CallExpression: function(path) {
                console.log("CallExpression", path.node.callee.name)
                if (path.node.ignore) {
                    return;
                }
                path.node.ignore = true

                var enterCall = babel.types.callExpression(
                    babel.types.identifier("enterFunction"), []
                )
                enterCall.ignore = true;
                path.insertBefore(enterCall)
            }
        }
    }
}

Ответ 1

В " Руководстве по бабелю" упоминается следующий раздел:

Если ваш плагин не должен работать в определенной ситуации, самое простое - записать ранний возврат.

BinaryExpression(path) {
  if (path.node.operator !== '**') return;
}

Если вы выполняете обход на верхнем уровне, вы можете использовать 2 предоставленных API-метода:

path.skip() пропускает прохождение детей текущего пути. path.stop() полностью останавливает обход.

outerPath.traverse({
  Function(innerPath) {
    innerPath.skip(); // if checking the children is irrelevant
  },
  ReferencedIdentifier(innerPath, state) {
    state.iife = true;
    innerPath.stop(); // if you want to save some state and then stop traversal, or deopt
  }
});

Короче говоря, используйте path.skip() чтобы пропустить перемещение детей текущего пути. Одно приложение этого метода проиллюстрировано в этом фрагменте с помощью "Посетители", "Вызов вызова" и "Пропустить"():

export default function (babel) {
  const { types: t } = babel;

  return {
    name: "ast-transform", // not required
    visitor: {
      CallExpression(path) {
        path.replaceWith(t.blockStatement([
          t.expressionStatement(t.yieldExpression(path.node))
        ]));
        path.skip();
      }
    }
  };
}