Description
Problem
When I attempt to run Cucumber.js with a step definition defined in a native ECMAScript module (as documented here), the attempt fails with a warning that I am using CommonJS's require()
on an ES module.
Cucumber.js version: 6.0.5
Node version: 13.8.0
Steps to reproduce
-
Setup a basic NPM package directory using
npm init
andnpm i cucumber
- Set
"type": "module"
in thepackage.json
file to ensure that JS files will be treated as native modules
- Set
-
Create a basic feature file,
features/mjs.feature
:Feature: Native JS Modules Scenario: Load a native JS module step definition Given I have 42 cucumbers in my belly
-
Create a basic step definition file,
features/step_definitions.js
:import { Given } from "cucumber"; Given("I have {int} cucumbers in my belly", function (cucumberCount) { console.log("Step parsed."); });
-
Attempt to run Cucumber:
$ ./node_modules/.bin/cucumber-js
Expected result
The step definition module should be loaded and used to parse the step.
Actual result
Error [ERR_REQUIRE_ESM]: Must use import to load ES Module: /some/path/cucumbertest/features/step_definitions.js
require() of ES modules is not supported.
require() of /some/path/cucumbertest/features/step_definitions.js from /some/path/cucumbertest/node_modules/cucumber/lib/cli/index.js is an ES module file as it is a .js file whose nearest parent package.json contains "type": "module" which defines all .js files in that package scope as ES modules.
Instead rename step_definitions.js to end in .cjs, change the requiring code to use import(), or remove "type": "module" from /some/path/cucumbertest/package.json.
at Object.Module._extensions..js (internal/modules/cjs/loader.js:1167:13)
at Module.load (internal/modules/cjs/loader.js:1000:32)
at Function.Module._load (internal/modules/cjs/loader.js:899:14)
at Module.require (internal/modules/cjs/loader.js:1040:19)
at require (internal/modules/cjs/helpers.js:72:18)
at /some/path/cucumbertest/node_modules/cucumber/lib/cli/index.js:119:42
at Array.forEach (<anonymous>)
at Cli.getSupportCodeLibrary (/some/path/cucumbertest/node_modules/cucumber/lib/cli/index.js:119:22)
at Cli.run (/some/path/cucumbertest/node_modules/cucumber/lib/cli/index.js:141:37)
at async Object.run [as default] (/some/path/cucumbertest/node_modules/cucumber/lib/cli/run.js:30:14)
Closing
For what it's worth, I really appreciate the work that the Cucumber.js team has put in on this project, it has been a major asset to me and my company. My team has invested some time in building some shared application components in native JS modules, and until recently I had been assuming (based on the import
syntax in the most current documentation) that this stuff would work with Cucumber when we got around to integrating with our test framework. That appears not to be the case, though. Since the shared component is already written and integrated into other parts of our application, I'm reluctant use only step definitions and support code written in CommonJS because of interoperability challenges.
Other things I attempted to get this to work included...
- Giving the native module files
.mjs
extensions, per Node.js convention (this caused them to be overlooked by Cucumber's auto-loading logic, and using--require
to explicitly load the step definition module threwError [ERR_REQUIRE_ESM]: Must use import to load ES Module
- Wrapping the native modules in CJS modules which use the dynamic
import()
function; the problem was that this approach is asynchronous, and even if the import promise is exported by the CJS module, Cucumber doesn't appear to wait for that promise to resolve before proceeding, because the step was marked as undefined (suggesting that the step definition in the native module was not yet registered).
If it's within my ability, I'd be happy to submit a PR to help resolve this issue, but I'm unfamiliar with the internal workings of Cucumber.js and would appreciate if someone could point me in the right direction.
Metadata
Metadata
Assignees
Type
Projects
Status