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 monorepo I have several packages which are tested using Jest. Jest, as you may know, uses 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, but it would be wise to start at the monorepo’s section.
Also in another place 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:
{
"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 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)
yarn test
The run fails with the following error:
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:
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:
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:
{
"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 @mattibarzeev on Twitter 🍻
Photo by Green Chameleon on Unsplash
Top comments (2)
Great work, how does this work with something like build?
Cheers! :)
Can you elaborate more on what you mean by "build"?
What are you aiming to achieve?