jamis0n
jamis0n

Reputation: 3800

Jest Typescript with ES Module in node_modules error - Must use import to load ES Module:

I'm trying to write a simple jest test for a 3rd party package were using that only exports an ES module. It's a wrapper around an http server.

Here is a test repo I setup (just run yarn && yarn jest to reproduce): https://github.com/jamesopti/hocuspocus-testing

No matter what config I experiment with, I still get this error when trying to run it:

 Must use import to load ES Module: /Users/j/hocuspocus-testing/node_modules/@hocuspocus/server/dist/hocuspocus-server.esm.js

    > 1 | import { Server, Hocuspocus } from '@hocuspocus/server'
        | ^
      2 | import * as request from 'supertest'
      3 |
      4 | describe('Server (e2e)', () => {

Things I've tried already:

Any ideas what I'm missing here? I thought this would be straightforward

[EDIT 1] - Added tsconfig.json and a working src/index.ts file to the example repo.

Upvotes: 49

Views: 47056

Answers (6)

dwjohnston
dwjohnston

Reputation: 11771

For what it's worth, the OPs approach of using transformIgnorePatterns works fine for me - just need to escape the forward slashes.

  transformIgnorePatterns: ['node_modules\\/(?!use-local-storage-state\\/)'],

The context this is for is that the package I'm using has code compiled to ESM only, which won't work for jest, and won't be transpiled for CJS because it lives in node_modules.

ie. without the above config, I get this error:

    ({"Object.<anonymous>":function(module,exports,require,__dirname,__filename,jest){import useLocalStorageState from './src/useLocalStorageState.js';
                                                                                      ^^^^^^

    SyntaxError: Cannot use import statement outside a module

Upvotes: 0

Holf
Holf

Reputation: 6432

Much as I'm loathe to give a "use something else" type answer, I'm going to, even though it may not directly answer the original question.

It does, however, answer the question "How can I make it easy to test my TypeScript files which have ESM dependencies, whilst using Jest syntax?"

After messing around with various jest.config.js options, with ideas taken from some of these answers, I eventually just did:

npm install -D vitest       

... and, it all just worked. No jest.config.js file. No messing with any other dependencies.

The only change I had to make was to import some test-related functions that you don't have to with Jest:

import { expect, describe, it } from "vitest";

Other than this, it's easy to forget I'm not simply using Jest.

Further detail here: https://vitest.dev/guide/.

EDIT: As suggested in a helpful comment, you can set an option such that importing expect / describe / it is not necessary.

See here for details on how to use globals: true: https://vitest.dev/config/#globals.

Upvotes: 15

BigMan73
BigMan73

Reputation: 1584

JAN 2023: ES2022, TypeScript 4.9.4, jest 29.3.1, ts-jest 29.0.3 FEB 2024: Modified transform file regex based on comment from @vick

This is what worked for me, after 2 hours of frustration. I used this configuration in jest.config.ts:

import type { JestConfigWithTsJest } from 'ts-jest'

const config: JestConfigWithTsJest = {
  extensionsToTreatAsEsm: ['.ts'],
  verbose: true,
  preset: 'ts-jest/presets/default-esm',
  testEnvironment: 'node',
  transform: {
    '^.+\\.tsx?$': ['ts-jest', { useESM: true }]
  },
  testPathIgnorePatterns: ['./dist']
}

export default config

Change the test script in package.json to:

I use pnpm. Change to npx jest with npm, or yarn exec with yarn

...
"scripts": {
  ...
  "test": "NODE_OPTIONS=--experimental-vm-modules pnpm exec jest",
  ...
}
...

tsconfig.json:

{
  "compilerOptions": {
    "rootDirs": ["src"],
    "outDir": "dist",
    "lib": ["ES2022"],
    "target": "ES2022",
    "module": "ES2022",
    "composite": true,
    "moduleResolution": "node",
    "declaration": true,
    "declarationMap": true,
    "incremental": true,
    "esModuleInterop": true,
    "types": ["jest", "node", "@types/jest"],
    "sourceMap": true
  },
  "ts-node": {
    "esm": true,
    "experimentalSpecifierResolution": "node"
  },
  "include": ["./src/**/*", "./tests/**/*"]
}

See this (rather confusing) documentation for reference: https://kulshekhar.github.io/ts-jest/docs/guides/esm-support/

Upvotes: 19

wetmarble
wetmarble

Reputation: 205

I was having the same problem with my svelte app and testing. I ultimately traced it to having a jest.config.js and a jest.config.json in my root folder. It seems that jest does not have automatic config file resolution and was using a default configuration instead of either of my specified configurations.

Upvotes: 1

miki noidea
miki noidea

Reputation: 922

So for anyone still hitting this, ESM configuration explained in this section of documentation :

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

{
  // [...]
  "jest": {
    "extensionsToTreatAsEsm": [".ts"],
    "globals": {
      "ts-jest": {
        "useESM": true
      }
    }
  }
}

Upvotes: 29

Kelvin Schoofs
Kelvin Schoofs

Reputation: 8718

You don't have a tsconfig.json file that specifies module. Therefore it uses the default, where it transpiles all your modules to CommonJS syntax, which uses require.

If you actually look at your dist/hocuspocus-server.esm.js, you should see it using require over the ESM import syntax.

Upvotes: 2

Related Questions