2

This Babel plugin:

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

produces this output for any javascript file:

Pre
Enter
Exit
Post

pre()is called right before Program.enter() and post() right after Program.exit().

If I want to run some code at the beginning/end of the AST traversal, is there any reason I should put that code inside pre/post instead of Program.enter/Program.exit?

Does it make any difference?

GetFree
  • 40,278
  • 18
  • 77
  • 104

2 Answers2

5

There's no difference AFAIK. Both are called before/after the syntax tree has been completely traversed.

The only difference would be that the parameters pass to Program.enter/Program.exit are different from the parameters passed to 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
        },
    }
}

For example, from Program.enter() you have access to state.opts with your plugin options, whereas from pre() you don't.

HrW
  • 256
  • 2
  • 5
2

There is a more fundamental use to pre and post: they run for all plugins before/after all traversals. The order for all plugins is:

  1. pre runs for all plugins
  2. visitor runs for all plugins
  3. post runs for all plugins.

To answer your question: visitor.Program.enter and pre behave the same in most cases, that is, if you don't care if other plugins have already started visiting Program by the time your own plugin's visitor starts. The main difference can be summed up in two points:

  1. pre is ensured to run before any plugin started traversal.
  2. pre is ensured to only ever run once, while node visitors can run many times, as changes to the AST by (your own or other plugins') visitors might warrant an indefinite amount of re-visits.

Execution order of plugins (and presets)

Note that execution order of plugins is an open problem that is heavily discussed (see here for a first introduction), and pre and post help alleviate some of that pain for plugins that can potentially can collide with other plugins.

For reference, this is the execution order of plugins and presets in Babel7 (when run with the babel.config.js given below):

[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

Reference 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
  ]
};

Discussion: Babel plugin execution order confusion

I was rather confused myself, when I wrote my first plugin. It seemed to run after @babel/preset-env, even though, according to the docs on plugin ordering, presets should run after plugins. However, as explained here, it is actually not that simple: all plugins' and presets' visitors traverse in parallel, while the order described in the docs (plugins before presets) is only ensured for each node individually, not for the entire AST traversal. That pain point is the main motivation for pre and post.

Domi
  • 22,151
  • 15
  • 92
  • 122