Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue 2105 more detailed error #2264

Merged
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
Please see [CONTRIBUTING.md](./CONTRIBUTING.md) on how to contribute to Cucumber.

## [Unreleased]

- Change the error message emitted when trying to import a CommonJS module with the --import directive or require an ES module with the --require directive to one less cryptic than that emitted by NodeJS ([2264](https://github.com/cucumber/cucumber-js/pull/2264))
## [9.0.1] - 2023-03-15
### Fixed
- Ensure feature paths are properly deduplicated ([#2258](https://github.com/cucumber/cucumber-js/pull/2258))
Expand Down
47 changes: 47 additions & 0 deletions features/esm_require_import_error.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
@dev
Feature: ES and CommonJS Error disabiguation

Import must be used for ES modules, Require for
CommonJS modules, but when developers cross these up
cucumber-js needs to throw an understandable error.

Scenario: ES module invoked with --require
Given a file named "features/a.feature" with:
"""
Feature:
Scenario: one
Given a step passes
"""
And a file named "features/step_definitions/cucumber_steps.js" with:
"""
import {Given} from '@cucumber/cucumber'

Given(/^a step passes$/, function() {});
"""
When I run cucumber-js with `--require features/**/*.js`
Then it fails
And the error output contains the text:
"""
Error: Cucumber expected a CommonJS module
"""

Scenario: CommonJS module invoked with --import
Given a file named "features/a.feature" with:
"""
Feature:
Scenario: one
Given a step passes
"""
And a file named "features/step_definitions/cucumber_steps.mjs" with:
"""
const {Given} = require('@cucumber/cucumber')

Given(/^a step passes$/, function() {});
"""
When I run cucumber-js with `--import features/**/*.mjs`
Then it fails
And the error output contains the text:
"""
Error: Cucumber expected an ES module
"""

20 changes: 18 additions & 2 deletions src/api/support.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,19 @@ import { pathToFileURL } from 'url'
// eslint-disable-next-line @typescript-eslint/no-var-requires
const { importer } = require('../importer')

function tryRequire(path: string) {
try {
return require(path)
} catch (error) {
if (error.message === 'Cannot use import statement outside a module') {
throw new Error(`Cucumber expected a CommonJS module at '${path}' but found an ES module.
Either change the file to CommonJS syntax or use the --import directive instead of --require`)
} else {
throw error
}
}
}

export async function getSupportCodeLibrary({
cwd,
newId,
Expand All @@ -24,10 +37,13 @@ export async function getSupportCodeLibrary({
requirePaths,
importPaths,
})
requireModules.map((module) => require(module))
requirePaths.map((path) => require(path))

requireModules.map((module) => tryRequire(module))
requirePaths.map((path) => tryRequire(path))

for (const path of importPaths) {
await importer(pathToFileURL(path))
}

return supportCodeLibraryBuilder.finalize()
}
11 changes: 10 additions & 1 deletion src/importer.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,16 @@
* @return {Promise<any>} Promise that resolves to the loaded module
*/
async function importer(descriptor) {
return await import(descriptor)
return await import(descriptor).catch((error) => {
if (error.message.includes('require is not defined in ES module scope')) {
throw new Error(
`Cucumber expected an ES module at '${descriptor}' but found a CommonJS module.
Either change the file to ES syntax or use the --require directive instead of --import`
)
} else {
throw error
}
})
}

module.exports = { importer }