В чем разница между Visitor.Program.enter () и pre () в плагине Babel?

Этот плагин Babel:

module.exports = function(){
    return {
        visitor:{   
            Program:{
                enter(){ console.info('Enter') },
                exit(){ console.info('Exit') }
            }
        },
        pre(){ console.info('Pre') },
        post(){ console.info('Post') }
    }
}

производит этот вывод для любого файла javascript:

Pre
Enter
Exit
Post

pre() вызывается непосредственно перед Program.enter() и post() сразу после Program.exit().

Если я хочу запустить какой-то код в начале / конце обхода AST, есть ли причина, по которой я должен поместить этот код в pre / post вместо Program.enter / Program.exit?

Какая разница?

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
0
943
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Нет никакой разницы, AFAIK. Оба вызываются до / после полного обхода синтаксического дерева.

Единственное отличие состоит в том, что параметры, передаваемые в Program.enter / Program.exit, отличаются от параметров, передаваемых в pre / post.

module.exports = function(){
    return {
        visitor:{   
            Program:{
                enter(path, state){
                    //path.node
                    //path.parent
                    //state.opts
                },
            }
        },
        pre(state){
            //state.scope
            //state.scope.globals
            //state.scope.plugins
        },
    }
}

Например, из Program.enter() у вас есть доступ к state.opts с параметрами вашего плагина, а из pre() - нет.

pre и post имеют более фундаментальное применение: они запускаются для всех плагинов до / после всех обходов. Порядок для всех плагинов:

  1. pre работает со всеми плагинами
  2. visitor работает со всеми плагинами
  3. post работает со всеми плагинами.

Чтобы ответить на ваш вопрос: visitor.Program.enter и pre в большинстве случаев ведут себя одинаково, то есть если вам все равно, начали ли другие плагины посещать Program к моменту запуска visitor вашего собственного плагина. Основное различие можно резюмировать двумя пунктами:

  1. pre гарантированно запускается до начала обхода любой плагин.
  2. pre гарантированно будет использовать только беги один раз, в то время как посетители узла могут запускаться много раз, поскольку изменения AST посетителями (вашими или другими плагинами) могут потребовать неограниченного количества повторных посещений.

Порядок выполнения плагинов (и пресетов)

Обратите внимание, что порядок выполнения плагинов - это открытая проблема, которая активно обсуждается (см. здесь для первого введения), а pre и post помогают облегчить часть этой боли для плагинов, которые потенциально могут конфликтовать с другими плагинами.

Для справки, это порядок выполнения плагинов и пресетов в Babel7 (при запуске с babel.config.js, указанным ниже):

[PLUGIN] pre plugin1
[PLUGIN] pre plugin2
[PLUGIN] pre pres2
[PLUGIN] pre pres1
[PLUGIN] Program plugin1
[PLUGIN] Program plugin2
[PLUGIN] Program pres2
[PLUGIN] Program pres1
[PLUGIN] post plugin1
[PLUGIN] post plugin2
[PLUGIN] post pres2
[PLUGIN] post pres1

Ссылка babel.config.js:

function makeReporterPlugin(msg) {
  return () => {
    return {
      pre() {
        console.info('[PLUGIN] pre', msg);
      },
      visitor: {
        Program() {
          console.info('[PLUGIN] Program', msg);
        }
      },
      post() {
        console.info('[PLUGIN] post', msg);
      },
    };
  };
}

const pres1 = {
  plugins: [
    makeReporterPlugin('pres1')
  ]
};
const pres2 = {
  plugins: [
    makeReporterPlugin('pres2')
  ]
};

const plugin1 = makeReporterPlugin('plugin1');
const plugin2 = makeReporterPlugin('plugin2');

module.exports = {
  "presets": [
    pres1,
    pres2
  ],
  "plugins": [
    plugin1,
    plugin2
  ]
};

Обсуждение: путаница в порядке выполнения плагина Babel

Когда я писал свой первый плагин, я сам был несколько сбит с толку. Казалось, что он запускается после @babel/preset-env, хотя, согласно документации по заказу плагинов, presets должен запускаться после plugins. Однако, как объяснено здесь, на самом деле не все так просто: visitors всех плагинов и пресетов проходят параллельно, в то время как порядок, описанный в документации (плагины перед пресетами), гарантируется только для каждого узла индивидуально, а не для всего обхода AST. Эта болевая точка является основной мотивацией для pre и post.

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