Repository navigation
Version 22.20.0 tightens Module Loader ? #60290
Description
Version
22.20.0 LTS
Platform
Darwin M-ldkfj2929292 24.6.0 Darwin Kernel Version 24.6.0: Mon Aug 11 21:16:34 PDT 2025; root:xnu-11417.140.69.701.11~1/RELEASE_ARM64_T6020 arm64
Subsystem
No response
What steps will reproduce the bug?
before 22.20.0 upgrade, we had 22.17.1 and our script configuration was working correctly.
The line of code (script block, and the affected line respecr) - with 22.17.1
"test": "cd ../../ && jest --verbose --testPathPatterns=packages/platform-tooling"
moduleNameMapper: { '^uuid$': require.resolve('uuid'), ... },
After upgrading to node 22.20.0 - we are seeing the following error stack when that uuid module is being loaded(uuid package version hasn't changed)
`Error: Jest: Failed to parse the TypeScript config file /Users/dumb-user/mockingbird/jest.config.ts
ReferenceError: require is not defined in ES module scope, you can use import instead
at readConfigFileAndSetRootDir (/Users/dumb-user/mockingbird/packages/platform-tooling/node_modules/jest-config/build/index.js:2258:13)
at async readInitialOptions (/Users/dumb-user/mockingbird/packages/platform-tooling/node_modules/jest-config/build/index.js:1147:13)
at async readConfig (/Users/dumb-user/mockingbird/packages/platform-tooling/node_modules/jest-config/build/index.js:918:7)
at async readConfigs (/Users/dumb-user/mockingbird/packages/platform-tooling/node_modules/jest-config/build/index.js:1168:26)
at async runCLI (/Users/dumb-user/mockingbird/packages/platform-tooling/node_modules/@jest/core/build/index.js:1393:7)
at async Object.run (/Users/dumb-user/mockingbird/packages/platform-tooling/node_modules/jest-cli/build/index.js:656:9)`
🤔 Why does this happen? And here is my stupid theory (please rebut and let me know the root cause)
-
Node v22.20.0 tightened its module loader so a require() on a .ts file now throws ERR_REQUIRE_ESM whenever the file is treated as an ES module. That change comes from the work introducing the native TypeScript loader (--experimental-strip-types) described in the v22.6.0+ release notes on nodejs.org; the loader defaults .ts to ESM, so plain require() is rejected (?)
-
When Jest tries to load
jest.config.ts, it calls require(configPath). Since Node 22.20 reports ERR_REQUIRE_ESM, the code in node_modules/jest-config/build/readConfigFileAndSetRootDir.js falls back to await import(configPath). Inside that ESM evaluation require is not defined, so the line^uuid$': require.resolve('uuid')immediately throws the “ReferenceError: require is not defined in ES module scope” you’re seeing(?)
Hope this makes sense. And thanks in advance for looking into it.
How often does it reproduce? Is there a required condition?
Immediately after moving from 22.17.1 to 22.20.0
What is the expected behavior? Why is that the expected behavior?
No change in behaviour in any minor/patch version released. Not at least for tightening ESM vs. CJS expectations.
What do you see instead?
As reported above in stack trace.
Additional information
No response
Activity
joyeecheung commentedon Oct 17, 2025
Can you provide a minimal reproduction? It would be difficult to do anything about it with the information given, as it seems to be specific to the exact graph and the loader customizations involved.
On a side note this sounds similar to #59963 which was fixed by #60072 but not yet backported to v22. If you can make it go away by testing it in v24.10.0 and above then it's likely it.
mohammedmanna-skyscanner commentedon Oct 17, 2025
@joyeecheung here it is 🤞
jest-bug-reproduction.zip
HayderAlshammari commentedon Oct 17, 2025
at readConfigFileAndSetRootDir (/Users/dumb-user/mockingbird/packages/platform-tooling/node_modules/jest-config/build/index.js:2258:13)
at async readInitialOptions (/Users/dumb-user/mockingbird/packages/platform-tooling/node_modules/jest-config/build/index.js:1147:13)
at async readConfig (/Users/dumb-user/mockingbird/packages/platform-tooling/node_modules/jest-config/build/index.js:918:7)
at async readConfigs (/Users/dumb-user/mockingbird/packages/platform-tooling/node_modules/jest-config/build/index.js:1168:26)
at async runCLI (/Users/dumb-user/mockingbird/packages/platform-tooling/node_modules/@jest/core/build/index.js:1393:7)
at async Object.run (/Users/dumb-user/mockingbird/packages/platform-tooling/node_modules/jest-cli/build/index.js:656:9)`
marco-ippolito commentedon Oct 17, 2025
I think the issue is that previously it was using an external TS loader to run typescript (ts-node), while now it's using type stripping and therefore you cannot use require in a ES module.
If you use Node 22.17.0 (before native ts) it throws this error:
You repo does not have ts-node installed.
And then once you install it, test pass.
If you have ts-node installed it also works with Node v22.18.0, 22.20.0 and latest 24.
So the only difference is that
Jest: 'ts-node' is required for the TypeScriptis no longer emitted if you dont have ts-node installed because it will use the native one (you can test it by adding a random enum in the file and see the error)The error is Jest does not check for ts-node being installed correctly and assumes that if you can run a ts file its ts-node.
So there is nothing much to do on Node.js side
mohammedmanna-skyscanner commentedon Oct 17, 2025
I am unsure what you are suggesting, sorry :) .
Also, the issue was never there for node 22.17.1 for me. As soon as I moved to 22.20.0 - it started after 22.20.0. So does it mean the expectation from that version has changed around require() ?
marco-ippolito commentedon Oct 17, 2025
no what changed is that now that Node.js can execute typescript natively, Jest does not correctly detect if ts-node is installed, and uses Node.js native type stripping. It's a Jest bug not a node bug.
mohammedmanna-skyscanner commentedon Oct 17, 2025
☝️ Got it - makes sense. So would this bug be more appropriate to raise for the Jest package.
marco-ippolito commentedon Oct 17, 2025
However if you install ts-node (
npm i ts-node) in your repo, the problem goes awaymohammedmanna-skyscanner commentedon Oct 17, 2025
Ok - but this is where my confusion is, or rather, I wanted to make sure I understood the root cause.
ts-nodealready.ts-nodein my monorepo package, that's a change. And I am wondering where this bug is e.g. Jest or Node.Because installing
ts-nodein nested package (along with top-level package.json) - is not a solution if it worked before 22.20.0 - unless this is what's warranted by 22.20.0.May be The issue is that Jest 30+ treats .ts config files differently in certain contexts (like when it's run from workspaces), and
require.resolve()isn't available in ES module scope. But even then, why would it start complaining after a minor node version upgrade.mohammedmanna-skyscanner commentedon Oct 17, 2025
OK - based on the conversation and some further research... I managed to recover the build. Below is the analysis (some common parts from previous comments)
Root Cause
Node.js 22.18.0 enabled TypeScript type stripping by default. This feature made
Node.jstreat.tsfiles especially, running them through a TypeScript parser/transformer even when loaded by tools like Jest.When jest.config.ts is loaded:
Before Node 22.18.0: The config was treated as a regular module, and require.resolve() worked fine
After Node 22.18.0: Node's built-in TypeScript support intercepts the loading, treating it as an ES module with type stripping, which breaks require.resolve() calls
The issue is NOT:
❌ Jest version mismatch
❌ Node.js version in general (22.17.1 vs 22.20.0)
❌ Missing ts-node or other tooling
The issue IS:
✅ Node 22.18.0's new default TypeScript type stripping feature interfering with Jest's config loading>
AND then....
The require.resolve('uuid') was a workaround NO LONGER NECESSARY 🤦♀️
Because....
The issue was fixed in Jest 28+: According to the GitHub issue, Jest 28 added proper support for the uuid package's exports field.
GitHub Issue: [https://github.com/uuidjs/uuid/issues/451]
SimenB's comment: [https://github.com/uuidjs/uuid/issues/451#issuecomment-1112030160]
I am using Jest 29.7.0: And that workaround interfered with Node 22.18+ native type-stripping.