diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f41380cd..bcea99e94 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/). Please see [CONTRIBUTING.md](https://github.com/cucumber/cucumber/blob/master/CONTRIBUTING.md) on how to contribute to Cucumber. ## [Unreleased] +### Changed +- Rename the `cucumber-js` binary's underlying file to be `cucumber.js`, so it doesn't fall foul of Node.js module conventions and plays nicely with ESM loaders (see [documentation](./docs/esm.md#transpiling)) ([#1993](https://github.com/cucumber/cucumber-js/pull/1993)) + ### Fixed - Correctly escape backslashes in generated expressions for snippets ([#1324](https://github.com/cucumber/cucumber-js/issues/1324) [#1995](https://github.com/cucumber/cucumber-js/pull/1995)) diff --git a/bin/cucumber-js b/bin/cucumber.js similarity index 100% rename from bin/cucumber-js rename to bin/cucumber.js diff --git a/docs/esm.md b/docs/esm.md index 3cd47a526..bf87e1938 100644 --- a/docs/esm.md +++ b/docs/esm.md @@ -58,4 +58,8 @@ export const ci = { ## Transpiling -When using a transpiler for e.g. TypeScript, ESM isn't supported - you'll need to configure your transpiler to output modules in CommonJS syntax (for now). See [this GitHub issue](https://github.com/cucumber/cucumber-js/issues/1844) for the latest on support for ESM loaders. +You can use [ESM loaders](https://nodejs.org/api/esm.html#loaders) to transpile your support code on the fly. The `requireModule` configuration option only works with CommonJS (i.e. `require` hooks) and is not applicable here. Cucumber doesn't have an equivalent option for ESM loaders because they currently can't be registered in-process, so you'll need to declare the loader externally, like this: + +```shell +$ NODE_OPTIONS="--loader " npx cucumber-js +``` diff --git a/docs/transpiling.md b/docs/transpiling.md index 8da547946..9dabcdcfb 100644 --- a/docs/transpiling.md +++ b/docs/transpiling.md @@ -20,10 +20,6 @@ Your `tsconfig.json` should have these `compilerOptions` on: Other than that, a pretty standard TypeScript setup should work as expected. -> ⚠️ Some TypeScript setups use `esnext` modules by default, -> which doesn't marry well with Node. You may consider using commonjs instead. -> See how to add [extra configuration](#extra-configuration) below. - You'll also need to specify where your support code is, since `.ts` files won't be picked up by default. ### With ts-node @@ -33,32 +29,31 @@ If you are using [ts-node](https://github.com/TypeStrong/ts-node): - In a configuration file `{ requireModule: ['ts-node/register'], require: ['step-definitions/**/*.ts'] }` - On the CLI `$ cucumber-js --require-module ts-node/register --require 'step-definitions/**/*.ts'` -### With Babel +#### ESM -If you are using babel with [@babel/preset-typescript](https://babeljs.io/docs/en/babel-preset-typescript): +For ESM projects, you can use `ts-node`'s ESM loader and then `import` your TypeScript files: -- In a configuration file `{ requireModule: ['@babel/register'], require: ['step-definitions/**/*.ts'] }` -- On the CLI `$ cucumber-js --require-module @babel/register --require 'step-definitions/**/*.ts'` - -## Extra Configuration +```shell +$ NODE_OPTIONS"--loader ts-node/esm" cucumber-js --import 'step-definitions/**/*.ts' +``` -Sometimes the required module (say `ts-node/register`) needs extra configuration. For example, you might want to configure it such that it prevents the compiled JS being written out to files, and pass some compiler options. In such cases, create a script (say, `tests.setup.js`): +Don't forget to set your `tsconfig.json` to emit JavaScript with `import` and `export` statements: -```js -require('ts-node').register({ - transpileOnly: true, - compilerOptions: { - "module": "commonjs", - "resolveJsonModule": true, - }, -}); +```json +{ + "compilerOptions": { + "module": "esnext" + } +} ``` -And then require it using the `require` option: +### With Babel + +If you are using babel with [@babel/preset-typescript](https://babeljs.io/docs/en/babel-preset-typescript): -- In a configuration file `{ require: ['tests.setup.js', 'features/**/*.ts'] }` -- On the CLI `$ cucumber-js --require tests.setup.js --require 'features/**/*.ts'` +- In a configuration file `{ requireModule: ['@babel/register'], require: ['step-definitions/**/*.ts'] }` +- On the CLI `$ cucumber-js --require-module @babel/register --require 'step-definitions/**/*.ts'` -## ESM +### ESM -Cucumber doesn't yet support native ESM loader hooks ([see GitHub issue](https://github.com/cucumber/cucumber-js/issues/1844)). +See [ESM](./esm.md) for general advice on using loaders for transpilation in ESM projects. diff --git a/features/support/hooks.ts b/features/support/hooks.ts index 686822fbf..ce7a5caf3 100644 --- a/features/support/hooks.ts +++ b/features/support/hooks.ts @@ -45,7 +45,7 @@ Before(function ( } catch (error) { warnUserAboutEnablingDeveloperMode(error) } - this.localExecutablePath = path.join(projectPath, 'bin', 'cucumber-js') + this.localExecutablePath = path.join(projectPath, 'bin', 'cucumber.js') }) Before('@esm', function (this: World) { @@ -102,7 +102,7 @@ Before('@global-install', function (this: World) { this.globalExecutablePath = path.join( globalInstallCucumberPath, 'bin', - 'cucumber-js' + 'cucumber.js' ) }) diff --git a/package.json b/package.json index f7804af9c..32c267c5f 100644 --- a/package.json +++ b/package.json @@ -291,7 +291,7 @@ "cck-test": "mocha 'compatibility/**/*_spec.ts'", "docs:ci": "api-extractor run --verbose", "docs:local": "api-extractor run --verbose --local && api-documenter markdown --input-folder ./tmp/api-extractor --output-folder ./docs/api", - "feature-test": "node ./bin/cucumber-js", + "feature-test": "node bin/cucumber.js", "lint-autofix": "eslint --fix \"{compatibility,example,features,scripts,src,test}/**/*.ts\"", "lint-code": "eslint \"{compatibility,example,features,scripts,src,test}/**/*.ts\"", "lint-dependencies": "dependency-lint", @@ -303,14 +303,14 @@ "prepublishOnly": "rm -rf lib && npm run build-local", "pretest-coverage": "npm run build-local", "pretypes-test": "npm run build-local", - "test-coverage": "nyc --silent mocha 'src/**/*_spec.ts' 'compatibility/**/*_spec.ts' && nyc --silent --no-clean node ./bin/cucumber-js && nyc report --reporter=lcov", + "test-coverage": "nyc --silent mocha 'src/**/*_spec.ts' 'compatibility/**/*_spec.ts' && nyc --silent --no-clean node bin/cucumber.js && nyc report --reporter=lcov", "test": "npm run lint && npm run types-test && npm run unit-test && npm run cck-test && npm run feature-test", "types-test": "tsd", "unit-test": "mocha 'src/**/*_spec.ts'", "update-dependencies": "npx npm-check-updates --upgrade" }, "bin": { - "cucumber-js": "./bin/cucumber-js" + "cucumber-js": "bin/cucumber.js" }, "license": "MIT", "files": [ diff --git a/src/configuration/argv_parser.ts b/src/configuration/argv_parser.ts index 5ccb45128..193df328f 100644 --- a/src/configuration/argv_parser.ts +++ b/src/configuration/argv_parser.ts @@ -1,6 +1,5 @@ import { Command } from 'commander' import merge from 'lodash.merge' -import path from 'path' import { dialects } from '@cucumber/gherkin' import Formatters from '../formatter/helpers/formatters' import { version } from '../version' @@ -65,7 +64,7 @@ const ArgvParser = { }, parse(argv: string[]): IParsedArgv { - const program = new Command(path.basename(argv[1])) + const program = new Command('cucumber-js') program .storeOptionsAsProperties(false)