Jest is refusing to transform the module due to a SyntaxError that states: "Unable to use import statement outside of a module"

I've been struggling to resolve the annoying

SyntaxError: Cannot use import statement outside a module
issue in my React, Typescript, Webpack project. Despite going through numerous Stack Overflow and GitHub threads, I still haven't found a clear solution. Has anyone successfully tackled this problem?

My specific scenario involves testing a module using Jest, but for some reason, Jest is failing to transform the module into plain JavaScript.

The error message I keep encountering is as follows:

/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 | 

Attempts made to troubleshoot (without success):

  • Utilizing the env setup in babel.config:

    env.test.preset: ['@babel/plugin-transform-modules-commonjs']

  • Modifying the transform settings in Jest configuration such as

    '^.+\\.jsx?$': 'babel-jest', '^.+\\.tsx?$': 'ts-jest'
    and exploring other possible configurations.

  • Editing the testPathIgnorePatterns and transformIgnorePatterns in Jest configuration.

  • Switching from .babelrc.js to .babel.config.js

...and more.

Current setup details:

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 insights on how to overcome this challenge would be greatly appreciated.

Answer №1

Despite trying them individually, I had not attempted using both transform and transformIgnorePatterns together in my jest configuration. Implementing this solved the issue I was facing:

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

My errors were:

  1. Failing to use transform and transformIgnorePatterns together.
  2. Mistakenly defining babel-jest as the transformer instead of ts-jest (which seems to be a problem when the preset of jest is set to ts-jest. Changing it back to babel-jest results in the same error):
--- "node_modules/variables/.+\\.(j|t)sx?$": "babel-jest"
+++ "node_modules/variables/.+\\.(j|t)sx?$": "ts-jest"

Answer №2

One unique issue I encountered was that when using jest, it would stumble upon .js files from a dependency in the node_modules with the error message

SyntaxError: Cannot use import statement outside a module
.

In order to resolve this, I had to ensure that ts-jest did not ignore transforming the problematic .js files within the dependency.

Upon careful reading of information on presets, I discovered that with preset: 'ts-jest', the files are left unchanged. So, I switched it to

preset: 'ts-jest/presets/js-with-ts'
and added "allowJs": true in my project's tsconfig.json.

To prevent any conflicts with my project's own tsconfig.json, I maintained a separate configuration for jest.

Ultimately, my jest.config.js mainly consists of the following:

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 did not need to specify a transform field since it was already included in the preset.

P.P.S. There was also no necessity for introducing any babel configuration.

Answer №3

Switch to Vitest for your testing needs!

After experimenting with various options, I have found Vitest to be a seamless solution compared to Jest. Jest proved to be challenging to set up correctly, whereas Vitest works effortlessly right from the start without the need for extensive configurations. This is based on my personal experience where it took me days to navigate through Jest's setup while Vitest was up and running immediately after installation.

I don't intend to disparage Jest as it remains a fantastic and user-friendly testing tool. However, in terms of convenience, Vitest excels with its 'Just Works' (tm) approach, offering a similar simple API style that we all appreciate from Jest.

To make the switch (I prefer pnpm, but you can use yarn or npm as well), follow these steps:

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

Remove jest.config.js, and instead create vite.config.ts:

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

import { defineConfig } from 'vite'

export default defineConfig({
  test: {
    /* You can opt for global usage to eliminate the need for global imports (describe, test, expect): */
    // globals: true,
  },
})

Make sure to include this snippet in your tests:

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

Update your package.json accordingly:

"test": "vitest",

Answer №4

As Jest struggles with esmodules compatibility, it is necessary to include these settings in your jest.config.js file to instruct Jest to utilize commonJS builds instead

moduleNameMapper: {
  '^variables$': 'variables/dist/cjs',
  '^[NAME OF DESIRED MODULE]$': '[NAME OF DESIRED MODULE]/dist/cjs'
}

Answer №5

Success! This solution did the trick for me.

npm install ts-jest --save-dev

To implement this, make sure to establish a jest.config.js file in the root directory of your project.

Then, insert the following code into the jest.config.js file:

module.exports = {
  "roots": [
    "<rootDir>/src"
  ],
  "testMatch": [
    "**/__tests__/**/*.+(ts|tsx|js)",
    "**/?(*.)+(spec|test).+(ts|tsx|js)"
  ],
  "transform": {
    ".+\\.(ts|tsx)$": "ts-jest"
  },
}

Answer №6

Through a stroke of luck, I stumbled upon a workaround that allows the pre-processor to process the entire node_modules directory, which is typically prohibited:

By default, the "node_modules" folder is not processed by transformers.

transformIgnorePatterns:
[
    'node_modules'
]

It appears that by using this approach, the Jest internal check can be bypassed.

transformIgnorePatterns:
[
    '//node_modules'
]

Is this a hack? Yes! Is it slow? Yes! However, it may provide a quick solution for someone in need.

Answer №7

While testing my app, I ran into a similar problem with functions that utilized the file-type library.

To address this issue, I made adjustments to the Jest configuration in my package.json file:

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

The key takeaway here is to focus on using only transformIgnorePatterns: [].

By setting transformIgnorePatterns to an empty array, Jest will transform all imported modules using the specified transformer (babel-jest). By default, Jest does not transform anything within node_modules, which may cause issues when a module (or its dependencies) relies on ES6 imports.

If you encounter this problem with specific modules rather than all of them, you can refine transformIgnorePatterns to exclude those particular modules. However, if the empty array resolves the issue effectively without causing any other complications, it's advisable to stick to that approach.

Answer №8

You may not have to switch the transformer from babel-jest to ts-jest after all.

Here's an alternative solution:

  1. Change the name of your .babelrc file to babel.config.json https://babeljs.io/docs/en/configuration#whats-your-use-case

  2. Include transformIgnorePatterns:

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

This workaround solved a similar issue for me without requiring extra transformation patterns. Since .babelrc is specific to your project, it doesn't impact node_modules in Jest.

Answer №9

If you are facing difficulties due to the antd framework, specifically with DatePicker components.

Issue

To resolve this issue, you decided to replace moment js with day js in your antd DatePicker.

You diligently followed the guidelines provided here

However, upon running your test, you encountered an annoying error message:

SyntaxError: Cannot use import statement outside a module

The root of this problem lies in these import statements:

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

This error is primarily caused by importing from antd/es/

To rectify this issue, you need to import from antd/lib/ instead.

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

Although this resolves the test error, it introduces a new bug - the DatePicker component no longer respects locales.

To address this, you must include the following code snippet:

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

Incorporate this fix into your jest.config.js file

By doing this, you can continue using antd/es/ in your code while substituting it with antd/lib/ during tests.

For more information, refer to this link

I hope this solution proves helpful for anyone grappling with similar challenges. It took me some time to unravel this conundrum.

Answer №10

One solution to this issue, as well as any potential future problems related to babel and typescript, is utilizing ts-jest, as mentioned in the Jest's getting started guide

It should be noted that using TypeScript with Babel has its limitations. Since TypeScript support in Babel is mainly for transpilation purposes, Jest will not perform type-checking on your tests during execution. To address this, you can opt for ts-jest or run the TypeScript compiler tsc separately (or integrate it into your build workflow).

Answer №11

I found myself facing a similar predicament. I was dealing with a private untranspiled package that was TypeScript-based, and all my source files and test files were utilizing the ECMA (import syntax), which led me to encounter the following error.

SyntaxError: Cannot use import statement outside a module

Here are the solutions I attempted:

  • I explored using transform, transformIgnorePatterns, and moduleNameMapper in my jest.config.ts file based on suggestions from previous answers.
  • I followed the guidance provided in the JEST official documentation on Configuring Jest #transformignorepatterns-arraystring and even referenced a use case from the React Native Guide.
  • I removed all instances of node_modules, including cached data, and performed a fresh reinstallation.
  • I resorted to using react-scripts and downgrading jest and babel-jest to version 26 from 27, although this introduced another issue.

Ultimately, I discovered guidance on ECMAScript Modules within the JEST official documentation, which outlined four steps that effectively resolved my problem. I've summarized their instructions here, but it's advisable to refer directly to the document itself for full details.

  1. Ensure code transformation is disabled by passing transform: {} or configure your transformer to emit ESM instead of CommonJS (CJS).
  2. Run 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, consider using cross-env to set environment variables.
  3. Additionally, aim to align with node's guidelines for activating "ESM mode" (such as examining type in package.json or mjs files); consult their documentation for further insights.
  4. If you need to treat other file extensions (e.g., ts) as ESM, utilize the extensionsToTreatAsEsm option.

Answer №12

Found the solution by including

"modules": "commonjs"
in
.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"
  }
}

Answer №13

In summary: for solving the issue, use tsconfig.spec.json with

{ "compilerOptions": { "allowJs": true } }

Implemented all other suggestions listed below:

  • Updated transformIgnorePatterns
  • Added an extra pattern to the transformation section
  • ... and more changes that were eventually undone

Upon diving into the transformer for debugging, I first noticed that the default workspace did not generate any logs, not even in ts-jest.log, despite using --verbose or --debug. It also failed to show a warning message which should have been:

Encountered a .js file requiring compilation while allowJs option is disabled (file: {{path}}). To resolve this issue:

  • If TypeScript should handle JS files, set allowJs to true in your TypeScript configuration (usually tsconfig.json)
  • If you prefer TypeScript to ignore your .js files, modify the transform value in your Jest configuration so it no longer matches .js files

(refer to: ts-jest-transformer.ts)

Answer №14

If you're looking for a quick fix, consider updating the preset in your jest.config.js file.

Rather than sticking with the default

"preset": "ts-jest"
, switch it up by using something like
"preset": "ts-jest/presets/js-with-ts"
. According to the documentation, this change will:

ts-jest/presets/js-with-ts

Transform TypeScript and JavaScript files (.ts, .tsx, .js, .jsx) to CommonJS syntax using ts-jest. Don't forget to enable allowJs in your tsconfig.json file.

Answer №15

I attempted numerous solutions, but none seemed to work in my case. I was on the verge of abandoning jest, until I stumbled upon a resolution. I will outline the steps as a comprehensive checklist.

To begin, ensure that jest is properly installed:

npm install --save-dev jest

Next, activate ESM within your package.json file:

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

Then, modify the test property in the package.json file:

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

Subsequently, create the jest.config.js file and include the following information:

export default { transform: {} };

Finally, re-run the jest command:

npm test

Answer №16

Using Direct Paths:

At the moment, there is an issue with ts-jest versions 28 & 29. The problem extends at least from version 28.1.3 to 29.1.0. More details can be found here: https://github.com/nodkz/mongodb-memory-server/issues/679

The bug occurs when specifying

moduleDirectories: ["node_modules", "src"]
, causing unexpected malfunctions in jest regarding imports and exports. To resolve this issue, modify the line to
moduleDirectories: ["node_modules", "<rootdir>/src/"]
or remove it completely. Please note that these solutions may affect the original functionality

One might include this line to enable direct paths so that instead of

import { minute } from "../../../utils";
, you can use import { minute } from "utils";. While convenient, after applying the above fixes, compilation will work but errors like
cannot find module "utils" in /some/directory/path
may still occur.

The only workaround I could find to restore my desired behavior is by utilizing the moduleNameMapper option as follows:

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

Credit goes to this answer on

This approach requires explicit specification of files for absolute imports. Although not ideal, it was the only method that worked for me.

Below is my complete jest.config.js file for reference:

/* 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"
    },
};

When configuring your tsconfig.json, consider the following settings:

        "allowSyntheticDefaultImports": true,
        "esModuleInterop": true,
        "moduleResolution": "Node",
        "module": "ESNext",
        "target": "ES2020",
``

Answer №17

While I was setting up NestJS using the guide provided at https://docs.nestjs.com/first-steps, I came across a similar issue.

To resolve it, I had to include the missing line

"preset": "ts-jest",
in the package.json file within the "jest" configuration block:

"jest": {
  ...
  "preset": "ts-jest",
  ...
}

Answer №18

To make changes to the test property within the package.json document, modify the following line (by default it reads react-scripts test):

"scripts": {
    "test": "jest"
}

Answer №19

If you are using ts-jest

While working with ts-jest, I encountered a frustrating error that stemmed from jest trying to utilize the JavaScript files generated from TypeScript tests. However, once I removed those files, everything worked perfectly.

Here's my solution:

  1. Ensure your code uses ESM syntax and include this line in your package.json
{
  ...
 "type": "module"
}
  1. Set up a jest config file with the following settings
// jest.config.ts
import type { JestConfigWithTsJest } from 'ts-jest'

const jestConfig: JestConfigWithTsJest = {
  // [...]
  preset: 'ts-jest/presets/default-esm', // or choose other ESM presets
  moduleNameMapper: {
    '^(\\.{1,2}/.*)\\.js$': '$1',
  },
  transform: {
    '^.+\\.tsx?$': [
      'ts-jest',
      {
        useESM: true,
      },
    ],
  },
}

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

(be cautious not to delete any of your own js files)

$ rm tests/*.js
  1. Rerun jest
bun run jest

Source:

Answer №20

Upon transitioning to NX monorepo v18, I encountered a similar issue involving the

node_modules/@angular/core/fesm2022/testing.mjs
file.

I discovered that the Angular library Jest configuration had not been migrated or updated. Here is the configuration that resolved the issue 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;

In addition, I utilized the following commands to clear the cache: npx nx reset and npx jest --clearCache.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Simulate a Pinia store action being called by a different store in Jest testing

I'm encountering a challenge when testing nested Pinia store actions with Jest. Below is the Jest test I have. Running this test results in a type error: TypeError: foodStore.getSnack.mockReturnValue is not a function If I uncomment this line in the ...

Creating a TypeScript object with fields arranged in the order they were added

Currently, I find myself in a perplexing predicament where I am required to construct an object with unspecified properties that must maintain the order of insertion. This necessity arises from the fact that I need to transmit this object to a server over ...

Supporting multiple types for matching object structures is a feature in Jest

I'm currently working on a test using jest to verify if an object key is either a string or a number. It seems like a basic task, but surprisingly there isn't much guidance in the documentation. Test Example: test('Checking asset structure ...

Is it possible to incorporate an interface with a named function in TypeScript (function declaration)?

Just dipping my toes into Typescript and experimenting with interfaces for my functions: interface StringFunction { (arg1: string): string } I managed to associate this interface with a function by declaring it as a variable: let testFunction: Strin ...

Transforming an NgDateStruct instance to a String representation

I have a NgbDateStruct object in my TypeScript file, and I need to convert it into a string of the format "YYYY-MM-DD". currentDate: NgbDateStruct; When I check console.log(this.currentDate), I see that it is displaying a Moment object. console.log(this. ...

Strategies for selectively hiding datasets in Chart.js using Angular based on specified conditions

I successfully implemented a doughnut chart using the Chart.js plugin in Angular, as shown in the screenshot below. https://i.sstatic.net/ADvOu.png One problem I encountered is that when the dataset value drops below 100, it should automatically hide, li ...

Experiencing an issue with enzyme test: Unexpected token found during mount() operation

I'm currently working on creating enzyme tests in React. For this, I have created a simple test that mounts an imported component and checks the states: import React from 'react'; import { expect } from 'chai'; import { mount } f ...

Trouble with accessing Dynamic StyleSheet properties based on type restrictions

I have successfully developed a functional component in React Native that supports both light and dark theme styles. const lightThemeOverrides = StyleSheet.create({ <light_override_styles_here> }); const styles = StyleSheet.create({ <styles_here&g ...

What is the best way to share your NodeJS library, which is coded in TypeScript, to be compatible with both BrowserJS and NodeJS, while also taking advantage of

Similar to lodash, the library @yamato-daiwa/es-extensions offers common functions for both BrowserJS and NodeJS. However, unlike lodash, it has a single main file index.js with re-exports. Requirements This library: Must be compatible with browser envir ...

The TypeScript compiler is unable to locate the injected property within a Vue Object Component

Struggling with a work example involving injecting a service during Vue's bootstrapping process. Everything "works" fine in my JavaScript and Class-Component TypeScript versions. However, when using the Vue object API with TypeScript, the compiler th ...

What is the method for obtaining class constructor argument types in the form of an object?

Let's consider an example where we have a class called Summator: export default class Summator { constructor(private readonly firstArgument: number, private readonly secondArgument: number) {} get sum() { return this.firstArgument + this.s ...

What are some ways to implement a service within a Component?

I'm experiencing an issue with my component not appearing when I inject UserService, but it works fine when I remove the service from the component. Here is the code snippet for providers in @NgModule: providers: [ UserService, { provi ...

Group records in MongoDB by either (id1, id2) or (id2, id1)

Creating a messaging system with MongoDB, I have designed the message schema as follows: Message Schema: { senderId: ObjectId, receiverId: ObjectId createdAt: Date } My goal is to showcase all message exchanges between a user and other users ...

Unexpected character error occurs when using VSCode with Vue: ''‌'

Recently, I've encountered some strange errors while working on a Vuejs project in VSCode. Whenever I try to write arrow functions or use brackets, I get unexpected character errors. For example, if I insert an empty computed section between methods a ...

Inconsistency with Angular 4 instance variables causes ambiguity within a function

Here is the code snippet: @Component({ selector: 'unb-navbar', templateUrl: './navbar.html' }) export class NavbarComponent implements OnInit { @Input() brand: string; controlador:boolean=false; overlay:string=""; @Input() menu ...

Create dynamic breadcrumb trails using router paths

I am currently working on developing a streamlined breadcrumbs path for my application. My goal is to achieve this with the least amount of code possible. To accomplish this, I have implemented a method of listening to router events and extracting data fr ...

Encountering an Angular 8 error: Unable to bind to 'config' as it is not a recognized property of 'maphilight'

I'm working with Angular 8 and I need to implement maphilight to select specific predefined areas on an image map (e.g. nose, eye, etc.) https://i.sstatic.net/Bevyg.png To integrate maphilight into my project, I used the following command: npm i ng- ...

Tips for mocking a module with a slash character in its name?

When it comes to mocking a standard npm project, the process is simple. Just create a __mocks__ folder next to the node_modules folder, then name the file after the package and insert the mock contents. For example: /__mocks__/axios.ts However, I encount ...

What causes a folder to disappear after rerunning in nest.js?

When working on my project using nest.js in MacOS Sonoma, I encountered a problem where the image folder src/products/images gets deleted after every project rerun (npm start). The images are saved like this: for (const image of images) { const fileName ...

Tips for consistently obtaining the screen's width and height with JavaScript?

Looking for a way to create a responsive website based on screen width using only javascript. I'm in need of a function that can continuously provide the screen width without interruption. Any ideas or solutions? ...