Fix: ERR_REQUIRE_ESM In Cucumber-tsflow With Callsites

by ADMIN 55 views

🐛 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:

  1. 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"
  }
}
  1. Install dependencies (callsites@4.2.0 will be installed automatically):
npm install
  1. 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');
    }
}
  1. Create a feature file (test.feature):
Feature: Test
  Scenario: Simple test
    Given I have a test step
  1. Create cucumber configuration (cucumber.js):
module.exports = {
    default: {
        paths: ['*.feature'],
        requireModule: ['ts-node/register'],
        require: ['*.step.ts'],
    },
};
  1. Run cucumber:
npx cucumber-js --config=cucumber.js
  1. 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:

  1. 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.
  1. 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.
  2. 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

Let's work together to get this fixed and make cucumber-tsflow work seamlessly with the latest and greatest JavaScript modules!