Fix: ERR_REQUIRE_ESM In Cucumber-tsflow With Callsites
🐛 Bug Report: Cucumber-tsflow and callsites ES Module Issues
Description
The dreaded ERR_REQUIRE_ESM
error! cucumber-tsflow@4.5.2
fails miserably when callsites@4.2.0
(an ES module) is installed. This leads to Cucumber tests crashing and burning. The core issue is that the compiled JavaScript code is stubbornly using require("callsites")
, while callsites@4.2.0
and beyond are pure ES modules. It's like trying to fit a square peg into a round hole, a classic case of module incompatibility!
Error Message
Error [ERR_REQUIRE_ESM]: require() of ES Module /path/to/node_modules/callsites/index.js from /path/to/node_modules/cucumber-tsflow/dist/our-callsite.js not supported.
Instead change the require of index.js in /path/to/node_modules/cucumber-tsflow/dist/our-callsite.js to a dynamic import() which is available in all CommonJS modules.
at tryRequire (/path/to/node_modules/@cucumber/cucumber/src/try_require.ts:12:13)
at /path/to/node_modules/@cucumber/cucumber/src/api/support.ts:39:15
at Array.map (<anonymous>)
at getSupportCodeLibrary (/path/to/node_modules/@cucumber/cucumber/src/api/support.ts:37:16)
at runCucumber (/path/to/node_modules/@cucumber/cucumber/src/api/run_cucumber.ts:73:36)
at async Cli.run (/path/to/node_modules/@cucumber/cucumber/src/cli/index.ts:79:25)
at async Object.run [as default] (/path/to/node_modules/@cucumber/cucumber/src/cli/run.ts:32:14) {
[cause]: Error [ERR_REQUIRE_ESM]: require() of ES Module /path/to/node_modules/callsites/index.js from /path/to/node_modules/cucumber-tsflow/dist/our-callsite.js not supported.
}
Root Cause Analysis
Let's break down why this is happening. cucumber-tsflow@4.5.2
declares a dependency on callsites: "^4.2.0"
in its package.json
. Now, callsites@4.0.0+
transitioned to pure ES modules, a breaking change that caused ripples. The problem is that the compiled code within cucumber-tsflow
stubbornly clings to require("callsites")
in /dist/our-callsite.js:13
. Node.js 12 and later versions put their foot down and disallow require()
of ES modules. The package.json
dependency got updated, but the compiled JavaScript code didn't get the memo about ES modules, hence the clash.
This issue arises because cucumber-tsflow
was not updated to handle the change in callsites
. It still uses CommonJS-style imports, which are not compatible with ES modules. The compiled JavaScript code needs to be updated to use dynamic imports or to be compiled as an ES module itself.
Steps to Reproduce
Want to see this in action? Follow these steps:
- Create a new project with the following
package.json
:
{
"devDependencies": {
"@cucumber/cucumber": "^12.2.0",
"cucumber-tsflow": "^4.5.2",
"ts-node": "^10.9.2",
"typescript": "^5.0.0"
}
}
- Install dependencies (callsites@4.2.0 will be installed automatically):
npm install
- Create a simple step file (
test.step.ts
):
import { binding, given } from 'cucumber-tsflow';
@binding()
export default class TestSteps {
@given('I have a test step')
public testStep() {
console.log('Test step executed');
}
}
- Create a feature file (
test.feature
):
Feature: Test
Scenario: Simple test
Given I have a test step
- Create cucumber configuration (
cucumber.js
):
module.exports = {
default: {
paths: ['*.feature'],
requireModule: ['ts-node/register'],
require: ['*.step.ts'],
},
};
- Run cucumber:
npx cucumber-js --config=cucumber.js
- Observe the error 🔴: Witness the
ERR_REQUIRE_ESM
error in all its glory.
Expected Behavior
We expect Cucumber tests to run smoothly, with cucumber-tsflow
decorators working their magic without a hitch. The ideal scenario is a successful test run!
Actual Behavior
Instead, the tests crash and burn immediately, spitting out the ERR_REQUIRE_ESM
error the moment it tries to load cucumber-tsflow
. It's a roadblock right from the start.
Environment
- cucumber-tsflow version: 4.5.2
- @cucumber/cucumber version: 12.2.0
- callsites version: 4.2.0 (automatically installed)
- Node.js version: 18.x, 20.x, 22.x (all affected)
- Package manager: npm, yarn, pnpm, bun (all affected)
- OS: macOS, Linux, Windows (all affected)
Current Workaround
Fear not! There's a workaround to get you back on track. Force callsites
to use the last CommonJS version:
For yarn:
{
"resolutions": {
"callsites": "3.1.0"
}
}
For npm:
{
"overrides": {
"callsites": "3.1.0"
}
}
By pinning callsites
to version 3.1.0
, which is CommonJS compatible, you bypass the ES module issue. This workaround ensures that the require
statement in cucumber-tsflow
can successfully load the callsites
module.
Suggested Fix
The long-term solution involves updating the compiled JavaScript code in our-callsite.js
to play nice with ES modules. Here are a few paths to consider:
- Dynamic import (recommended for CommonJS output):
// Instead of: const callsites_1 = require("callsites");
const callsites_1 = await import("callsites");
This approach replaces the synchronous `require` with an asynchronous `import()`, which is designed to handle ES modules in CommonJS environments. It's the most direct way to solve the immediate problem.
- Update TypeScript compilation to output ES modules. Configure
tsconfig.json
to emit ES modules. This aligns the entire project with ES module syntax, ensuring compatibility with modern JavaScript practices. However, this might require changes throughout the codebase. - Provide dual package (CommonJS + ES modules). Consider creating a dual package that offers both CommonJS and ES module versions. This allows consumers to choose the appropriate version based on their environment and build system. This provides maximum flexibility but requires careful management of different module formats.
Additional Context
This issue is a thorn in the side of any project using cucumber-tsflow
with recent Node.js versions and package managers that automatically grab the latest callsites
version. The root cause is the transition of callsites
to pure ES modules in v4.0.0, while cucumber-tsflow
's compiled code remained stuck in the past.
Related Issues
- callsites breaking change: https://github.com/sindresorhus/callsites/releases/tag/v4.0.0
Let's work together to get this fixed and make cucumber-tsflow
work seamlessly with the latest and greatest JavaScript modules!