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

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

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

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

Pre
Enter
Exit
Post

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

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

Есть ли разница?


person GetFree    schedule 18.10.2018    source источник


Ответы (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() — нет.

person HrW    schedule 04.04.2019

У 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.log('[PLUGIN] pre', msg);
      },
      visitor: {
        Program() {
          console.log('[PLUGIN] Program', msg);
        }
      },
      post() {
        console.log('[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.

person Domi    schedule 06.12.2019