jest: Jest fails to load jest.config.ts in a ESM project using ts-node 10
š Bug Report
In a project using TypeScript, Jest and setup as ESM (the output of the transpiler is ESM so Node will run ESM instead of CJS) JEST is failing with ts-node 10 but works with ts-node 9.
Error:
Error: Jest: Failed to parse the TypeScript config file /projects/ts-jest-ts-node-10/jest.config.ts
Error: Must use import to load ES Module: /projects/ts-jest-ts-node-10/jest.config.ts
require() of ES modules is not supported.
require() of /projects/ts-jest-ts-node-10/jest.config.ts from /projects/ts-jest-ts-node-10/node_modules/ts-node/dist/index.js is an ES module file as it is a .ts file whose nearest parent package.json contains "type": "module" which defines all .ts files in that package scope as ES modules.
Instead change the requiring code to use import(), or remove "type": "module" from /projects/ts-jest-ts-node-10/package.json.
at readConfigFileAndSetRootDir (/projects/ts-jest-ts-node-10/node_modules/jest-config/build/readConfigFileAndSetRootDir.js:118:13)
at readConfig (/projects/ts-jest-ts-node-10/node_modules/jest-config/build/index.js:216:18)
at readConfigs (/projects/ts-jest-ts-node-10/node_modules/jest-config/build/index.js:405:26)
at runCLI (/projects/ts-jest-ts-node-10/node_modules/@jest/core/build/cli/index.js:220:59)
at Object.run (/projects/ts-jest-ts-node-10/node_modules/jest-cli/build/cli/index.js:163:37)
error Command failed with exit code 1
To Reproduce
Steps to reproduce the behavior:
- Create a new folder
- Run npm init -y
- Run
npm i -D ts-node typescript jest
- Add
"type": "module"
to your package.json - Create a jest.config.ts file with the content below:
import type { Config } from '@jest/types';
const config: Config.InitialOptions = {
verbose: true
};
export default config;
- Run
npx jest
Expected behavior
The config file should be loaded.
Link to repl or repo (highly encouraged)
I have reproduced the issue in a StackBlitz:
- ts-node 9: https://stackblitz.com/edit/ts-jest-ts-node-9
- ts-node 10: https://stackblitz.com/edit/ts-jest-ts-node-10
In order to run the code please run npm test
in the console and the error above will be raised.
Both projects are identical aside from the ts-node version.
Keep in mind that this project is using StackBlitz WebContainers and Turbo package manager (which is not NPM, though it have an alias) if you want to tweak my example but not go to learn about specifics about Turbo or WebContainers it may be better to just clone the repo.
envinfo
This is my local environment:
System:
OS: macOS 11.3.1
CPU: (12) x64 Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz
Binaries:
Node: 16.2.0 - ~/.nvm/versions/node/v16.2.0/bin/node
Yarn: 1.22.10 - ~/.nvm/versions/node/v16.2.0/bin/yarn
npm: 7.14.0 - ~/.nvm/versions/node/v16.2.0/bin/npm
npmPackages:
jest: ^27.0.1 => 27.0.1
About this issue
- Original URL
- State: closed
- Created 3 years ago
- Reactions: 36
- Comments: 62 (43 by maintainers)
Commits related to this issue
- build(`esm`): support esm - ķØķ¤ģ§ģ `type: module` ė° `sideEffects: false` ģ¶ź° - `eslint-plugin`ģ ESLintź° ESMģ ģ§ģķģ§ ģģ¼ėÆė” `type: commonjs` ģ¬ģ© - `.eslintrc.js`ė„¼ `type: module` ķØķ¤ģ§ ė“ė¶ģģė `.eslintrc.cjs`ė” ... — committed to younho9/lib by younho9 3 years ago
- chore(dev): add ts-node moduleTypes workaround for jest.config.ts Ref: https://github.com/facebook/jest/issues/11453 — committed to gadicc/node-yahoo-finance2 by gadicc 3 years ago
- fix(jest): change config file extension See related issue https://github.com/facebook/jest/issues/11453 — committed to mateus-f-torres/barefoot by mateus-f-torres 2 years ago
- fix(jest): change config file extension See related issue https://github.com/facebook/jest/issues/11453 — committed to mateus-f-torres/barefoot by mateus-f-torres 2 years ago
- fix(jest): change config file extension See related issue https://github.com/facebook/jest/issues/11453 — committed to mateus-f-torres/barefoot by mateus-f-torres 2 years ago
- fix(jest): change config file extension See related issue https://github.com/facebook/jest/issues/11453 — committed to mateus-f-torres/barefoot by mateus-f-torres 2 years ago
- Fixed tsconfig for jest config (see https://github.com/jestjs/jest/issues/11453) — committed to UCL-VR/ubiq by sebjf 7 months ago
Workaround for anyone running into this with ESM + TypeScript + Jest v27 (until this issue is addressed):
jest.config.ts
tojest.config.cjs
@cspotcode So if I understand the release notes and docs correctly, the following configuration would allow loading an ESM
jest.config.ts
like the one below?tsconfig.json
jest.config.ts
https://www.facebook.com/profile.php?id=100072718209535
Hm, seems like I donāt even need to configure
moduleTypes
at all,ts-node@10.1.0
just automatically works forjest.config.ts
š¤ šSee this for more details: https://github.com/TypeStrong/ts-node/discussions/1390#discussioncomment-988597
Edit: Ah, I think there was something wrong with the version that I had, now that I upgraded, I needed to specify this configuration in the
tsconfig.json
:But why not just rename it to
But why not just change it to
jest.config.js
?Iām still facing this issue. It happens in a monorepo setup, when I launch tests on a sub package. The tests run fine when running from the root package.
Please find a reproduction on this repository
ts-node v10.1.0 adds a new
moduleTypes
configuration which can be used to support this use-case.Hi, Iām the
ts-node
maintainer and someone pointed this out on our issue tracker as well. I can explain whatās happening here, and I want to share my proposed solution to see if the jest team agrees.In
node
, if you try torequire()
a.js
file in an ESM package (package.json"type": "module"
) node will throwERR_REQUIRE_ESM
Must use import to load ES Module
ts-node
10 aligns with nodeās behavior, so if you try torequire()
a.ts
file in an ESM package, youāll get the same error, because.ts
corresponds to.js
. (Release notes mention this underERR_REQUIRE_ESM
)I see that when jest installs
ts-node
, it overridescompilerOptions.module = commonjs
. I believe that jest would like to have a mode forts-node
that forces commonjs, so that yourjest.config.ts
is executed as commonjs, ignoring nodeās ESM behavior.This could be exposed as an API flag, used like this:
https://github.com/facebook/jest/blob/00888027257e5a751ffb7002805248b1fc758681/packages/jest-config/src/readConfigFileAndSetRootDir.ts#L84-L91
If it were possible to programmatically install
--loader
s, I would suggest that we allowjest.config.ts
to be executed as ESM, but I donāt think that would be ergonomic.Do you think itās important to support when someone is using
--loader ts-node/esm
? I donāt think so, since itās only for the config file. But Iām not sure if projects commonlyimport
other ESM files from theirjest.config.*
I see that jest is already hardcoding
compilerOptions: module: commonjs
https://github.com/facebook/jest/blob/95f49691d3472d8187640f1b703209b93c2bcecc/packages/jest-config/src/readConfigFileAndSetRootDir.ts#L90-L94Based on that code, it sounds like jest wants
jest.config.ts
ā and any other TS files loaded by it ā to always be treated like a CommonJS module. Itās forcing compilation to emit CommonJS. Is that correct?If so, should jest be passing a
moduleTypes
config, too?Yeah, maybe Jest can just set all of these
ts-node
options internally? So that there is no need to configurets-node
when you want to use it with Jest?For what itās worth, ts-node has an swc integration built-in. Also, once TS 4.5 is stable, youāll be able to use the new
cts
file extension for your jest config, which will eliminate the need for moduleTypes.There are plans to put the swc integration under a much terser flag, so youāll be able to do this in
tsconfig.json
, combined with the newcts
file extension:Just a heads up for anyone coming across this issue:
You can solve this problem by adding this to your
tsconfig.json
file:Iāve thought about this for quite some time and Iām pretty sure itās quite difficult to implement support on the Jest side. The best solution would be one where the user does not need to modify their
tsconfig.json
, as I find that fairly intrusive. Usingts-node
requires the end user setsmodule: "esnext"
in yourtsconfig.json
when you try to implement support in Jest. Perhaps using a different tool ( like a transpiler such as SWC ) that doesnāt do type-checking. would be better here? The user can do type-checking themselves, and it makes it easier to import the file. Just transpile, write to a temporary file innode_modules
, require that, then delete the file.If this is an approach that any of the maintainers like, Iād be happy to open a PR.
Let me know š
This worked to me, i need to export the file as a module, just like that: export.modules = jestConfigs Before i do that i wasnāt able to access the CLI jest commands
Or does the
moduleTypes
config mean that you need to write CommonJS in your TypeScript config files? (š¤ this seems weird - not even sure if this is valid syntax below)jest.config.ts
@vahdet You can also downgrade to ts-node 9, wait for TypeStrong/ts-node#1371 to be released, and then upgrade to ts-node 10.
This should save on effort and allow you to use .ts configs the entire time.
Another option is a config that says ātreat files matching these globs as .cjsā
Something like
"moduleTypes": {"jest.config.ts": "cjs"}
or"moduleTypes": {".": "cjs"}
This might be more intuitive, since it maps cleanly to a
node
concept. We will behave as if those.ts
files compile into.cjs
so that node runs them like CommonJS.Hi, I feel like I am missing something between this thread, https://github.com/TypeStrong/ts-node/issues/1342 (linked in one of the comments here) and the docs for 29.5 of
jest
. The basic configuration in typescript is shown to be as follow.I have the following error when running jest:
jest.config.ts:23:1 - error TS1286: ESM syntax is not allowed in a CommonJS module when 'verbatimModuleSyntax' is enabled.
.To be honest, I have no clues if this is related or not. The thing is if we write TS files with ESM syntax, but
jest
is forcingcommonjs
but we are using theesm
base configuration fortsconfig.json
,verbatimModuleSyntax
is true, so we must override through"ts-node"
field oftsconfig.json
.I made a stackblitz to show what I am saying. When adding
verbatimModuleSyntax
tofalse
, it reads the configuration file. (ignore the failing configuration and failing testsā¦ I just found out about reading that typescript ESM configuration file after 8 hoursā¦)https://github.com/facebook/jest/issues/11453#issuecomment-877653950
This works for me and even though I have
tsconfig.json
properly set:when I use
import.meta.url
in order to do an ESM workaround for the__dirname
:I get an error:
@cspotcode, @SimenB could you please investigate and help? Thanks a lot! If it is not straightforward, please let me know if I need to create a fully reproducible repo?
https://github.com/facebook/jest/releases/tag/v28.0.0-alpha.1
Yeah, makes sense to me. I wasnāt sure quite how jest handles bootstrapping itself and if it was able to pass
--loader
to a child process. But sounds like config files are executed within the main jest process, not a child process? EDIT nevermind this is answered by your comment aboveRe: delegation, I see that jest forces CommonJS emit. In this case, seems like a good thing: pair it with the correct
moduleTypes
override, and users are in business by being forced to write CJS configs. In the future, when jest wants to support ESM configs, forcing CommonJS emit will be an issue for users, so we might want to remove that flag and instead delegate fully to tsconfig.Based on that integration test, are you planning to recommend that users enable that flag in their tsconfigs, or are you planning for jest to pass that option as an override similar to how it is forcing CJS emit? If the latter, probably best to make the override a catch-all
"**"
or merge the override with the userās ownmoduleTypes
config. For example, ifjest.config.ts
importsconfig-helpers.ts
My thinking, at a very high level, is that ts-nodeās sole job is executing TS directly, and we take care of all the gotchas. So anything we can do for jest to delegate responsibility to ts-node is good for both jest, ts-node, and the users. Iāve seen other projects think they can implement TS execution themselves, hit issues and complexity, and eventually deprecate their solution in favor of ts-node.
ts-nodeās website has a dedicated section for āRecipes;ā we can add one or more for Jest. For example, our AVA recipe
Sounds like there are 3x issues in this ticket:
A) Users want to write config files that import ESM dependencies and thus must execute as ESM
Thisāll require jest to use
node --loader ts-node/esm
. With that, you canimport
orrequire
any TS file and itāll work. If users hit issues, there are well-known recipes for configuringts-node
via their tsconfig.json, which avoid jest complexity.If you want, we could also expose a no-op loader that can be initialized on-demand by jest. For example
node --loader ts-node/esm-lazy
and then at runtime, when jest realizes it needs to load an ESM ts file, callprocess.lateBindingLoader.install('ts-node/esm')
Maybe we could make it fully automatic? Happy to discuss.B) Users do not need config files to execute as ESM, but theyāre in a project with
"type": "module"
I think those users can be unblocked today with https://typestrong.org/ts-node/docs/module-type-overrides, and in the future with
jest.config.cts
. Only requires change from jest would be to recognize the cts file extension. ts-node should hopefully have cts support merged by that point.C) Users want faster execution of config files
ts-node has
"swc": true
to use SWC, or"transpileOnly": true
to use the TS compilerās transpilation. Both avoid typechecking. Users may ask why weāre not using esbuild since it may have better name recognition, but for this use case, esbuild and swc are effectively identical.Coming back to my earlier thought about how users might already be using ts-node for other scripts, thereās a good change they already have
"ts-node": {"swc": true}
in their tsconfig, so theyāll get this benefit without any intervention from jest.Sorry this wound up more verbose than intended. I hope it helps!
@cspotcode Iāve completely lost track of this (and any context I might have possessed at some point). Whatās your recommendation for moving this forward? Next release of Jest is a major, so if thereās some change thatās
ts-node@10
only we can drop v9.Weāre also dropping node 10, so loading ESM should be fine