Jest won't transform the module - SyntaxError: Cannot use import statement outside a module
Asked Answered
A

20

176

I couldn't get rid of this SyntaxError: Cannot use import statement outside a module error no matter what I have tried and it got so frustrating. Is there anybody out here solved this issue? I have read a million stackoverflow and github issue threads. No clear solutions.

This is a React, Typescript, Webpack project. I am trying to test a module. But Jest won't transform the module to plain javascript somehow.

The error I get is

/Users/me/dev/Project/project/node_modules/variables/src/variables.js:12
    import './main.js';
    ^^^^^^

    SyntaxError: Cannot use import statement outside a module

      17 | 
      18 | */
    > 19 | import { GlobalVars } from 'variables'
         | ^
      20 | 
      21 | export const Vars = new GlobalVars()
      22 | 

What I have tried to solve this (and didn't work):

  • Using env setup in babel.config: env.test.preset: ['@babel/plugin-transform-modules-commonjs']

  • modifying transform setup in Jest configuration as '^.+\\.jsx?$': 'babel-jest', '^.+\\.tsx?$': 'ts-jest' and all other possibilities around this.

  • In Jest configuration, testPathIgnorePatterns, transformIgnorePatterns

  • Using .babel.config.js instead of .babelrc.js

...and more.

I have this setup:

package.json

  "jest": {
    "preset": "ts-jest",
    "testEnvironment": "node"
  }

.babelrc.js

module.exports = {
  presets: [
    ['@babel/preset-env', { targets: { node: 'current' } }],
    '@babel/preset-react',
    '@babel/preset-typescript',
  ],
  plugins: [
    '@babel/plugin-transform-runtime',
    '@babel/proposal-class-properties',
    '@babel/transform-regenerator',
    '@babel/plugin-transform-template-literals',
    'react-hot-loader/babel',
  ],
}

variables.ts

import { GlobalVars } from 'variables'

export const Vars = new GlobalVars()

variables.spec.ts

import { Vars } from './variables.ts'

describe('Test The Package', () => {
  it('Should accept new variables', () => {
    Vars.newVariable = 'new variable'
    expect(Vars.newVariable).toEqual('new variable')
  })
})

Any idea on how to resolve this problem?

Aberdare answered 13/5, 2020 at 17:41 Comment(1)
Try experimental ESM running node with --experimental-vm-modules as in this answer: https://mcmap.net/q/832462/-quot-syntaxerror-cannot-use-import-statement-outside-a-module-quot-error-while-testing-react-native-project-with-jest-and-testing-library-react-nativeBarracuda
A
118

Even though I have tried them separately, I haven't tried them together (transform and transformIgnorePatterns). So this jest configuration solved my issue:

  "jest": {
    "preset": "ts-jest",
    "testEnvironment": "node",
    "transform": {
      "node_modules/variables/.+\\.(j|t)sx?$": "ts-jest"
    },
    "transformIgnorePatterns": [
      "node_modules/(?!variables/.*)"
    ]
  },

My mistakes were:

  1. Not using transform and transformIgnorePatterns together.
  2. And defining babel-jest as the transformer instead of ts-jest (I guess that is a problem when the preset of jest is defined as ts-jest. Because if I change it to be babel-jest it throws the same error again.):
--- "node_modules/variables/.+\\.(j|t)sx?$": "babel-jest"
+++ "node_modules/variables/.+\\.(j|t)sx?$": "ts-jest"
Aberdare answered 13/5, 2020 at 21:16 Comment(8)
For anyone using babel instead of ts-jest, also rename your .babelrc to babel.config.json see: babeljs.io/docs/en/configuration#whats-your-use-case This way you might not need an extra transform if you are already using babel.Bedbug
which jest config? There is a jest.config.js file, there is jest config section in package.json. Where is exactly does this configuration reside?Thickwitted
Super !. I have done this configuration in the package.json and it worked . Thank youFranciscofranciska
I'm new jest but I feel like I have tried every suggestion on the internet and this error refuses to go away. I have a sinking feeling that things have changed between jest v26 and v27. Also tried ts-jest.Psi
This surprisingly does not work for me.. Sad..Fawcett
How can we use babel plugins with this setup like: babel-plugin-transform-vite-meta-env ?Multiplication
I get the error "Module ts-jest in the transform option was not found."Actinic
I didn't have to add anything related to transform or transformIgnorePatterns but was missing "preset": "ts-jest" and "testEnvironment": "node", once I added those my tests worked, thanks!Lawtun
B
37

My issue was different in a way that jest would stumle on .js files from one of the dependencies in node_modules with SyntaxError: Cannot use import statement outside a module.

I had to make sure, that ts-jest wouldn't ignore (when transforming) .js files in troublesome dependency.

After reading carefully about presets, I realized, that it leaves them 'as-is' with preset: 'ts-jest'. I changed it to preset: 'ts-jest/presets/js-with-ts' and set "allowJs": true in tsconfig.json.

To not mess up my project's tsconfig.json, I have a separate one for jest.

In the end, my jest.config.js looks mainly like this:

module.exports = {
    preset: 'ts-jest/presets/js-with-ts',
    testEnvironment: "node",
    globals: {
        'ts-jest': {
            tsconfig: '<rootDir>/test/tsconfig.json',
        },
    },
    transformIgnorePatterns: [
        "node_modules/(?!troublesome-dependency/.*)",
    ],
}

P.S. I didn't need a transform field, since the preset is already on it.

P.P.S. I didn't need to introduce any babel configuration

Backsheesh answered 20/3, 2022 at 8:41 Comment(1)
Changing the preset from ts-jest to ts-jest/presets/js-with-ts was all I needed to get jest running on legacy code I'm migrating to typescript. Thanks!Phillada
G
25

Use Vitest.

I have tried most of the suggestions made here. I found Jest to be quite painful to install correctly, whereas Vitest works out of the box without any config. This is my personal experience. It took me days to get Jest to sort-of work. I had Vitest working immediately after installation.

I don't want to hate on Jest, I actually think it's a wonderful and intuitive testing tool. But ultimately Vitest "Just Works" (tm), and you can use the same simple Jest style API we all have come to love.

Steps (I use pnpm, but of course you could do the same with yarn or npm):

pnpm remove jest ts-jest @types/jest
pnpm add -D vite vitest

Delete jest.config.js, and create vite.config.ts:

/// <reference types="vitest" />

import { defineConfig } from 'vite'

export default defineConfig({
  test: {
    /* for example, use global to avoid globals imports (describe, test, expect): */
    // globals: true,
  },
})

Add to your tests:

import { assert, expect, test } from 'vitest'

Update your package.json:

"test": "vitest",
Goalie answered 3/2, 2023 at 17:59 Comment(6)
I gave up on fighting Jest and moved to Vitest that required only npm i vitest without any configs.Cream
100% agree. I was overjoyed at how quickly and simply I was able to get Vitest working.Adenocarcinoma
One more here...Tepper
OMG. It. Just. Works. Fast. And watches tests out-of-the box.Spring
Although, "just use something else" is a terrible SO answer, this really is a much better option than wasting hours trying to make Jest work.Annamariaannamarie
I am happy with this Vitest. I tried using Jest for more than four days to no avail. Its full of suffering. It must be retired!Pappose
G
10

Since Jest is not working with esmodules well, you need to add these configurations in jest.config.js to tell Jest to use commonJS builds instead

moduleNameMapper: {
  '^variables$': 'variables/dist/cjs',
  '^[NAME OF MODULE YOU WANT TO IMPORT]$': '[NAME OF MODULE YOU WANT TO IMPORT]/dist/cjs'
}
Giustino answered 11/12, 2020 at 11:1 Comment(4)
Can you explain your solution? Or give a link to documentation about moduleNameMapper?Headstock
do you have a example of a module? instead of name of module? (The module I'm having problems with does not have a dist/cjs folder. so even if I add "^ol$": "ol/dist/ol.js" i still get the error.Luciolucita
here is an example of this solution: #73959468Presbyter
this only works in case your build has cjs module output.Curren
H
9

This worked for me.

npm i ts-jest -D

Create jest.config.js in your root dir

Then add this to it:

module.exports = {
  "roots": [
    "<rootDir>/src"
  ],
  "testMatch": [
    "**/__tests__/**/*.+(ts|tsx|js)",
    "**/?(*.)+(spec|test).+(ts|tsx|js)"
  ],
  "transform": {
    "^.+\\.(ts|tsx)$": "ts-jest"
  },
}
Hendecasyllable answered 22/3, 2023 at 11:58 Comment(0)
B
5

You don't necessarily need to change the transformer from babel-jest to ts-jest.

Instead:

  1. Rename your .babelrc to babel.config.json https://babeljs.io/docs/en/configuration#whats-your-use-case

  2. Add transformIgnorePatterns:

    "transformIgnorePatterns": [
      "node_modules/(?!variables/.*)"
    ]

This solved similar problem to me without need to add additional transform patterns. .babelrc is local to your project so it won't be applied to node_modules in Jest.

Bedbug answered 30/6, 2021 at 10:57 Comment(2)
thank you! that worked! i have third party dependency (lodash-es) and jest, configured with babel (as by default) and jest was firing an error in lodash-es, so adding transformIgnorePatterns: ['node_modules/(?!lodash-es/.*)'] forced lodash to transpile and now tests work! NOTE: i use Typescript with ESM imports both in code and in tests, and run tsc only on precommit hook, so don't need to configure jest with typescript (or ts-jest) at allPee
when you say '.babelrc is local to your project so it won't be applied to node_modules in Jest.' can you elaborate?... any docs that describe this?Constriction
C
5

By accident I found a hack to let pre-processor process the whole node_modules what is usually forbidden:

By default "node_modules" folder is ignored by transformers.

transformIgnorePatterns:
[
    'node_modules'
]

Using this pattern seems to bypass the Jest internal check.

transformIgnorePatterns:
[
    '//node_modules'
]

It this a hack? Yes! Is this slow? Yes! But properly it can help someone who needs a quick fix.

Choriamb answered 20/8, 2022 at 10:17 Comment(0)
E
5

I encountered the same issue when I was trying to test my app and some functions were using the file-type library.

I did jest configuration as below in my package.json

"jest": {
    "moduleFileExtensions": [
      "js",
      "json",
      "jsx",
      "node",
      "mjs"
    ],
    "transformIgnorePatterns": []
  },

I think the important thing is to only use "transformIgnorePatterns": [].

Setting transformIgnorePatterns to an empty array means Jest will transform every imported module using the specified transformer, in this case, babel-jest. By default, Jest does not transform anything in node_modules, which can lead to issues when a module (or its dependencies) uses ES6 imports.

If you only encounter this issue with a subset of modules, you can further refine transformIgnorePatterns to exclude only those specific modules from being ignored, but if the empty array works well and does not introduce other complications, you can stick with that.

Earache answered 26/8, 2023 at 8:28 Comment(2)
It works. Except it is VERY slow as it transforms the whole node_modules, which can be large on some projects.Codd
I have the same issue while doing automation test with jest. skia-canvas Index.node file cannot be imported, now it worksTimework
S
3

For people suffering from this due to antd framework.

Scenario

You override antd DatePicker to use day js instead of default moment js.

You followed the instructions here

Now if you run your test, it will have that dreaded error

SyntaxError: Cannot use import statement outside a module

The reason for that is because of this

import generatePicker from "antd/es/date-picker/generatePicker";
import "antd/es/date-picker/style/index";

Specifically it is caused because of importing from antd/es/

The fix is to import from antd/lib/ instead.

import generatePicker from "antd/lib/date-picker/generatePicker";
import "antd/lib/date-picker/style/index";

This fixes the test error, however, another problem that would arise is that this code have a bug. The DatePicker component now will not respect locales.

So the final solution is to add

  moduleNameMapper: {
    ... other mappings here ...
    
    // Add this to fix the problem!
    "^antd/es/(.*)$": "antd/lib/$1",
  }

in your jest.config.js file

This will let you use antd/es/ in your code but will replace it with antd/lib/ on your tests.

Reference here

Hope this helps anyone, this took me a while to figure out.

Saber answered 9/8, 2022 at 5:32 Comment(0)
A
2

Another way to solve it is and possible other subsequent issues regarding babel and typescript is to use ts-jest, quoted from Jest's getting started

However, there are some caveats to using TypeScript with Babel. Because TypeScript support in Babel is purely transpilation, Jest will not type-check your tests as they are run. If you want that, you can use ts-jest instead, or just run the TypeScript compiler tsc separately (or as part of your build process).

Ardath answered 11/3, 2021 at 19:39 Comment(0)
V
2

I got stuck in the same situation. Due to I had a private untranspiled package which is based on TypeScript, and all my source files and test files were all applied with ECMA ( import syntax ), I encountered the following error as well.

SyntaxError: Cannot use import statement outside a module

The solutions I have tried.

  • The above answers, such as use transform, transformIgnorePatterns, moduleNameMapper in jest.config.ts.
  • Followed JEST official document, Configuration Jest#transformignorepatterns-arraystring, I used exactly the same method and even referred to the use case from React Native Guide.
  • Removed all node_modules, including cache, and reinstalled them.
  • Used react-scripts and downgrade jest and babel-jest to 26 from 27. There was another issue that occurred.

After all, I found ECMAScript Modules from the JEST official document, the four steps perfectly solved my problem. I pasted their instructions here, however, you should take a look at the document itself.

  1. Ensure you either disable code transforms by passing transform: {} or otherwise configure your transformer to emit ESM rather than the default CommonJS (CJS).
  2. Execute node with --experimental-vm-modules, e.g. node --experimental-vm-modules node_modules/.bin/jest or NODE_OPTIONS=--experimental-vm-modules npx jest etc.. On Windows, you can use cross-env to be able to set environment variables.
  3. Beyond that, we attempt to follow node's logic for activating "ESM mode" (such as looking at type in package.json or mjs files), see their docs for details.
  4. If you want to treat other file extensions (such as ts) as ESM, please use the extensionsToTreatAsEsm option.
Veritable answered 22/6, 2021 at 7:48 Comment(0)
C
2

Solved mine by adding "modules": "commonjs" inside .babelrc -> presets -> @babel/preset-env

.babelrc

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "modules": "commonjs"
      }
    ],
    "@babel/preset-react"
  ]
}

package.json

"jest": {
  "verbose": true,
  "moduleDirectories": ["node_modules", "src"],
  "testEnvironment": "node",
  "transformIgnorePatterns": [
    "node_modules/(?!variables/.*)"
  ],
  "transform": {
    "^.+\\.jsx?$": "babel-jest"
  }
}
Client answered 20/7, 2022 at 22:51 Comment(0)
A
1

tl;dr: tsconfig.spec.json > { "compilerOptions": { "allowJs": true } }

Did everything else mentioned here:

  • transformIgnorePatterns
  • add an additional pattern to the transform section
  • ... much more that I reverted afterwards

Then I debugged into the transformer. First think I realized: the preconfigured workspace does not log anything, not even into ts-jest.log, not with --verbose or --debug and not even the warning, that would have been:

Got a .js file to compile while allowJs option is not set to true (file: {{path}}). To fix this:

  • if you want TypeScript to process JS files, set allowJs to true in your TypeScript config (usually tsconfig.json)
  • if you do not want TypeScript to process your .js files, in your Jest config change the transform key which value is ts-jest so that it does not match .js files anymore

(see: ts-jest-transformer.ts)

Amentia answered 9/9, 2021 at 18:23 Comment(0)
S
1

A simple solution would be to use a different preset in your jest.config.js file.

Instead of using the default "preset": "ts-jest", try use presets like "preset": "ts-jest/presets/js-with-ts". Based on the documentation, it will:

ts-jest/presets/js-with-ts

TypeScript and JavaScript files (.ts, .tsx, .js, .jsx) will be transformed by ts-jest to CommonJS syntax. You'll need to set allowJs to true in your tsconfig.json file.

Sept answered 22/2, 2022 at 1:32 Comment(0)
D
0

I tried a lot of answers here, but nothing worked for me. I was about to give up on jest but found a solution. I will document the steps as a checklist.

First, make sure jest is installed:

npm install --save-dev jest

Second, enable ESM in your package.json file:

{
  ...
 "type": "module"
}

Third, update the test property in the package.json file:

"scripts": {
    "test": "node --experimental-vm-modules ./node_modules/.bin/jest"
}

Following that, create the jest.config.js and add the following contents:

export default { transform: {} };

With that, run the jest again:

npm test
Duct answered 24/1, 2023 at 10:27 Comment(2)
this setup causes the following error for me .\node_modules\.bin\jest:2 basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')") SyntaxError: missing ) after argument listWashout
The above solution didn't work for me, but this error simply means you're not specifying correct jest path. Nowadays it would be node_modules/jest/bin/jest.jsJointure
U
0

If you use direct paths:

Right now ts-jest version 28 & 29 is broken. I don't know all of the versions it affects, but it at least affects 28.1.3 - 29.1.0. It is documented here: https://github.com/nodkz/mongodb-memory-server/issues/679

The bug is that specifying moduleDirectories: ["node_modules", "src"] breaks jest in unexpected ways, including being essentially super mad at you for imports and exports. The fix is to change the line to moduleDirectories: ["node_modules", "<rootdir>/src/"] or remove it entirely. Note: these solutions do not permit the original functionality

The reason one might include this line is to allow direct paths so instead of import { minute } from "../../../utils"; you can write import { minute } from "utils"; In my opinion this is very convenient. The above fixes will allow you to compile, but you will probably still get errors like cannot find module "utils" in /some/directory/path

The only fix I was able to find to get back my desired behaviour is using the moduleNameMapper option like so:

"moduleNameMapper": {
    '^utils(.*)$': '<rootDir>/src/utils$1',
},

credit to answer https://stackoverflow.com/a/67667249

This means that you now have to explicitly specify which files you will reference with absolute imports. While not ideal, I could not get anything else to work

For reference, here is my complete jest.config.js file:

/* eslint-disable import/no-commonjs */
/** @type {import('@ts-jest/dist/types').InitialOptionsTsJest} */
module.exports = {
    preset: "ts-jest/presets/js-with-ts",
    testEnvironment: "node",
    roots: ["<rootDir>/src"],
    "transformIgnorePatterns": [
        "node_modules/(?!variables/.*)"
    ],
    testPathIgnorePatterns: ["<rootDir>/dist/", "node_modules"],
    moduleDirectories: ["node_modules", "<rootdir>/src/"],
    setupFiles: ['dotenv/config'],
    collectCoverage: true,
    coverageDirectory: "coverage",
    extensionsToTreatAsEsm: ['.ts'],
    "moduleNameMapper": {
        "/src/(.*)": "<rootDir>/src/$1",
        '^utils(.*)$': '<rootDir>/src/utils$1',
    },
    "transform": {
        "node_modules/variables/.+\\.(j|t)sx?$": "ts-jest"
    },
};

For your tsconfig.json, you may want to consider the following settings:

        "allowSyntheticDefaultImports": true,
        "esModuleInterop": true,
        "moduleResolution": "Node",
        "module": "ESNext",
        "target": "ES2020",
``
Unconscionable answered 2/5, 2023 at 22:19 Comment(0)
I
0

I encountered the same issue when installing NestJS following this documentation: https://docs.nestjs.com/first-steps.

The fix was adding the missing "preset": "ts-jest", in the package.json file under "jest" configuration:

"jest": {
  ...
  "preset": "ts-jest",
  ...
}
Ironist answered 27/6, 2023 at 12:39 Comment(3)
What if it does not work?Unclassified
@AlexBuznik it worked using this config: "jest": { "moduleFileExtensions": [ "js", "json", "ts" ], "preset": "ts-jest", "rootDir": "src", "testRegex": ".*\\.spec\\.ts$", "transform": { "^.+\\.(t|j)s$": "ts-jest" }, "collectCoverageFrom": [ "**/*.(t|j)s" ], "coverageDirectory": "../coverage", "testEnvironment": "node" } and these versions of the dependencies: "jest": "~29.5.0", "ts-jest": "~29.1.0", "@types/jest": "~29.5.2"Ironist
Thanks! I think I was just editing .js tests and expecting them to work. Converted to TS and magic happened)Unclassified
T
0

Update the test property in the package.json file (the default is react-scripts test):

"scripts": {
    "test": "jest"
}
Talion answered 31/1 at 9:25 Comment(0)
A
0

If you are using ts-jest

I was using ts-jest and encountered this error. The key is that jest was still trying to use the JavaScript files that had been generated from the TypeScript tests. Once I deleted those, I was good to go.

Here's how I solved it.

  1. Make sure you are using ESM syntax in your code and that your package.json contains this line
{
  ...
 "type": "module"
}
  1. Create a jest config file with this content
// jest.config.ts
import type { JestConfigWithTsJest } from 'ts-jest'

const jestConfig: JestConfigWithTsJest = {
  // [...]
  preset: 'ts-jest/presets/default-esm', // or other ESM presets
  moduleNameMapper: {
    '^(\\.{1,2}/.*)\\.js$': '$1',
  },
  transform: {
    // '^.+\\.[tj]sx?$' to process js/ts with `ts-jest`
    // '^.+\\.m?[tj]sx?$' to process js/ts/mjs/mts with `ts-jest`
    '^.+\\.tsx?$': [
      'ts-jest',
      {
        useESM: true,
      },
    ],
  },
}

export default jestConfig
  1. Delete any (previously) generated JavaScript files in the tests directory

(take care not to delete any of your own js files)

$ rm tests/*.js
  1. Run jest again
bun run jest

Source: https://kulshekhar.github.io/ts-jest/docs/guides/esm-support

Avaavadavat answered 29/2 at 8:55 Comment(0)
U
0

I had similar problem after migrating to NX monorepo v18, but with node_modules/@angular/core/fesm2022/testing.mjs file.

Angular library Jest config was not migrated/updated, here is config that worked for me:

Root jest.config.js

/** @type {import('jest').Config} */
const config = {
  testMatch: ['**/+(*.)+(spec|test).+(ts|js)?(x)'],
  transform: {
    '^.+\\.[tj]sx?$': ['ts-jest', { tsconfig: '<rootDir>/tsconfig.spec.json' }],
  },
  resolver: '@nx/jest/plugins/resolver',
  coverageReporters: ['html'],
  testEnvironment: 'jsdom',
  preset: 'ts-jest/presets/js-with-ts',
};

module.exports = config;

Angular Library jest.config.js

/** @type {import('jest').Config} */
const config = {
  displayName: 'tests',
  preset: '../../jest.config.js',
  setupFilesAfterEnv: ['./src/test-setup.ts'],
  coverageDirectory: '../../coverage/libs/tests',
  transform: {
    '^.+\\.(ts|mjs|js|html)$': [
      'jest-preset-angular',
      {
        tsconfig: '<rootDir>/tsconfig.spec.json',
        stringifyContentPathRegex: '\\.(html|svg)$',
      },
    ],
  },
  transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'],
};

module.exports = config;

Also used following commands to clear cache: npx nx reset and npx jest --clearCache.

Undesirable answered 14/3 at 12:55 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.