ESM Support
Jest will take into account of the following things when working with ESM:
- ESM runtime
- The value of
moduleoption in tsconfig file is either:Node16/Node18/NodeNext: this MUST go together withtype: "module"inpackage.json.- Otherwise, the value MUST BE one of the ES values, e.g.
ES2015,ES2020etc...
One can configure ts-jest to work with Jest in ESM mode by following the steps below.
We have EXAMPLE APPS which contains some projects which have basic setup to work with ESM (next to CJS config).
Configure Jest runtime
Jest runtime currently has a few issues related to support ESM:
- Not taking into account of
type: "module"field inpackage.jsonyet to run as ESM mode. - Mocking ES modules are not supported yet, track progress here https://github.com/jestjs/jest/pull/10976
Overall progress and discussion can be found at https://github.com/jestjs/jest/issues/9430
If one is using Jest config in TypeScript, one should install ts-node as a dev dependency.
- npm
- Yarn
- pnpm
- Bun
npm install -D ts-node
yarn add --dev ts-node
pnpm add -D ts-node
bun add --dev ts-node
Execute Jest with with --experimental-vm-modules flag for NodeJs
node --experimental-vm-modules node_modules/jest/bin/jest.js
Alternative way for Yarn users:
yarn node --experimental-vm-modules $(yarn bin jest)
This command will also work if you use Yarn Plug'n'Play.
Configure tsconfig
One can choose EITHER ONE of the following options for tsconfig:
Using ES module values
See more details about ES module values
ts-jest recommends to use ES2022 or ESNext when using ES module values to achieve full support for all recent ESM features.
{
"compilerOptions": {
"module": "ES2022", // or `ESNext`
"target": "ESNext",
"esModuleInterop": true
}
}
Using hybrid module values
See more details about hybrid module
Currently, the code transpiler ONLY supports hybrid values with isolatedModules: true
{
"compilerOptions": {
"module": "Node16", // or Node18/NodeNext
"target": "ESNext",
"esModuleInterop": true,
"isolatedModules": true
}
}
Configure Jest config
Jest will attempt to load ESM files from node_modules with default jest-resolve which usually works for most of the cases.
However, there are cases like Angular libraries ESM built files or ESM files which are outside node_modules might not be loaded
correctly.
To fix that, one can use moduleNameMapper in jest config to instruct Jest to load the correct ESM files or create a
custom Jest resolver.
Using ESM presets
See available ESM preset creator functions HERE
import type { Config } from 'jest'
import { createDefaultEsmPreset } from 'ts-jest'
const presetConfig = createDefaultEsmPreset({
//...options
})
export default {
...presetConfig,
} satisfies Config
NOT using ESM presets
import type { Config } from 'jest'
import { TS_EXT_TO_TREAT_AS_ESM, ESM_TS_TRANSFORM_PATTERN } from 'ts-jest'
export default {
extensionsToTreatAsEsm: [...TS_EXT_TO_TREAT_AS_ESM],
transform: {
[ESM_TS_TRANSFORM_PATTERN]: [
'ts-jest',
{
//...other `ts-jest` options
useESM: true,
},
],
},
} satisfies Config
Resolve .mjs/.mts extensions
To work with .mts extension, besides the requirement to run Jest and ts-jest in ESM mode, there are a few extra requirements to be met:
package.jsonshould contain"type": "module"- A custom Jest resolver to resolve
.mjsextension, for example:
import type { SyncResolver } from 'jest-resolve'
const mjsResolver: SyncResolver = (path, options) => {
const mjsExtRegex = /\.mjs$/i
const resolver = options.defaultResolver
if (mjsExtRegex.test(path)) {
try {
return resolver(path.replace(mjsExtRegex, '.mts'), options)
} catch {
// use default resolver
}
}
return resolver(path, options)
}
export default mjsResolver
and then add this to Jest config:
import type { Config } from 'jest'
const config: Config = {
//...other options
resolver: '<rootDir>/path/to/custom-resolver.ts',
}