From cabdae708d43464f4f7e86e6f400823dcc95abe6 Mon Sep 17 00:00:00 2001 From: Matt Mower <135273348+mdmower-csnw@users.noreply.github.com> Date: Wed, 16 Jul 2025 09:25:19 -0700 Subject: [PATCH] fix: Add extensionAlias for ESM TS to webpack-batteries-included (#31994) * chore: Add extensionAlias for ESM TS to webpack-batteries-included For TypeScript ESM projects that use module resolution requiring file extensions, `.js` extension must be used for `.ts` imports. Take advantage of `resolve.extensionAlias` to resolve these imports. Fixes #26827 Fixes #28805 * (Revisions) chore: Add extensionAlias for ESM TS to webpack-batteries-included - Update typescript regex to allow .mts files - Add ESM import tests * chore: add changelog entry * make sure extensionAlias is backwards compatible --------- Co-authored-by: Bill Glesias --- .circleci/cache-version.txt | 2 +- cli/CHANGELOG.md | 1 + npm/webpack-batteries-included-preprocessor/index.js | 7 ++++--- .../test/e2e/features.spec.js | 4 ++++ .../test/fixtures/file-types/mts-file.mts | 8 ++++++++ .../test/fixtures/typescript_esm_imports_spec.js | 7 +++++++ 6 files changed, 25 insertions(+), 4 deletions(-) create mode 100644 npm/webpack-batteries-included-preprocessor/test/fixtures/file-types/mts-file.mts create mode 100644 npm/webpack-batteries-included-preprocessor/test/fixtures/typescript_esm_imports_spec.js diff --git a/.circleci/cache-version.txt b/.circleci/cache-version.txt index 1275f06c8a..3806857b40 100644 --- a/.circleci/cache-version.txt +++ b/.circleci/cache-version.txt @@ -1,3 +1,3 @@ # Bump this version to force CI to re-create the cache from scratch. -5-14-2025 +7-15-2025 diff --git a/cli/CHANGELOG.md b/cli/CHANGELOG.md index 85f4045f64..3aad68fe72 100644 --- a/cli/CHANGELOG.md +++ b/cli/CHANGELOG.md @@ -6,6 +6,7 @@ _Released 7/30/2025 (PENDING)_ **Bugfixes:** - Fixed missing support for setting an absolute path for `component.indexHtmlFile` in `@cypress/webpack-dev-server`. Fixes [#31819](https://github.com/cypress-io/cypress/issues/31819). +- Fixed an issue where TypeScript ESM projects using `.js` and `.mjs` extensions where not resolving correctly within `@cypress/webpack-batteries-included-preprocessor`. Addressed in [#31994](https://github.com/cypress-io/cypress/pull/31994). ## 14.5.2 diff --git a/npm/webpack-batteries-included-preprocessor/index.js b/npm/webpack-batteries-included-preprocessor/index.js index 46ffecef9f..545a2ce104 100644 --- a/npm/webpack-batteries-included-preprocessor/index.js +++ b/npm/webpack-batteries-included-preprocessor/index.js @@ -7,6 +7,8 @@ const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPl const debug = Debug('cypress:webpack-batteries-included-preprocessor') const WBADebugNamespace = 'cypress-verbose:webpack-batteries-included-preprocessor:bundle-analyzer' +const typescriptExtensionRegex = /\.m?tsx?$/ + const hasTsLoader = (rules) => { return rules.some((rule) => { if (!rule.use || !Array.isArray(rule.use)) return false @@ -45,7 +47,7 @@ const addTypeScriptConfig = (file, options) => { configFile ? debug(`found user tsconfig.json at ${configFile?.path} with compilerOptions: ${JSON.stringify(configFile?.config?.compilerOptions)}`) : debug('no user tsconfig.json found') webpackOptions.module.rules.push({ - test: /\.tsx?$/, + test: typescriptExtensionRegex, exclude: [/node_modules/], use: [ { @@ -61,6 +63,7 @@ const addTypeScriptConfig = (file, options) => { }) webpackOptions.resolve.extensions = webpackOptions.resolve.extensions.concat(['.ts', '.tsx']) + webpackOptions.resolve.extensionAlias = webpackOptions.resolve.extensionAlias || { '.js': ['.ts', '.js'], '.mjs': ['.mts', '.mjs'] } webpackOptions.resolve.plugins = [new TsconfigPathsPlugin({ configFile: configFile?.path, silent: true, @@ -188,8 +191,6 @@ const getDefaultWebpackOptions = () => { } } -const typescriptExtensionRegex = /\.tsx?$/ - const preprocessor = (options = {}) => { return (file) => { if (!options.typescript && typescriptExtensionRegex.test(file.filePath)) { diff --git a/npm/webpack-batteries-included-preprocessor/test/e2e/features.spec.js b/npm/webpack-batteries-included-preprocessor/test/e2e/features.spec.js index ff5e436ed7..de748443a7 100644 --- a/npm/webpack-batteries-included-preprocessor/test/e2e/features.spec.js +++ b/npm/webpack-batteries-included-preprocessor/test/e2e/features.spec.js @@ -90,6 +90,10 @@ describe('webpack-batteries-included-preprocessor features', () => { await runAndEval('typescript_imports_spec.js', { ...options }) }) + it('handles importing ESM .ts and .mts', async () => { + await runAndEval('typescript_esm_imports_spec.js', { ...options }) + }) + it('handles esModuleInterop: false (default)', async () => { await runAndEval('typescript_esmoduleinterop_false_spec.ts', { ...options }) }) diff --git a/npm/webpack-batteries-included-preprocessor/test/fixtures/file-types/mts-file.mts b/npm/webpack-batteries-included-preprocessor/test/fixtures/file-types/mts-file.mts new file mode 100644 index 0000000000..6bf27c2f38 --- /dev/null +++ b/npm/webpack-batteries-included-preprocessor/test/fixtures/file-types/mts-file.mts @@ -0,0 +1,8 @@ +// simple example of typescript types +type SomeMtsType = { + mtsProp: string +} + +export const mtsTypeExample: SomeMtsType = { mtsProp: 'mts value' } + +export const fromMts = 'from mts' diff --git a/npm/webpack-batteries-included-preprocessor/test/fixtures/typescript_esm_imports_spec.js b/npm/webpack-batteries-included-preprocessor/test/fixtures/typescript_esm_imports_spec.js new file mode 100644 index 0000000000..60be348364 --- /dev/null +++ b/npm/webpack-batteries-included-preprocessor/test/fixtures/typescript_esm_imports_spec.js @@ -0,0 +1,7 @@ +import { fromTs, tsTypeExample } from './file-types/ts-file.js' +import { fromMts, mtsTypeExample } from './file-types/mts-file.mjs' + +expect(fromTs).equal('from ts') +expect(tsTypeExample.tsProp).equal('ts value') +expect(fromMts).equal('from mts') +expect(mtsTypeExample.mtsProp).equal('mts value')