ESM Support
Jest will take into account of the following things when working with ESM:
- ESM runtime
- The value of
module
option 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
,ES2020
etc...
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.json
yet 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
npm install -D ts-node
yarn add --dev ts-node
pnpm add -D 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.json
should contain"type": "module"
- A custom Jest resolver to resolve
.mjs
extension, 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',
}