147

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?

skyboyer
  • 22,209
  • 7
  • 57
  • 64
dugong
  • 3,690
  • 4
  • 11
  • 27

18 Answers18

109

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"
Jeff Tian
  • 5,210
  • 3
  • 51
  • 71
dugong
  • 3,690
  • 4
  • 11
  • 27
  • I am glad it helped! – dugong Apr 19 '21 at 22:59
  • 2
    For anyone using babel instead of ts-jest, also rename your .babelrc to babel.config.json see: https://babeljs.io/docs/en/configuration#whats-your-use-case This way you might not need an extra transform if you are already using babel. – Ville Jun 30 '21 at 10:53
  • 1
    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? – Oliver Watkins Sep 30 '21 at 08:13
  • Super !. I have done this configuration in the package.json and it worked . Thank you – jfk Oct 18 '21 at 04:17
  • 3
    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. – sleep Nov 28 '21 at 05:43
  • This surprisingly does not work for me.. Sad.. – Chen Ni Mar 27 '22 at 07:35
  • How can we use babel plugins with this setup like: `babel-plugin-transform-vite-meta-env` ? – Ahsan Farooq Aug 17 '22 at 13:36
  • I get the error "Module ts-jest in the transform option was not found." – Scorb Oct 28 '22 at 20:14
  • 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! – Issung Jul 03 '23 at 12:05
30

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

Alex Seleznov
  • 399
  • 3
  • 7
  • 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! – Arajay Jul 07 '23 at 20:26
8

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'
}
Audwin Oyong
  • 2,247
  • 3
  • 15
  • 32
snowyBunny
  • 365
  • 4
  • 8
  • 4
    Can you explain your solution? Or give a link to documentation about moduleNameMapper? – Dmitry Malugin Mar 23 '22 at 15:52
  • 1
    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. – Peter Sep 09 '22 at 06:26
4

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.

Ville
  • 464
  • 4
  • 14
  • 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 all – Виталий Абрамов May 11 '22 at 08:10
  • 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? – blu10 Jul 26 '22 at 11:38
4

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.

Henry Ruhs
  • 1,533
  • 1
  • 20
  • 32
3

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",
svenema
  • 1,766
  • 2
  • 23
  • 45
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).

Abdullah Khaled
  • 309
  • 1
  • 2
  • 11
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.
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"
  }
}
tjvg1991
  • 382
  • 1
  • 5
  • 10
2

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.

Jplus2
  • 2,216
  • 2
  • 28
  • 49
2

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"
  },
}
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)

E. Hein
  • 266
  • 1
  • 5
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.

Andrew
  • 78
  • 1
  • 9
0

I solved my problem, mocking the file ex: jest.mock('rehype-raw/index.js', () => jest.fn());

this in the top of your test file

  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Jun 25 '22 at 14:41
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
Stanley Ulili
  • 702
  • 5
  • 7
  • 1
    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 list` – Viktor Mar 21 '23 at 07:52
  • 1
    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.js` – Maciej Gurban Jul 04 '23 at 09:46
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",
``
Sam Spade
  • 1,107
  • 7
  • 20
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",
  ...
}
dugong
  • 3,690
  • 4
  • 11
  • 27
I.G. Pascual
  • 5,818
  • 5
  • 42
  • 58
0

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.