Skip to content

Commit 5224de2

Browse files
authored
use import by default for support code (#2337)
1 parent ddb3312 commit 5224de2

File tree

5 files changed

+54
-49
lines changed

5 files changed

+54
-49
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ Please see [CONTRIBUTING.md](./CONTRIBUTING.md) on how to contribute to Cucumber
1414
### Changed
1515
- BREAKING CHANGE: Use appropriate module loading mechanism for configuration files ([#2334](https://github.com/cucumber/cucumber-js/pull/2334))
1616
- BREAKING CHANGE: Use `await import()` to load all custom formatters and snippet syntaxes ([#2334](https://github.com/cucumber/cucumber-js/pull/2334))
17+
- BREAKING CHANGE: Use `await import()` for default support code loading ([#2337](https://github.com/cucumber/cucumber-js/pull/2337))
1718

1819
### Removed
1920
- BREAKING CHANGE: Drop support for Node.js 14, 16 and 19 ([#2331](https://github.com/cucumber/cucumber-js/pull/2331))

docs/configuration.md

Lines changed: 33 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -8,40 +8,20 @@ _**You are reading the documentation in the `main` development branch, which mig
88

99
You can keep your configuration in a file. Cucumber will look for one of these files in the root of your project, and use the first one it finds:
1010

11-
- `cucumber.js`
12-
- `cucumber.cjs`
13-
- `cucumber.mjs`
1411
- `cucumber.json`
1512
- `cucumber.yaml`
1613
- `cucumber.yml`
14+
- `cucumber.js`
15+
- `cucumber.cjs`
16+
- `cucumber.mjs`
1717

1818
You can also put your file somewhere else and tell Cucumber via the `--config` CLI option:
1919

2020
```shell
21-
$ cucumber-js --config config/cucumber.js
22-
```
23-
24-
Here's a concise example of a configuration file in CommonJS format:
25-
26-
```js
27-
module.exports = {
28-
default: {
29-
parallel: 2,
30-
format: ['html:cucumber-report.html']
31-
}
32-
}
33-
```
34-
35-
And the same in ESM format:
36-
37-
```js
38-
export default {
39-
parallel: 2,
40-
format: ['html:cucumber-report.html']
41-
}
21+
$ cucumber-js --config config/cucumber.json
4222
```
4323

44-
And the same in JSON format:
24+
Here's a concise example of a configuration file in JSON format:
4525

4626
```json
4727
{
@@ -61,6 +41,26 @@ default:
6141
- "html:cucumber-report.html"
6242
```
6343
44+
And the same in JavaScript (ESM) format:
45+
46+
```js
47+
export default {
48+
parallel: 2,
49+
format: ['html:cucumber-report.html']
50+
}
51+
```
52+
53+
And the same in JavaScript (CommonJS) format:
54+
55+
```js
56+
module.exports = {
57+
default: {
58+
parallel: 2,
59+
format: ['html:cucumber-report.html']
60+
}
61+
}
62+
```
63+
6464
Cucumber also supports the configuration being a string of options in the style of the CLI, though this isn't recommended:
6565

6666
```js
@@ -87,7 +87,7 @@ These options can be used in a configuration file (see [above](#files)) or on th
8787
| `failFast` | `boolean` | No | `--fail-fast` | Stop running tests when a test fails - see [Fail Fast](./fail_fast.md) | false |
8888
| `format` | `string[]` | Yes | `--format`, `-f` | Name/path and (optionally) output file path of each formatter to use - see [Formatters](./formatters.md) | [] |
8989
| `formatOptions` | `object` | Yes | `--format-options` | Options to be provided to formatters - see [Formatters](./formatters.md) | {} |
90-
| `import` | `string[]` | Yes | `--import`, `-i` | Paths to where your support code is, for ESM - see [ESM](./esm.md) | [] |
90+
| `import` | `string[]` | Yes | `--import`, `-i` | Paths to where your support code is | [] |
9191
| `language` | `string` | No | `--language` | Default language for your feature files | en |
9292
| `name` | `string` | No | `--name` | Regular expressions of which scenario names should match one of to be run - see [Filtering](./filtering.md#names) | [] |
9393
| `order` | `string` | No | `--order` | Run in the order defined, or in a random order | defined |
@@ -123,15 +123,15 @@ For more granular options to control _which scenarios_ from your features should
123123
By default, Cucumber finds support code files with this logic:
124124

125125
* If the features live in a `features` directory (at any level)
126-
* `features/**/*.(js)`
126+
* `features/**/*.@(js|cjs|mjs)`
127127
* Otherwise
128-
* `<DIR>/**/*.(js)` for each directory containing the selected features
128+
* `<DIR>/**/*.@(js|cjs|mjs)` for each directory containing the selected features
129129

130-
If your files are somewhere else, you can override this by proving your own [glob](https://github.com/isaacs/node-glob), directory or file path to the `require` configuration option:
130+
If your files are somewhere else, you can override this by proving your own [glob](https://github.com/isaacs/node-glob), directory or file path to the `import` configuration option:
131131

132-
- In a configuration file `{ require: ['somewhere-else/support/*.js'] }`
133-
- On the CLI `$ cucumber-js --require somewhere-else/support/*.js`
132+
- In a configuration file `{ import: ['somewhere-else/support/*.js'] }`
133+
- On the CLI `$ cucumber-js --import somewhere-else/support/*.js`
134134

135-
Once you specify any `require` options, the defaults described above are no longer applied. The option is repeatable, so you can provide several values and they'll be combined, meaning you can load files from multiple locations.
135+
Once you specify any `import` options, the defaults described above are no longer applied. The option is repeatable, so you can provide several values and they'll be combined, meaning you can load files from multiple locations.
136136

137-
The default behaviour and the `require` option both use the [legacy CommonJS modules API](https://nodejs.org/api/modules.html) to load your files. If your files are native ES modules, you'll need to use the `import` option instead in the same way, and they'll be loaded with the [new ES modules API](https://nodejs.org/api/esm.html). See [ES Modules](./esm.md) for more on using Cucumber in an ESM project.
137+
The default behaviour and the `import` option both use the [new ES modules API](https://nodejs.org/api/esm.html) to load your files. This should work fine for the majority of cases, but sometimes (e.g. when transpiling with the `require-module` option), you'll need to use the `require` option instead in the same way, and they'll be loaded with the [legacy CommonJS modules API](https://nodejs.org/api/modules.html).

src/api/paths.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -147,9 +147,8 @@ async function deriveSupportPaths(
147147
unexpandedImportPaths.length === 0
148148
) {
149149
const defaultPaths = getFeatureDirectoryPaths(cwd, featurePaths)
150-
const requirePaths = await expandPaths(cwd, defaultPaths, '.js')
151-
const importPaths = await expandPaths(cwd, defaultPaths, '.mjs')
152-
return { requirePaths, importPaths }
150+
const importPaths = await expandPaths(cwd, defaultPaths, '.@(js|cjs|mjs)')
151+
return { requirePaths: [], importPaths }
153152
}
154153
const requirePaths =
155154
unexpandedRequirePaths.length > 0

src/api/paths_spec.ts

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,8 @@ describe('resolvePaths', () => {
5151
// Assert
5252
expect(featurePaths).to.eql([featurePath])
5353
expect(unexpandedFeaturePaths).to.eql([relativeFeaturePath])
54-
expect(requirePaths).to.eql([jsSupportCodePath])
55-
expect(importPaths).to.eql([esmSupportCodePath])
54+
expect(requirePaths).to.eql([])
55+
expect(importPaths).to.eql([jsSupportCodePath, esmSupportCodePath])
5656
})
5757

5858
it('deduplicates features based on overlapping expressions', async function () {
@@ -113,7 +113,7 @@ describe('resolvePaths', () => {
113113
await fsExtra.outputFile(supportCodePath, '')
114114

115115
// Act
116-
const { featurePaths, unexpandedFeaturePaths, requirePaths } =
116+
const { featurePaths, unexpandedFeaturePaths, importPaths } =
117117
await resolvePaths(
118118
new FakeLogger(),
119119
cwd,
@@ -130,7 +130,7 @@ describe('resolvePaths', () => {
130130
// Assert
131131
expect(featurePaths).to.eql([featurePath])
132132
expect(unexpandedFeaturePaths).to.eql([relativeFeaturePath])
133-
expect(requirePaths).to.eql([supportCodePath])
133+
expect(importPaths).to.eql([supportCodePath])
134134
})
135135
})
136136

@@ -145,7 +145,7 @@ describe('resolvePaths', () => {
145145
await fsExtra.outputFile(supportCodePath, '')
146146

147147
// Act
148-
const { featurePaths, unexpandedFeaturePaths, requirePaths } =
148+
const { featurePaths, unexpandedFeaturePaths, importPaths } =
149149
await resolvePaths(
150150
new FakeLogger(),
151151
cwd,
@@ -162,7 +162,7 @@ describe('resolvePaths', () => {
162162
// Assert
163163
expect(featurePaths).to.eql([featurePath])
164164
expect(unexpandedFeaturePaths).to.eql([relativeFeaturePath])
165-
expect(requirePaths).to.eql([supportCodePath])
165+
expect(importPaths).to.eql([supportCodePath])
166166
})
167167

168168
it('returns the appropriate .md and support code paths', async function () {
@@ -179,7 +179,7 @@ describe('resolvePaths', () => {
179179
await fsExtra.outputFile(supportCodePath, '')
180180

181181
// Act
182-
const { featurePaths, unexpandedFeaturePaths, requirePaths } =
182+
const { featurePaths, unexpandedFeaturePaths, importPaths } =
183183
await resolvePaths(
184184
new FakeLogger(),
185185
cwd,
@@ -196,7 +196,7 @@ describe('resolvePaths', () => {
196196
// Assert
197197
expect(featurePaths).to.eql([featurePath])
198198
expect(unexpandedFeaturePaths).to.eql([relativeFeaturePath])
199-
expect(requirePaths).to.eql([supportCodePath])
199+
expect(importPaths).to.eql([supportCodePath])
200200
})
201201
})
202202

@@ -297,8 +297,8 @@ describe('resolvePaths', () => {
297297
const relativeFeaturePath = path.join('features', 'a.feature')
298298
const featurePath = path.join(cwd, relativeFeaturePath)
299299
await fsExtra.outputFile(featurePath, '')
300-
const jsSupportCodePath = path.join(cwd, 'features', 'a.js')
301-
await fsExtra.outputFile(jsSupportCodePath, '')
300+
const cjsSupportCodePath = path.join(cwd, 'features', 'a.cjs')
301+
await fsExtra.outputFile(cjsSupportCodePath, '')
302302
const esmSupportCodePath = path.join(cwd, 'features', 'a.mjs')
303303
await fsExtra.outputFile(esmSupportCodePath, '')
304304

@@ -311,8 +311,8 @@ describe('resolvePaths', () => {
311311
},
312312
{
313313
requireModules: [],
314-
requirePaths: [],
315-
importPaths: [],
314+
requirePaths: [cjsSupportCodePath],
315+
importPaths: [esmSupportCodePath],
316316
}
317317
)
318318

@@ -323,7 +323,7 @@ describe('resolvePaths', () => {
323323
)
324324
expect(logger.debug).to.have.been.calledWith(
325325
'Found support files to load via `require` based on configuration:',
326-
[jsSupportCodePath]
326+
[cjsSupportCodePath]
327327
)
328328
expect(logger.debug).to.have.been.calledWith(
329329
'Found support files to load via `import` based on configuration:',

src/configuration/validate_configuration.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@ export function validateConfiguration(
1010
'`publishQuiet` option is no longer needed, you can remove it from your configuration; see https://github.com/cucumber/cucumber-js/blob/main/docs/deprecations.md'
1111
)
1212
}
13+
if (configuration.requireModule.length && !configuration.require.length) {
14+
logger.warn(
15+
'Use of `require-module` option normally means you should specify your support code paths with `require`; see https://github.com/cucumber/cucumber-js/blob/main/docs/configuration.md#finding-your-code'
16+
)
17+
}
1318
if (configuration.retryTagFilter && !configuration.retry) {
1419
throw new Error(
1520
'a positive `retry` count must be specified when setting `retryTagFilter`'

0 commit comments

Comments
 (0)