Work with ES Modules in Jest using Babel

Photo by Susan Q Yin on Unsplash

Work with ES Modules in Jest using Babel

The notion of transpilation.

Foreword

I recently worked with a npm package that is an ES Module. Looking at the library's package.json, it has module as its type which means it uses ES6 modules. ES6 or ECMAScript 2015 introduced the import and export keywords; effectively leading to a more robust JavaScript module system.

Problem

However, ES6 is not fully supported yet by Node at the time of this writing. Thus, Node throws an error when running JavaScript code that uses ES modules.

SyntaxError: Cannot use import statement outside a module

The same error above is thrown by Jest when you try to import ES module libraries in your tests. How do we make this work?

Solution

A solution is to use a transpiler such as Babel. Babel works by converting JavaScript code written in a particular version to a specific target version. It allows us to use the latest features of JavaScript while ensuring compatibility with platforms that are still using old versions of JavaScript (like browsers).

In our case, we want to convert ES6 JavaScript code to the current version that Node supports.

Install

Install the following as dev dependencies:

  • babel-jest - allows Jest to use your Babel configuration.
  • @babel/preset-env - allows you to conveniently use latest JavaScript code and transpile to a target version for compatibility.

You can also install @babel/preset-typescript if you are using TypeScript. It has a slight caveat of Jest not type-checking your code however.

npm install babel-jest @babel/preset-env @babel/preset-typescript --save-dev

Configure

After installing the dev dependencies, it is time to configure Babel and Jest.

Babel

Create or modify your existing babel.config.js file in the same directory as your package.json. Add and configure the installed Babel presets.

module.exports = {
    presets: [
        [
            '@babel/preset-env',
            {
                targets: {
                    node: 'current', // compile against the current node version
                },
            },
        ],
        '@babel/preset-typescript', // if you use TypeScript
    ],
};

Jest

Create or modify your existing jest.config.js file to transpile your ES module libraries using your Babel configuration. Add the ES modules that you want to transform in the esModules array.

const esModules = ['@org/somelibrary1', '@org/somelibrary2'].join('|');

module.exports = {
    transform: {
        '^.+\\.(m?js|ts)$': 'babel-jest', // transpile mjs, mts, js, ts files
    },
    transformIgnorePatterns: [`/node_modules/(?!${esModules})`],
};

In this configuration, the following events happen:

  1. babel-jest would be ran on the files matching the regex. It would then let Babel transform mjs, mts, js, and ts files.
  2. The entries in the transformIgnorePatterns are your ES module libraries that will be not ignored so that Babel could transpile them.

Run Jest again and your tests that import ES modules should now work!

Closing Thoughts

There many ways around this error such as enabling an experimental flag in Node so that ES modules can be natively supported. Using Babel transpilation is the much more common solution.

I am looking forward to the future that ES6 is fully supported in Node! It would be only a matter of time when that happens. In terms of browser support, most browsers can run ES6 directly. You can check out this website to learn more.

I learned a lot about ES6 and JavaScript versioning from these articles. Check them out!