From 3d23eec9938887ab5051744315a83ccf2781b15f Mon Sep 17 00:00:00 2001 From: David Goss Date: Sun, 8 Oct 2023 16:48:32 +0100 Subject: [PATCH] use import wherever possible to load code (#2334) --- CHANGELOG.md | 4 + dependency-lint.yml | 1 - features/custom_formatter.feature | 9 +-- package-lock.json | 64 +++------------- package.json | 3 +- src/api/support.ts | 5 +- src/configuration/from_file.ts | 55 +++++++++++--- src/configuration/from_file_spec.ts | 109 +++++++++++++++++++++++---- src/formatter/builder.ts | 24 ++---- src/formatter/builder_spec.ts | 1 - src/formatter/fixtures/typescript.ts | 1 - src/importer.js | 13 ---- src/runtime/parallel/worker.ts | 4 +- test-d/{api.ts => api.mts} | 0 tsconfig.json | 2 +- 15 files changed, 168 insertions(+), 127 deletions(-) delete mode 100644 src/formatter/fixtures/typescript.ts delete mode 100644 src/importer.js rename test-d/{api.ts => api.mts} (100%) diff --git a/CHANGELOG.md b/CHANGELOG.md index a810af2e6..6eb25fa76 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,10 @@ Please see [CONTRIBUTING.md](./CONTRIBUTING.md) on how to contribute to Cucumber ### Added - Add support for Node.js 20 ([#2331](https://github.com/cucumber/cucumber-js/pull/2331)) +### Changed +- BREAKING CHANGE: Use appropriate module loading mechanism for configuration files ([#2334](https://github.com/cucumber/cucumber-js/pull/2334)) +- BREAKING CHANGE: Use `await import()` to load all custom formatters and snippet syntaxes ([#2334](https://github.com/cucumber/cucumber-js/pull/2334)) + ### Removed - BREAKING CHANGE: Drop support for Node.js 14, 16 and 19 ([#2331](https://github.com/cucumber/cucumber-js/pull/2331)) diff --git a/dependency-lint.yml b/dependency-lint.yml index 8bba52fca..92c40926b 100644 --- a/dependency-lint.yml +++ b/dependency-lint.yml @@ -48,7 +48,6 @@ requiredModules: - 'dist/**/*' - 'lib/**/*' - 'node_modules/**/*' - - 'src/importer.js' - 'tmp/**/*' root: '**/*.{js,ts}' stripLoaders: false diff --git a/features/custom_formatter.feature b/features/custom_formatter.feature index 22cd33e7c..8acb1959f 100644 --- a/features/custom_formatter.feature +++ b/features/custom_formatter.feature @@ -30,9 +30,9 @@ Feature: custom formatter this.log(testCaseAttempt.gherkinDocument.feature.name + ' / ' + testCaseAttempt.pickle.name + '\n') const parsed = formatterHelpers.parseTestCaseAttempt({ cwd: this.cwd, - snippetBuilder: this.snippetBuilder, + snippetBuilder: this.snippetBuilder, supportCodeLibrary: this.supportCodeLibrary, - testCaseAttempt + testCaseAttempt }) parsed.testSteps.forEach(testStep => { this.log(' ' + testStep.keyword + (testStep.text || '') + ' - ' + Status[testStep.result.status] + '\n') @@ -83,9 +83,9 @@ Feature: custom formatter this.log(testCaseAttempt.gherkinDocument.feature.name + ' / ' + testCaseAttempt.pickle.name + '\n') const parsed = formatterHelpers.parseTestCaseAttempt({ cwd: this.cwd, - snippetBuilder: this.snippetBuilder, + snippetBuilder: this.snippetBuilder, supportCodeLibrary: this.supportCodeLibrary, - testCaseAttempt + testCaseAttempt }) parsed.testSteps.forEach(testStep => { this.log(' ' + testStep.keyword + (testStep.text || '') + ' - ' + Status[testStep.result.status] + '\n') @@ -139,7 +139,6 @@ Feature: custom formatter Then it passes Examples: | EXT | IMPORT_STATEMENT | EXPORT_STATEMENT | - | .ts | import {Formatter} from '@cucumber/cucumber' | export default CustomFormatter | | .mjs | import {Formatter} from '@cucumber/cucumber' | export default CustomFormatter | | .js | const {Formatter} = require('@cucumber/cucumber') | module.exports = CustomFormatter | | .js | const {Formatter} = require('@cucumber/cucumber') | exports.default = CustomFormatter | diff --git a/package-lock.json b/package-lock.json index 82f03ec6e..2ebd9bc76 100644 --- a/package-lock.json +++ b/package-lock.json @@ -38,6 +38,7 @@ "mkdirp": "^2.1.5", "mz": "^2.7.0", "progress": "^2.0.3", + "read-pkg-up": "^7.0.1", "resolve-pkg": "^2.0.0", "semver": "7.5.3", "string-argv": "^0.3.1", @@ -132,7 +133,6 @@ "version": "7.21.4", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.21.4.tgz", "integrity": "sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g==", - "dev": true, "dependencies": { "@babel/highlight": "^7.18.6" }, @@ -333,7 +333,6 @@ "version": "7.19.1", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", - "dev": true, "engines": { "node": ">=6.9.0" } @@ -365,7 +364,6 @@ "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", - "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.18.6", "chalk": "^2.0.0", @@ -379,7 +377,6 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, "dependencies": { "color-convert": "^1.9.0" }, @@ -391,7 +388,6 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -405,7 +401,6 @@ "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, "dependencies": { "color-name": "1.1.3" } @@ -413,14 +408,12 @@ "node_modules/@babel/highlight/node_modules/color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" }, "node_modules/@babel/highlight/node_modules/escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, "engines": { "node": ">=0.8.0" } @@ -429,7 +422,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, "engines": { "node": ">=4" } @@ -438,7 +430,6 @@ "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, "dependencies": { "has-flag": "^3.0.0" }, @@ -1611,8 +1602,7 @@ "node_modules/@types/normalize-package-data": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz", - "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==", - "dev": true + "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==" }, "node_modules/@types/progress": { "version": "2.0.5", @@ -3060,7 +3050,6 @@ "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, "dependencies": { "is-arrayish": "^0.2.1" } @@ -4086,8 +4075,7 @@ "node_modules/function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, "node_modules/function.prototype.name": { "version": "1.1.5", @@ -4389,7 +4377,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, "dependencies": { "function-bind": "^1.1.1" }, @@ -4705,8 +4692,7 @@ "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" }, "node_modules/is-bigint": { "version": "1.0.4", @@ -4764,7 +4750,6 @@ "version": "2.12.0", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.0.tgz", "integrity": "sha512-RECHCBCd/viahWmwj6enj19sKbHfJrddi/6cBDsNTKbNq0f7VeaUkBo60BqzvPqo/W54ChS62Z5qyun7cfOMqQ==", - "dev": true, "dependencies": { "has": "^1.0.3" }, @@ -5182,8 +5167,7 @@ "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" }, "node_modules/js-yaml": { "version": "3.13.1", @@ -5213,8 +5197,7 @@ "node_modules/json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" }, "node_modules/json-schema-traverse": { "version": "0.4.1", @@ -5291,8 +5274,7 @@ "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" }, "node_modules/locate-path": { "version": "6.0.0", @@ -6217,7 +6199,6 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, "engines": { "node": ">=6" } @@ -6273,7 +6254,6 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", @@ -6300,7 +6280,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, "engines": { "node": ">=8" } @@ -6324,8 +6303,7 @@ "node_modules/path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, "node_modules/path-platform": { "version": "0.11.15", @@ -6638,7 +6616,6 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", - "dev": true, "dependencies": { "@types/normalize-package-data": "^2.4.0", "normalize-package-data": "^2.5.0", @@ -6653,7 +6630,6 @@ "version": "7.0.1", "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", - "dev": true, "dependencies": { "find-up": "^4.1.0", "read-pkg": "^5.2.0", @@ -6670,7 +6646,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" @@ -6683,7 +6658,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, "dependencies": { "p-locate": "^4.1.0" }, @@ -6695,7 +6669,6 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, "dependencies": { "p-try": "^2.0.0" }, @@ -6710,7 +6683,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, "dependencies": { "p-limit": "^2.2.0" }, @@ -6722,7 +6694,6 @@ "version": "0.8.1", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "dev": true, "engines": { "node": ">=8" } @@ -6730,14 +6701,12 @@ "node_modules/read-pkg/node_modules/hosted-git-info": { "version": "2.8.9", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "dev": true + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==" }, "node_modules/read-pkg/node_modules/normalize-package-data": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, "dependencies": { "hosted-git-info": "^2.1.4", "resolve": "^1.10.0", @@ -6749,7 +6718,6 @@ "version": "5.7.2", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true, "bin": { "semver": "bin/semver" } @@ -6758,7 +6726,6 @@ "version": "0.6.0", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", - "dev": true, "engines": { "node": ">=8" } @@ -6895,7 +6862,6 @@ "version": "1.22.2", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", "integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==", - "dev": true, "dependencies": { "is-core-module": "^2.11.0", "path-parse": "^1.0.7", @@ -7358,7 +7324,6 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", - "dev": true, "dependencies": { "spdx-expression-parse": "^3.0.0", "spdx-license-ids": "^3.0.0" @@ -7367,14 +7332,12 @@ "node_modules/spdx-exceptions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", - "dev": true + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==" }, "node_modules/spdx-expression-parse": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", - "dev": true, "dependencies": { "spdx-exceptions": "^2.1.0", "spdx-license-ids": "^3.0.0" @@ -7383,8 +7346,7 @@ "node_modules/spdx-license-ids": { "version": "3.0.13", "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.13.tgz", - "integrity": "sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w==", - "dev": true + "integrity": "sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w==" }, "node_modules/sprintf-js": { "version": "1.0.3", @@ -7603,7 +7565,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, "engines": { "node": ">= 0.4" }, @@ -8073,7 +8034,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dev": true, "dependencies": { "spdx-correct": "^3.0.0", "spdx-expression-parse": "^3.0.0" diff --git a/package.json b/package.json index 943e1aba0..04d5f4def 100644 --- a/package.json +++ b/package.json @@ -236,6 +236,7 @@ "mkdirp": "^2.1.5", "mz": "^2.7.0", "progress": "^2.0.3", + "read-pkg-up": "^7.0.1", "resolve-pkg": "^2.0.0", "semver": "7.5.3", "string-argv": "^0.3.1", @@ -310,7 +311,7 @@ "ansi-regex": "^5.0.1" }, "scripts": { - "build-local": "genversion --es6 src/version.ts && tsc --build tsconfig.node.json && shx cp src/importer.js lib/ && shx cp src/wrapper.mjs lib/ && shx cp src/api/wrapper.mjs lib/api/", + "build-local": "genversion --es6 src/version.ts && tsc --build tsconfig.node.json && shx cp src/wrapper.mjs lib/ && shx cp src/api/wrapper.mjs lib/api/", "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", diff --git a/src/api/support.ts b/src/api/support.ts index f1e0d91b8..91f007d43 100644 --- a/src/api/support.ts +++ b/src/api/support.ts @@ -4,9 +4,6 @@ import supportCodeLibraryBuilder from '../support_code_library_builder' import { pathToFileURL } from 'url' import tryRequire from '../try_require' -// eslint-disable-next-line @typescript-eslint/no-var-requires -const { importer } = require('../importer') - export async function getSupportCodeLibrary({ cwd, newId, @@ -30,7 +27,7 @@ export async function getSupportCodeLibrary({ requirePaths.map((path) => tryRequire(path)) for (const path of importPaths) { - await importer(pathToFileURL(path)) + await import(pathToFileURL(path).toString()) } return supportCodeLibraryBuilder.finalize() diff --git a/src/configuration/from_file.ts b/src/configuration/from_file.ts index d95ab51e1..1749e7d61 100644 --- a/src/configuration/from_file.ts +++ b/src/configuration/from_file.ts @@ -2,6 +2,7 @@ import stringArgv from 'string-argv' import fs from 'fs' import path from 'path' import YAML from 'yaml' +import readPkgUp from 'read-pkg-up' import { promisify } from 'util' import { pathToFileURL } from 'url' import { IConfiguration } from './types' @@ -10,16 +11,13 @@ import ArgvParser from './argv_parser' import { checkSchema } from './check_schema' import { ILogger } from '../logger' -// eslint-disable-next-line @typescript-eslint/no-var-requires -const { importer } = require('../importer') - export async function fromFile( logger: ILogger, cwd: string, file: string, profiles: string[] = [] ): Promise> { - const definitions = await loadFile(cwd, file) + const definitions = await loadFile(logger, cwd, file) if (!definitions.default) { logger.debug('No default profile defined in configuration file') definitions.default = {} @@ -43,6 +41,7 @@ export async function fromFile( } async function loadFile( + logger: ILogger, cwd: string, file: string ): Promise> { @@ -61,17 +60,44 @@ async function loadFile( await promisify(fs.readFile)(filePath, { encoding: 'utf-8' }) ) break - default: - try { - // eslint-disable-next-line @typescript-eslint/no-var-requires - definitions = require(filePath) - } catch (error) { - if (error.code === 'ERR_REQUIRE_ESM') { - definitions = await importer(pathToFileURL(filePath)) + case '.cjs': + logger.debug( + `Loading configuration file "${file}" as CommonJS based on extension` + ) + // eslint-disable-next-line @typescript-eslint/no-var-requires + definitions = require(filePath) + break + case '.mjs': + logger.debug( + `Loading configuration file "${file}" as ESM based on extension` + ) + definitions = await import(pathToFileURL(filePath).toString()) + break + case '.js': + { + const parentPackage = await readPackageJson(filePath) + if (!parentPackage) { + logger.debug( + `Loading configuration file "${file}" as CommonJS based on absence of a parent package` + ) + // eslint-disable-next-line @typescript-eslint/no-var-requires + definitions = require(filePath) + } else if (parentPackage.type === 'module') { + logger.debug( + `Loading configuration file "${file}" as ESM based on "${parentPackage.name}" package type` + ) + definitions = await import(pathToFileURL(filePath).toString()) } else { - throw error + logger.debug( + `Loading configuration file "${file}" as CommonJS based on "${parentPackage.name}" package type` + ) + // eslint-disable-next-line @typescript-eslint/no-var-requires + definitions = require(filePath) } } + break + default: + throw new Error(`Unsupported configuration file extension "${extension}"`) } if (typeof definitions !== 'object') { throw new Error(`Configuration file ${filePath} does not export an object`) @@ -79,6 +105,11 @@ async function loadFile( return definitions } +async function readPackageJson(filePath: string) { + const parentPackage = await readPkgUp({ cwd: path.dirname(filePath) }) + return parentPackage?.packageJson +} + function extractConfiguration( logger: ILogger, name: string, diff --git a/src/configuration/from_file_spec.ts b/src/configuration/from_file_spec.ts index 7b75557e8..1d36ef744 100644 --- a/src/configuration/from_file_spec.ts +++ b/src/configuration/from_file_spec.ts @@ -7,32 +7,40 @@ import { fromFile } from './from_file' import { FakeLogger } from '../../test/fake_logger' async function setup( - file: string = 'cucumber.js', - content: string = `module.exports = {default: {paths: ['some/path/*.feature']}, p1: {paths: ['other/path/*.feature']}, p2: 'other/other/path/*.feature --no-strict'}` + file: string = 'cucumber.json', + content: string = JSON.stringify({ + default: { paths: ['some/path/*.feature'] }, + p1: { paths: ['other/path/*.feature'] }, + p2: 'other/other/path/*.feature --no-strict', + }), + packageJson: string = `{}` ) { const logger = new FakeLogger() const cwd = await promisify(tmp.dir)({ unsafeCleanup: true, }) fs.writeFileSync(path.join(cwd, file), content, { encoding: 'utf-8' }) + fs.writeFileSync(path.join(cwd, 'package.json'), packageJson, { + encoding: 'utf-8', + }) return { logger, cwd } } describe('fromFile', () => { it('should return empty config if no default provide and no profiles requested', async () => { const { logger, cwd } = await setup( - 'cucumber.js', - `module.exports = {p1: {paths: ['other/path/*.feature']}}` + 'cucumber.json', + JSON.stringify({ p1: { paths: ['other/path/*.feature'] } }) ) - const result = await fromFile(logger, cwd, 'cucumber.js', []) + const result = await fromFile(logger, cwd, 'cucumber.json', []) expect(result).to.deep.eq({}) }) it('should get default config from file if no profiles requested', async () => { const { logger, cwd } = await setup() - const result = await fromFile(logger, cwd, 'cucumber.js', []) + const result = await fromFile(logger, cwd, 'cucumber.json', []) expect(result).to.deep.eq({ paths: ['some/path/*.feature'] }) }) @@ -40,7 +48,7 @@ describe('fromFile', () => { const { logger, cwd } = await setup() try { - await fromFile(logger, cwd, 'cucumber.js', ['nope']) + await fromFile(logger, cwd, 'cucumber.json', ['nope']) expect.fail('should have thrown') } catch (error) { expect(error.message).to.eq(`Requested profile "nope" doesn't exist`) @@ -50,14 +58,14 @@ describe('fromFile', () => { it('should get single profile config from file', async () => { const { logger, cwd } = await setup() - const result = await fromFile(logger, cwd, 'cucumber.js', ['p1']) + const result = await fromFile(logger, cwd, 'cucumber.json', ['p1']) expect(result).to.deep.eq({ paths: ['other/path/*.feature'] }) }) it('should merge multiple profiles config from file', async () => { const { logger, cwd } = await setup() - const result = await fromFile(logger, cwd, 'cucumber.js', ['p1', 'p2']) + const result = await fromFile(logger, cwd, 'cucumber.json', ['p1', 'p2']) expect(result).to.deep.eq({ paths: ['other/path/*.feature', 'other/other/path/*.feature'], strict: false, @@ -66,11 +74,11 @@ describe('fromFile', () => { it('should throw when an object doesnt conform to the schema', async () => { const { logger, cwd } = await setup( - 'cucumber.js', - `module.exports = {p1: {paths: 4, things: 8, requireModule: 'aardvark'}}` + 'cucumber.json', + JSON.stringify({ p1: { paths: 4, things: 8, requireModule: 'aardvark' } }) ) try { - await fromFile(logger, cwd, 'cucumber.js', ['p1']) + await fromFile(logger, cwd, 'cucumber.json', ['p1']) expect.fail('should have thrown') } catch (error) { expect(error.message).to.eq( @@ -79,8 +87,8 @@ describe('fromFile', () => { } }) - describe('other formats', () => { - it('should work with esm', async () => { + describe('supported formats', () => { + it('should work with .mjs', async () => { const { logger, cwd } = await setup( 'cucumber.mjs', `export default {}; export const p1 = {paths: ['other/path/*.feature']}` @@ -90,7 +98,49 @@ describe('fromFile', () => { expect(result).to.deep.eq({ paths: ['other/path/*.feature'] }) }) - it('should work with json', async () => { + it('should work with .cjs', async () => { + const { logger, cwd } = await setup( + 'cucumber.cjs', + `module.exports = { default: {}, p1: { paths: ['other/path/*.feature'] } }` + ) + + const result = await fromFile(logger, cwd, 'cucumber.cjs', ['p1']) + expect(result).to.deep.eq({ paths: ['other/path/*.feature'] }) + }) + + it('should work with .js when commonjs (undeclared)', async () => { + const { logger, cwd } = await setup( + 'cucumber.js', + `module.exports = { default: {}, p1: { paths: ['other/path/*.feature'] } }` + ) + + const result = await fromFile(logger, cwd, 'cucumber.js', ['p1']) + expect(result).to.deep.eq({ paths: ['other/path/*.feature'] }) + }) + + it('should work with .js when commonjs (explicit)', async () => { + const { logger, cwd } = await setup( + 'cucumber.js', + `module.exports = { default: {}, p1: { paths: ['other/path/*.feature'] } }`, + JSON.stringify({ type: 'commonjs' }) + ) + + const result = await fromFile(logger, cwd, 'cucumber.js', ['p1']) + expect(result).to.deep.eq({ paths: ['other/path/*.feature'] }) + }) + + it('should work with .js when module (explicit)', async () => { + const { logger, cwd } = await setup( + 'cucumber.js', + `export default {}; export const p1 = {paths: ['other/path/*.feature']}`, + JSON.stringify({ type: 'module' }) + ) + + const result = await fromFile(logger, cwd, 'cucumber.js', ['p1']) + expect(result).to.deep.eq({ paths: ['other/path/*.feature'] }) + }) + + it('should work with .json', async () => { const { logger, cwd } = await setup( 'cucumber.json', `{ "default": {}, "p1": { "paths": ["other/path/*.feature"] } }` @@ -100,7 +150,7 @@ describe('fromFile', () => { expect(result).to.deep.eq({ paths: ['other/path/*.feature'] }) }) - it('should work with yaml', async () => { + it('should work with .yaml', async () => { const { logger, cwd } = await setup( 'cucumber.yaml', `default: @@ -114,5 +164,32 @@ p1: const result = await fromFile(logger, cwd, 'cucumber.yaml', ['p1']) expect(result).to.deep.eq({ paths: ['other/path/*.feature'] }) }) + + it('should work with .yml', async () => { + const { logger, cwd } = await setup( + 'cucumber.yml', + `default: + +p1: + paths: + - "other/path/*.feature" +` + ) + + const result = await fromFile(logger, cwd, 'cucumber.yml', ['p1']) + expect(result).to.deep.eq({ paths: ['other/path/*.feature'] }) + }) + + it('should throw for an unsupported format', async () => { + const { logger, cwd } = await setup('cucumber.foo', `{}`) + try { + await fromFile(logger, cwd, 'cucumber.foo', ['p1']) + expect.fail('should have thrown') + } catch (error) { + expect(error.message).to.eq( + 'Unsupported configuration file extension ".foo"' + ) + } + }) }) }) diff --git a/src/formatter/builder.ts b/src/formatter/builder.ts index 82e80b581..abb1dd8ae 100644 --- a/src/formatter/builder.ts +++ b/src/formatter/builder.ts @@ -13,10 +13,8 @@ import { EventEmitter } from 'events' import EventDataCollector from './helpers/event_data_collector' import { Writable as WritableStream } from 'stream' import { SnippetInterface } from './step_definition_snippet_builder/snippet_syntax' -import { fileURLToPath, pathToFileURL } from 'url' +import { pathToFileURL } from 'url' import Formatters from './helpers/formatters' -// eslint-disable-next-line @typescript-eslint/no-var-requires -const { importer } = require('../importer') interface IGetStepDefinitionSnippetBuilderOptions { cwd: string @@ -120,20 +118,7 @@ const FormatterBuilder = { }, async loadFile(urlOrName: URL | string) { - let result - try { - // eslint-disable-next-line @typescript-eslint/no-var-requires - result = require(typeof urlOrName === 'string' - ? urlOrName - : fileURLToPath(urlOrName)) - } catch (error) { - if (error.code === 'ERR_REQUIRE_ESM') { - result = await importer(urlOrName) - } else { - throw error - } - } - return result + return await import(urlOrName.toString()) }, resolveConstructor(ImportedCode: any) { @@ -147,6 +132,11 @@ const FormatterBuilder = { typeof ImportedCode.default === 'function' ) { return ImportedCode.default + } else if ( + typeof ImportedCode.default === 'object' && + typeof ImportedCode.default.default === 'function' + ) { + return ImportedCode.default.default } return null }, diff --git a/src/formatter/builder_spec.ts b/src/formatter/builder_spec.ts index 215575dfc..e5867aed8 100644 --- a/src/formatter/builder_spec.ts +++ b/src/formatter/builder_spec.ts @@ -8,7 +8,6 @@ describe('custom class loading', () => { 'esm.mjs', 'exports_dot_default.cjs', 'module_dot_exports.cjs', - 'typescript.ts', ] varieties.forEach((filename) => { describe(filename, () => { diff --git a/src/formatter/fixtures/typescript.ts b/src/formatter/fixtures/typescript.ts deleted file mode 100644 index 2174f613f..000000000 --- a/src/formatter/fixtures/typescript.ts +++ /dev/null @@ -1 +0,0 @@ -export default class Formatter {} diff --git a/src/importer.js b/src/importer.js deleted file mode 100644 index 5347a38ca..000000000 --- a/src/importer.js +++ /dev/null @@ -1,13 +0,0 @@ -/** - * Provides the async `import()` function to source code that needs it, - * without having it transpiled down to commonjs `require()` by TypeScript. - * See https://github.com/microsoft/TypeScript/issues/43329. - * - * @param {any} descriptor - A URL or path for the module to load - * @return {Promise} Promise that resolves to the loaded module - */ -async function importer(descriptor) { - return await import(descriptor) -} - -module.exports = { importer } diff --git a/src/runtime/parallel/worker.ts b/src/runtime/parallel/worker.ts index 67babfed6..5aa65416f 100644 --- a/src/runtime/parallel/worker.ts +++ b/src/runtime/parallel/worker.ts @@ -16,8 +16,6 @@ import { } from './command_types' import tryRequire from '../../try_require' -// eslint-disable-next-line @typescript-eslint/no-var-requires -const { importer } = require('../../importer') const { uuid } = IdGenerator type IExitFunction = (exitCode: number, error?: Error, message?: string) => void @@ -78,7 +76,7 @@ export default class Worker { requireModules.map((module) => tryRequire(module)) requirePaths.map((module) => tryRequire(module)) for (const path of importPaths) { - await importer(pathToFileURL(path)) + await import(pathToFileURL(path).toString()) } this.supportCodeLibrary = supportCodeLibraryBuilder.finalize(supportCodeIds) diff --git a/test-d/api.ts b/test-d/api.mts similarity index 100% rename from test-d/api.ts rename to test-d/api.mts diff --git a/tsconfig.json b/tsconfig.json index c20aa7b4d..eded03c87 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,7 +2,7 @@ "compilerOptions": { "esModuleInterop": true, "lib": ["es2022"], - "module": "commonjs", + "module": "node16", "noImplicitAny": true, "noImplicitReturns": true, "noImplicitThis": true,