Jest and ESM: Cannot use import statement outside a module

steveruizok

Steve Ruiz

Posted on June 25, 2021

Jest and ESM: Cannot use import statement outside a module

This post describes a fix for a common issue with Jest and ESM 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
Enter fullscreen mode Exit fullscreen mode

The solution

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

1. Install dependencies

yarn add --dev jest @babel/core babel-jest
Enter fullscreen mode Exit fullscreen mode

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.

// babel.config.js

module.exports = {
  presets: ['next/babel'],
}
Enter fullscreen mode Exit fullscreen mode

3.

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

// jest.config.js

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

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, 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!

💖 💪 🙅 🚩
steveruizok
Steve Ruiz

Posted on June 25, 2021

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related