Skip to content

fix: keep the original request resolvable when extensionAlias lists its own extension - #641

Merged
alexander-akait merged 1 commit into
mainfrom
fix/extension-alias-original-request-fallback
Jul 28, 2026
Merged

fix: keep the original request resolvable when extensionAlias lists its own extension#641
alexander-akait merged 1 commit into
mainfrom
fix/extension-alias-original-request-fallback

Conversation

@alexander-akait

@alexander-akait alexander-akait commented Jul 28, 2026

Copy link
Copy Markdown
Member

Summary

ExtensionAliasPlugin marks every candidate fullySpecified: true and then refuses to fall back, so a request ending in an aliased extension can only ever resolve as an exact file — never as a directory or via package.json#main. With the source extension listed among its own aliases ({ ".js": [".js", ".ts"] } — the documented TypeScript recipe, and what experiments.typescript sets in webpack) that entry is meant to keep the original request valid, but it is tried fully specified too, so any package or directory whose name ends in .js becomes unreachable. This makes the identity entry mean what it says: after the fully specified candidates fail, fall through to normal resolution. A mapping that drops the source extension ({ ".js": [".ts"] }, { ".mjs": ".mts" }) stays strict.

Refs webpack/webpack#21541. That regression is fixed on the webpack side in webpack/webpack#21542 and does not need this; this closes the same failure for projects that genuinely enable TypeScript today, and ahead of TypeScript becoming a default.

What kind of change does this PR introduce?

fix

Did you add tests for your changes?

Yes — three cases in test/extension-alias.test.js (directory named dir3.js, an aliased absolute path to a pkg.js package, and a strict mapping that must keep failing) plus the matching fixtures.

Does this PR introduce a breaking change?

No — it only turns previously failing resolutions into successes; exact-file candidates are still tried first, so nothing that resolves today resolves differently.

If relevant, what needs to be documented once your changes are merged or what have you already documented?

extensionAlias docs should say that listing an extension among its own aliases keeps the original request resolvable in its normal form, while omitting it makes the mapping strict.

Use of AI

AI (Claude Code) was used to trace the failure, draft the fix and tests, and verify them; the diagnosis and the final diff were reviewed by a human before submitting.

…ts own extension

ExtensionAliasPlugin marked every candidate fullySpecified and refused to fall
back, so a request ending in an aliased extension could no longer resolve as a
directory or with appended extensions.
@changeset-bot

changeset-bot Bot commented Jul 28, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 1e33470

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
enhanced-resolve Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@codecov

codecov Bot commented Jul 28, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 98.12%. Comparing base (2bcac6e) to head (1e33470).

Additional details and impacted files
@@           Coverage Diff           @@
##             main     #641   +/-   ##
=======================================
  Coverage   98.12%   98.12%           
=======================================
  Files          49       49           
  Lines        9902     9910    +8     
=======================================
+ Hits         9716     9724    +8     
  Misses        186      186           
Flag Coverage Δ
integration 98.12% <100.00%> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@codspeed-hq

codspeed-hq Bot commented Jul 28, 2026

Copy link
Copy Markdown

Merging this PR will degrade performance by 73.23%

⚠️ Different runtime environments detected

Some benchmarks with significant performance changes were compared across different runtime environments,
which may affect the accuracy of the results.

Open the report in CodSpeed to investigate

⚡ 1 improved benchmark
❌ 2 regressed benchmarks
✅ 139 untouched benchmarks

Warning

Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Mode Benchmark BASE HEAD Efficiency
Memory extensions-many: 6-extension list (warm) 1.9 KB 96.6 KB -98.05%
Memory self-reference: import own package name (warm) 2.1 KB 2.5 KB -15.24%
Memory node-compare: node require.resolve x 1000 24.7 KB 21.2 KB +16.26%

Tip

Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.


Comparing fix/extension-alias-original-request-fallback (1e33470) with main (2bcac6e)

Open in CodSpeed

Copy link
Copy Markdown
Member Author

Closing — this was the wrong layer.

ExtensionAliasPlugin's strict no-fallback behavior is not a regression: it is byte-identical in 5.12.0 (when extensionAlias was introduced), 5.16.0 and 5.24.3, and it is asserted on purpose by "should not allow to fallback to the original extension or add extensions".

The actual cause of webpack/webpack#21541 is on the webpack side: experiments.typescript: "auto" checks only that Node.js >= 22.6 can strip types and that no TS loader is registered — never whether the project contains any TypeScript — so every project on a recent Node.js started getting TypeScript resolution semantics, including this extensionAlias. Fixed in webpack/webpack#21542, which needs no change here.


Generated by Claude Code

Copy link
Copy Markdown
Member Author

Reopened with a corrected rationale (title and description updated); my earlier comment was right that this is not the cause of webpack/webpack#21541, but wrong to conclude nothing needed fixing here.

The webpack-side fix (webpack/webpack#21542) stops experiments.typescript: "auto" from applying this extensionAlias to projects that never asked for TypeScript. It does not help when TypeScript is genuinely enabled — verified against that fix, resolve.alias pointing at a package directory named tippy.js still fails under experiments.typescript: true and under futureDefaults (webpack 6's defaults). Isolating the cause with webpack's own generated resolve options: dropping tsconfig still fails, dropping extensionAlias resolves. So this is the piece that keeps a package or directory whose name ends in .js unreachable, today for explicit opt-in and by default once TypeScript stops being opt-in.


Generated by Claude Code

@alexander-akait
alexander-akait merged commit 5e2e296 into main Jul 28, 2026
70 of 72 checks passed
@alexander-akait
alexander-akait deleted the fix/extension-alias-original-request-fallback branch July 28, 2026 15:33
alexander-akait added a commit to webpack/webpack that referenced this pull request Jul 28, 2026
The `.js` -> `.ts` extensionAlias that `experiments.typescript` installs made
`ExtensionAliasPlugin` resolve such a request as an exact file only, so a
package directory named `pkg.js` became unreachable. Fixed in enhanced-resolve
(webpack/enhanced-resolve#641); take the release and add a regression test.
alexander-akait pushed a commit that referenced this pull request Jul 28, 2026
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and the packages will
be published to npm automatically. If you're not ready to do a release
yet, that's fine, whenever you add more changesets to main, this PR will
be updated.


# Releases
## enhanced-resolve@5.24.4

### Patch Changes

- Keep the original request resolvable when `extensionAlias` lists its
own extension. (by
[@alexander-akait](https://github.com/alexander-akait) in
[#641](#641))

- Fix string `restrictions` boundary checks: a restriction ending with a
separator no longer rejects everything inside it, restrictions are
normalized before they are compared, and a Windows path now matches the
way `path.win32` does, treating `/` and `\` as interchangeable and
comparing case-insensitively, while `\` stays a filename character in a
posix path. The same comparison backs `tsconfig` path matching. (by
[@alexander-akait](https://github.com/alexander-akait) in
[#643](#643))

- Treat a UNC path (`\\server\share\…`) as a Windows path, so it
normalizes, joins and walks up with `path.win32` semantics instead of
being taken for a bare module request. (by
[@alexander-akait](https://github.com/alexander-akait) in
[#644](#644))

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
alexander-akait added a commit to webpack/webpack that referenced this pull request Jul 28, 2026
The `.js` -> `.ts` extensionAlias that `experiments.typescript` installs made
`ExtensionAliasPlugin` resolve such a request as an exact file only, so a
package directory named `pkg.js` became unreachable. Fixed in enhanced-resolve
(webpack/enhanced-resolve#641); take the release and add a regression test.
alexander-akait added a commit to webpack/webpack that referenced this pull request Jul 28, 2026
…21542)

* fix: resolve aliases pointing at a package directory ending in .js

The `.js` -> `.ts` extensionAlias that `experiments.typescript` installs made
`ExtensionAliasPlugin` resolve such a request as an exact file only, so a
package directory named `pkg.js` became unreachable. Fixed in enhanced-resolve
(webpack/enhanced-resolve#641); take the release and add a regression test.

* test: pin the extensionAlias in the regression case

Relying on the `experiments.typescript` "auto" default made the case a no-op
below Node.js 22.6, where the alias is never installed.

* build: lock enhanced-resolve 5.24.4
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant