Writing a Babel Plugin with Grandma

How to Write a Custom Babel Plugin šŸ‘µšŸ“š

Stephen Cook
The Startup
4 min readApr 25, 2020

--

I wrote a custom Babel plugin that lets you write React with emoji. It was not a productive use of time.

A preview of the `grandmas-own-jsx` babel plugin

How To Write a Babel Plugin

In this post Iā€™m going to walk you through how to make a Babel plugin, so you can make your own custom one, like grandmas-own-jsx.

I suppose you could also make a plugin thatā€™s actually useful. I mean ā€” I canā€™t physically stop you.

What is Babel?

Before we add anything to Babel, we first need to understand it a bit. I assume youā€™re roughly familiar with Babel (if not, give this a read first) ā€” but letā€™s break it down a bit.

At its heart, Babel does these 3 things: parse, transform, and generate.

a hand-drawn diagram showing some code being parsed, transformed, then generated

In other words, it parses our input code into an Abstract Syntax Tree (AST), transforms that AST into a different shape, and then generates code from the transformed AST.

ā€œASTā€ sounds confusing, but itā€™s just a fancy way of saying ā€œcode, but represented as a treeā€.

What is a Babel Plugin

A Babel plugin is code that we can add into the transform step. Itā€™s important to note that this is the only step that we can influence. We canā€™t do anything to the parse step, which means we canā€™t add custom syntax.

In other words, weā€™re limited to transforming from valid JavaScript, to valid JavaScript. If we want to transform something that isnā€™t valid JavaScript, we would need to modify Babel itself.

How Do We Transform Code

To transform the code, we need to traverse the AST we get given from the parse step. We can traverse this using a visitor. Letā€™s look at this simple Babel plugin:

Now, we could write code like this:

And it would run through Babel with our plugin, and compile to this:

Whatā€™s happening here? The visitor is an object mapping from the name of an AST node, to a function describing what to do with that node. So when Babel sees the matching StringLiteral, our plugin kicks in and transforms the stringā€™s value.

How Do We Transform More Code?

So weā€™ve looked at a really simple Babel plugin ā€” letā€™s make something a bit more complicated. Weā€™ll break down the code in the grandmas-own-jsx plugin, step by step.

Since grandmas-own-jsx leverages comments, the first thing we do is look through the CommentLine and CommentBlock elements in the Program node. We use these comments to get the information we need to build our React elements later on.

Next, we set up a new visitor to run on the rest of the tree. This lets us pass down state as we traverse the tree, without depending on global state.

So letā€™s take a look at the new visitor:

Here, we first look for any string that is just "šŸ‘µ" in our program. From the state that we were passed from the GrandmaVisitorInitiator, we grab the reference of what React elements are meant to be there.

Then, we generate a new sub-tree with genTypeExpression (weā€™ll look at that in a moment).

Finally, we replace the current node (the "šŸ‘µ" string) with the new sub-tree using replaceWith. In other words, weā€™re performing a transform like this:

a hand-written diagram showing the AST transform of ā€˜šŸ‘µā€™ to a React expression
The most ridiculous diagram I think Iā€™ve ever drawn

Generating Sub-Trees (Type Expressions)

In the previous step we created a whole new sub-tree of our AST using genTypeExpression. Babel lets us generate these AST sub-trees using type expressions ā€” letā€™s dig into how create these using the types builders that Babel provides:

By calling e.g. t.callExpression we create a CallExpression node. Similarly, to create a StringLiteral we would call t.stringLiteral.

To build a particularly complex tree, I would strongly recommend first using something like AST explorer to get the AST node names, and then find the corresponding builder in the babel-types documentation, so you know which arguments are needed.

Summary

You should now know:

  • What Babel does (parse, transform, generate)
  • To use AST explorer to view codeā€™s corresponding AST
  • How to create a visitor to traverse and modify an AST

And thatā€™s it! Go forth and create your own Babel plugins! You could turn CSS comments into real CSS, transpile import statements to deferred require statements, or maybeā€¦ just maybeā€¦ make a stupid alternative to JSX that depends heavily on the šŸ‘µ emoji.

--

--

Stephen Cook
The Startup

Software engineer at @Thread. Saving up to fulfil true dream of professional Mario Kartā€¦ https://stephencook.dev/