Jest and ESM: Cannot use import statement outside a module
Published: 2021-06-25 10:27:09

This post describes a fix for a common issue with [Jest](https://jestjs.io/) and [ESM modules](https://jestjs.io/docs/ecmascript-modules). The available help is not very good: I found many variations on the same question while searching for an answer.

---

## The problem

In a Next.js project, I kept running into an error while using dependencies with ESM modules (.mjs). In my case, this dependency was `sucrase`, which I was using as part of my app.

The error I saw was:

```
    Jest encountered an unexpected token

    Jest failed to parse a file. This happens e.g. when your code or its dependencies use non-standard JavaScript syntax, or when Jest is not configured to support such syntax.

    Out of the box Jest supports Babel, which will be used to transform your files into valid JS based on your Babel configuration.

    By default "node_modules" folder is ignored by transformers.

    Here's what you can do:
     • If you are trying to use ECMAScript Modules, see https://jestjs.io/docs/ecmascript-modules for how to enable it.
     • To have some of your "node_modules" files transformed, you can specify a custom "transformIgnorePatterns" in your config.
     • If you need a custom transformation specify a "transform" option in your config.
     • If you simply want to mock your non-JS modules (e.g. binary assets) you can stub them out with the "moduleNameMapper" config option.

    You'll find more details and examples of these config options in the docs:
    https://jestjs.io/docs/configuration
    For information about custom transformations, see:
    https://jestjs.io/docs/code-transformation

    Details:

    /Users/.../node_modules/sucrase/dist/index.mjs:1
    ({"Object.<anonymous>":function(module,exports,require,__dirname,__filename,jest){import CJSImportProcessor from "./CJSImportProcessor";
                                                                                      ^^^^^^

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

## The solution

After much trial and error, here's what worked.

### 1. Install dependencies

```bash
yarn add --dev jest @babel/core babel-jest
```

### 2. Create a babel.config.js

By default, a Next.js project uses a file called `.babelrc` to configure Babel. We need to **replace** this with a file named `babel.config.js`.

```js
// babel.config.js

module.exports = {
  presets: ['next/babel'],
}
```

### 3.
Finally, the important part. In your `jest.config.js`, include the following lines.

```js
// jest.config.js

module.exports = {  
  transformIgnorePatterns: ['node_modules/(?!(sucrase)/)'],
  transform: {
    '^.+\\.(js|jsx|ts|tsx|mjs)$': 'babel-jest',
  },
  // ...the rest of your config
}
```

In my case, the two packages that were causing problems was `sucrase`. In the config above, we are telling Jest to transform modules using `babel-jest`, and to ignore everything in `node_modules` folder **except for** those two modules.

In your case, it may be other modules that are causing the problem: replace `sucrase` with the names of your module or, if more than one, each module's name separated by a `|` (e.g. `(module-a|module-b)`.

### 4.

If a module is still causing problems, you'll need to take other measures. In my case, the solution above worked for `sucrase` but not for [browser-fs-access](https://github.com/GoogleChromeLabs/browser-fs-access), another module that used ESM modules. I was able to fix the error by dynamically importing the module (e.g. `await import("browser-fs-access")`) but still cannot test that part of my code. 

If you have a fix for that, I'd love to hear it!