How to Configure Babel For Your Monorepo
Tags: webdev, babel, testing, tutorial
Published: 2022-05-20 09:50:40

In this post join me as I add Babel configuration to a monorepo, making sure that it can sustain different configurations packages need and making sense of Babel’s confusing docs on the matter. 

When you have multiple packages in your monorepo that require babel for their building or testing steps, it is time to manage Babel configuration in a maintainable manner.

## Requirements
Starting up with the requirements is always a good thing to do, so that we know what we’re aiming for and helps us to stay focused on that task.

In my [Pedalboard](https://github.com/mbarzeev/pedalboard) monorepo I have several packages which are tested using Jest. Jest, as you may know, uses [babel-jest](https://www.npmjs.com/package/babel-jest) OOTB in order to transpile the “modern” code (ESM, etc.) into CJS compatible code which Jest can run under NodeJS.

This means that each of these packages need to have a babel configuration which fits its requirements - one package only needs the preset plugins for env while the other also needs the React preset on top of it to parse JSX correctly, among others.

Although some packages require babel config, some have the same configuration exactly and duplication on this matter is out of the question of course.

So if to sum up our requirements:
* A top level common babel configuration which can be shared and extended
* No need to create a configuration file in a package if its configuration does not need to extend the top level common configuration
* Have Jest test work from the root of the monorepo as well from the package level itself

Are we set? Let’s start :)

***

## Babel Poor Documentation
 
Babel is a great tool but sadly I find its documentation very confusing. For example, while there can be many types of configuration (json, js etc.) the code examples are not consistent with the types. It is not quite clear what is a CLI param and what is a configuration option, bits of information of the same context are scattered across multiple pages (as we will soon see) and… why are there so many ways to define a configuration file?

I hope that this post will help to put some order into things.

## Babel configuration format type in a monorepo
So the first question that I had was what configuration file format should I use? should it be `.babelrc`? Perhaps `.babelrc.json`? `babel.config.mjs`?
The answer can be found in [this page](https://babeljs.io/docs/en/config-files), but it would be wise to start at the [monorepo’s section](https://babeljs.io/docs/en/config-files#monorepos).
Also in [another place](https://babeljs.io/docs/en/configuration#whats-your-use-case) the docs recommend using `babel.config.json` for monorepos.

We would like to have a root configuration which will be applied for the entire project, or as they call it “Project-wide configuration”.
Now, as I mentioned, Babel docs recommend `babel.config.json`. They recommend that in cases where you do not need to have complex logic to the configuration, and luckily my monorepo does not need complexity of that sort.

I’ve created a `babel.config.json` file at the root level of the project, with the following content:

```json
{
   "presets": ["@babel/preset-env"]
}
```

Staggering, I know, but this is the bare minimum I need and something that is applied to all packages - the env preset of babel.

Having this file at the root level of the base directory of our monorepo repository determines that it is root configuration (as described [here](https://babeljs.io/docs/en/config-files#root-babelconfigjson-file) in a very confusing manner). This fact will come handy soon enough.

## Jest
Jest is the testing command that I use to test several of the monorepo packages. It is a good candidate to check whether my Babel configuration is respected. Let’s try it from the root level of the project (the monorepo uses Yarn’s `workspaces foreach` to run the test across the packages. You can read more about it [here](https://dev.to/mbarzeev/rethinking-the-one-ring-to-rule-them-all-monorepo-manager-2n5g))

`yarn test`

The run fails with the following error:

```bash
SyntaxError: Cannot use import statement outside a module
```

Yes, it appears that our babel configuration was not respected. But why?
These are all nested packages within my monorepo, but when I run the test for them, either from the project’s root or from the package itself, it cannot find the configuration, since the configuration is way up the top level.

How do we tell Jest to look up the project tree?
Simple - with an option over the `transformer` tool used, that is `babel-jest`. We go to our main jest configuration and define the transformer to look upwards when it looks for babel configuration, like this:

```json
transform: {
       '\\.[jt]sx?$': ['babel-jest', {rootMode: 'upward'}],
   },
```

Running the tests now has a different result - 
tests for the `@pedalboard/hooks` pass, tests for the `@pedalboard/eslint-plugin-craftsmanlint` pass, tests for the `@pedalboard/scripts` pass, but… the tests for `@pedalboard/components` do not pass.

Why is that?
Well the error does not leave much room for mistakes:

```bash
Add @babel/preset-react (https://git.io/JfeDR) to the 'presets' section of your Babel config to enable transformation.
```

I need to add the `@babel/preset-react` but I don’t want to have for all the packages so what I do is to create a babel.config.json on the components package itself, with the following content:

```json
{
   "extends": "../../babel.config.json",
   "presets": ["@babel/preset-react"]
}
```

I’m extending the configuration from the project’s root, and I’m adding another preset on top of it. This will merge the presets I have on the package and the common presets I have on the project’s root.

Running the tests again and they all pass, also from the actual packages location :)
And so…
Let’s go over our requirements from the beginning and see if we’ve fulfilled them - 
We have a top level common babel configuration which can be shared and extended. We don’t need to create a configuration file in a package if its configuration does not need to extend the common configuration, and we have Jest test work from the root of the monorepo as well from the package level itself

I think it’s time for that beer ;)

As always, if you have any questions or comments on how this can be done better, please be sure to leave them in the comments below.

*Hey! If you liked what you've just read check out <a href="https://twitter.com/mattibarzeev?ref_src=twsrc%5Etfw" class="twitter-follow-button" data-show-count="false">@mattibarzeev</a><script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> on Twitter* :beers:

<small><small><small>Photo by <a href="https://unsplash.com/@craftedbygc?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Green Chameleon</a> on <a href="https://unsplash.com/s/photos/planning?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></small></small></small>