Jest and ESM: Cannot use import statement outside a module
Steve Ruiz
Posted on June 25, 2021
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
The solution
After much trial and error, here's what worked.
1. Install dependencies
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
.
// babel.config.js
module.exports = {
presets: ['next/babel'],
}
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
}
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!
Posted on June 25, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.