Mauro Bringolf

Currently geeking out as WordPress developer at WebKinder and student of computer science at ETH.

Babel plugins: Shorthand syntax for specifying visitors

May 10, 2017
, ,

Babel is a JavaScript build tool that allows you to make automatic modifications on source code. It is most widely used to transpile new cutting-edge JavaScript versions into older ones with better browser support. It lets you use language features that have not landed in the specification or are not implemented in browsers yet. It is open source and has a large community: babeljs.io. I see it as a huge learning opportunity and decided to start reading its documentation and source code. I am taking notes along the way, so I might as well publish some of them. This post summarizes what I found out about how to specify visitors in a babel plugin so far.

I am fascinated by how Babel seems to make complicated code mutations quite simple and expressive. A Babel plugin is just saying things like “I want to perform this operation on all nodes of this kind.”, where a node represents a certain syntactic construct like a variable declaration for example. The possibilities for operations to perform on nodes are endless, because they are plain functions that work with the nodes! This is a form of the visitor design pattern, which makes Babel extremely extensible without plugin authors having to worry too much about the details of tree traversal and modifications.

Specifying visitors in a Babel plugin

A babel plugin simply exports a collection of operations to be performed on certain node types 1. There is a playground called astexplorer.net 2 which is a great place to get started with babel plugins. A visitor can decide whether to run an operation on node entrance, node exit or both. Or you can perform two different operations on the two events as well. The most verbose way of defining such operations is:

{
  Identifier: {
    enter: function(path) {
      // Do something
    },

    exit: function(path) {
      // Do something (else?)
    }
  }
}

Here, Identifier is the type of node these operations will be performed on. Using ES2015 shorthand syntax for function valued object properties (object methods), we can shorten this to:

{
  Identifier: {
    enter(path) {
      // Do something
    },

    exit(path) {
      // Do something (else?)
    }
  }
}

If you only want to define an operation on node entrance, you can set the corresponding function directly onto the node type property. Babel checks if you pass a function directly and uses it as entrance operation by default. Without ES2015 it looks something like this:

{
  Identifier: function(path) {
    // Do something
  }
}

This can be shortened in ES2015 which is the most common way I have seen it in babel plugins up to now:

{
  Identifier(path) {
    // Do something
  }
}

Performing operations on multiple node types

Babel also supports a neat syntax to specify one operation for multiple node types. Lets say you want to visit all nodes of type FunctionDeclaration and ObjectExpression. This can be done by specifying the operation like this:

{
 "FunctionDeclaration|ObjectExpression"(path) {
    // Do something.
  }
}

Node types also have aliases, which let you visit a semantic group of nodes rather than a list of specific node types. These can be found in the babel-types documentation 3. For example the two class nodes called `ClassDeclaration` and `ClassExpression` both alias `Class` which can be used to perform operations on both. The explicit naming of types using the "|" notation can be combined with aliases which gives you a lot of power to select a very specific set of node types. As far as I can tell, the documentation only lists aliases for each node type but not all node types that fall under some alias (the inverse relation). However, babel-types exports this relation in form of an object for you. It can be accessed in the following way:

import * as t from 'babel-types'
t.FLIPPED_ALIAS_KEYS['Class'] // ['ClassDeclaration', 'ClassExpression']

References

  1. https://github.com/thejameskyle/babel-handbook/blob/master/translations/en/plugin-handbook.md#writing-your-first-babel-plugin
  2. https://astexplorer.
  3. http://babeljs.io/docs/core-packages/babel-types/