Skip to content

fix: Add extensionAlias for ESM TS to webpack-batteries-included#31994

Merged
AtofStryker merged 4 commits into
cypress-io:developfrom
mdmower-csnw:mdm-extension-alias
Jul 16, 2025
Merged

fix: Add extensionAlias for ESM TS to webpack-batteries-included#31994
AtofStryker merged 4 commits into
cypress-io:developfrom
mdmower-csnw:mdm-extension-alias

Conversation

@mdmower-csnw
Copy link
Copy Markdown
Contributor

@mdmower-csnw mdmower-csnw commented Jul 5, 2025

For TypeScript ESM projects that use module resolution requiring file extensions, .js/.mjs extension must be used for .ts/.mts imports.

  • Take advantage of resolve.extensionAlias to resolve these imports.
  • Update regex pattern to load .mts files with ts-loader.
  • Add new .mts text fixture and ESM import test.

Closes #26827
Closes #28805

@CLAassistant
Copy link
Copy Markdown

CLAassistant commented Jul 5, 2025

CLA assistant check
All committers have signed the CLA.

@cypress-app-bot
Copy link
Copy Markdown
Collaborator

@mdmower-csnw mdmower-csnw changed the title Add extensionAlias for ESM TypeScript to webpack-batteries-included config chore: Add extensionAlias for ESM TS to webpack-batteries-included Jul 5, 2025
@mdmower-csnw mdmower-csnw force-pushed the mdm-extension-alias branch from 6cfaa7f to f5bfd99 Compare July 5, 2025 18:09
@mdmower-csnw mdmower-csnw marked this pull request as ready for review July 5, 2025 18:13
Copy link
Copy Markdown
Collaborator

@AtofStryker AtofStryker left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @mdmower-csnw.

Thank you for opening a PR. Are you able to add some type of test, preferably a system-test that showcases how add resolveAlias helps resolve the extensions of a given file?

@mdmower-csnw
Copy link
Copy Markdown
Contributor Author

Are you able to add some type of test, preferably a system-test that showcases how add resolveAlias helps resolve the extensions of a given file?

@AtofStryker - Test added in 43e5923. I also fixed an oversight on my part to load .mts files with ts-loader.

@AtofStryker AtofStryker self-requested a review July 14, 2025 16:44
Copy link
Copy Markdown
Collaborator

@AtofStryker AtofStryker left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me. Thank you @mdmower-csnw !

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 cypress-io#26827
Fixes cypress-io#28805
…-included

- Update typescript regex to allow .mts files
- Add ESM import tests
@AtofStryker AtofStryker changed the title chore: Add extensionAlias for ESM TS to webpack-batteries-included fix: Add extensionAlias for ESM TS to webpack-batteries-included Jul 15, 2025
@AtofStryker AtofStryker force-pushed the mdm-extension-alias branch from 3f2986a to 45a4fc6 Compare July 15, 2025 21:29
cursor[bot]

This comment was marked as outdated.

@mdmower-csnw
Copy link
Copy Markdown
Contributor Author

I'm not sure whether my input is needed on the cursor bot suggestions; I'll give my 2cents.

  1. webpackOptions.resolve.extensions is missing .mts, preventing direct imports of .mts files despite the updated typescriptExtensionRegex matching them.

Module resolution name possibilities:

  • name (extensionless) - From the handbook: "TypeScript currently never supports omitting a .mjs/.mts or .cjs/.cts file extension, even though some runtimes and bundlers do." Supposing this project intends to stick close to official TypeScript usage, we don't need to consider .mts files for extensionless imports.
  • name.mjs: This is handled by this pull request via extension aliasing (no need to add it to webpackOptions.resolve.extensions).
  • name.mts: This is interesting. .mts import statements are allowed when option allowImportingTsExtensions is enabled. Its use case is limited, though: "This flag is only allowed when --noEmit or --emitDeclarationOnly is enabled, since these import paths would not be resolvable at runtime in JavaScript output files." The use case is niche, but it could be argued that Cypress tests are exactly the kind of place where this kind of import would be desired and legitimate.

In summary, adding .mts to webpackOptions.resolve.extensions would probably be safe, but has niche use.

  1. webpackOptions.resolve.extensionAlias is overwritten instead of merged, potentially breaking user configurations. This should be merged with existing aliases, similar to how resolve.extensions is handled.

This assignment was chosen after I verified getDefaultWebpackOptions() does not set a value for resolve.extensionAlias. It certainly could be rewritten to protect against the future if that's wanted:

webpackOptions.resolve.extensionAlias = {
  ...webpackOptions.resolve.extensionAlias,
  '.js': ['.ts', '.js'],
  '.mjs': ['.mts', '.mjs'],
}

@AtofStryker
Copy link
Copy Markdown
Collaborator

I'm not sure whether my input is needed on the cursor bot suggestions; I'll give my 2cents.

  1. webpackOptions.resolve.extensions is missing .mts, preventing direct imports of .mts files despite the updated typescriptExtensionRegex matching them.

Module resolution name possibilities:

  • name (extensionless) - From the handbook: "TypeScript currently never supports omitting a .mjs/.mts or .cjs/.cts file extension, even though some runtimes and bundlers do." Supposing this project intends to stick close to official TypeScript usage, we don't need to consider .mts files for extensionless imports.
  • name.mjs: This is handled by this pull request via extension aliasing (no need to add it to webpackOptions.resolve.extensions).
  • name.mts: This is interesting. .mts import statements are allowed when option allowImportingTsExtensions is enabled. Its use case is limited, though: "This flag is only allowed when --noEmit or --emitDeclarationOnly is enabled, since these import paths would not be resolvable at runtime in JavaScript output files." The use case is niche, but it could be argued that Cypress tests are exactly the kind of place where this kind of import would be desired and legitimate.

In summary, adding .mts to webpackOptions.resolve.extensions would probably be safe, but has niche use.

  1. webpackOptions.resolve.extensionAlias is overwritten instead of merged, potentially breaking user configurations. This should be merged with existing aliases, similar to how resolve.extensions is handled.

This assignment was chosen after I verified getDefaultWebpackOptions() does not set a value for resolve.extensionAlias. It certainly could be rewritten to protect against the future if that's wanted:

webpackOptions.resolve.extensionAlias = {
  ...webpackOptions.resolve.extensionAlias,
  '.js': ['.ts', '.js'],
  '.mjs': ['.mts', '.mjs'],
}

@mdmower-csnw I think the only thing that I really took away from the suggestions was that we want to keep extensionAlias if a user sets it. I am of the opinion we don't want to merge the defaults since the user is alreadt overriding them and providing them would be straight forward, so I just set the setting of extensionAlias to

  webpackOptions.resolve.extensionAlias = webpackOptions.resolve.extensionAlias || { '.js': ['.ts', '.js'], '.mjs': ['.mts', '.mjs'] }

This should be sufficient

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: TypeScript Module Resolution Fails

The commit introduces two issues affecting TypeScript module resolution:

  1. resolve.extensionAlias merging: The webpackOptions.resolve.extensionAlias assignment uses the logical OR (||) operator. This prevents the new TypeScript aliases (.js -> .ts, .mjs -> .mts) from merging with existing user configurations, breaking TypeScript ESM import resolution when users have their own extensionAlias settings.
  2. resolve.extensions incompleteness: Although the typescriptExtensionRegex and ts-loader are updated to process .mts and .mtsx files, these extensions are not added to webpackOptions.resolve.extensions. This prevents webpack from resolving direct or extensionless imports of .mts and .mtsx files.

npm/webpack-batteries-included-preprocessor/index.js#L64-L66

webpackOptions.resolve.extensions = webpackOptions.resolve.extensions.concat(['.ts', '.tsx'])
webpackOptions.resolve.extensionAlias = webpackOptions.resolve.extensionAlias || { '.js': ['.ts', '.js'], '.mjs': ['.mts', '.mjs'] }

Fix in CursorFix in Web


Was this report helpful? Give feedback by reacting with 👍 or 👎

@AtofStryker AtofStryker merged commit cabdae7 into cypress-io:develop Jul 16, 2025
67 of 69 checks passed
@cypress-bot
Copy link
Copy Markdown
Contributor

cypress-bot Bot commented Jul 25, 2025

Released in 14.5.3.

This comment thread has been locked. If you are still experiencing this issue after upgrading to
Cypress v14.5.3, please open a new issue.

@cypress-bot cypress-bot Bot locked as resolved and limited conversation to collaborators Jul 25, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Error using ESM imports with Typescript Error when using fully-qualified ESM imports with TypeScript

6 participants