Visitar URL original
fix: resolve tsconfig extends like tsc by abrahamguo · Pull Request #709 · webpack/enhanced-resolve · GitHub
Skip to content

fix: resolve tsconfig extends like tsc - #709

Merged
alexander-akait merged 1 commit into
webpack:mainfrom
abrahamguo:fix/tsconfig-extends-like-tsc
Nov 12, 2026
Merged

alexander-akait merged 1 commit into
webpack:mainfrom
abrahamguo:fix/tsconfig-extends-like-tsc

Conversation

@abrahamguo

@abrahamguo abrahamguo commented Nov 9, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Fixes #706.

TsconfigPathsPlugin builds extends paths by hand. It appends .json to any value without .json in it, and maps package names to node_modules paths itself. tsc treats ./, ../ and absolute values as files (.json optional) and resolves everything else like a require. So four forms that tsc accepts fail the whole tsconfig, including the project's own paths.

This keeps the file branch and resolves everything else with a child resolver set up like tsc's config lookup: exports with the require/types/node conditions, the tsconfig field, then tsconfig.json. That replaces the package special cases added in #483, #575 and #631. Like tsc, backslashes are normalized first. When the lookup fails, a bare file name such as "tsconfig.base.json" still loads from the config's directory, as it always has here, though tsc rejects it.

Resolving @/foo with the #706 repro, where tsc gives src/foo.ts for all four:

extends Before After
"cfgpkg/strict" via exports ENOENT … node_modules/cfgpkg/strict.json src/foo.ts
"cfgpkg" with a tsconfig field ENOENT … node_modules/cfgpkg/tsconfig.json src/foo.ts
"./configs/base.tsconfig" ENOENT … configs/base.tsconfig.json src/foo.ts
".." ENOENT … dotdot/pkg/...json src/foo.ts

What kind of change does this PR introduce?

fix

Did you add tests for your changes?

Yes: five cases in test/tsconfig-paths.test.js, with fixtures in test/fixtures/tsconfig-paths/extends-resolution/. The four #706 forms fail on main. The .. case also checks that a sibling <dir>.json doesn't win. The bare file name case passes on main and guards the kept fallback.

Does this PR introduce a breaking change?

No.

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

n/a

Use of AI

Yes. I used Claude Code to write the change and the tests. I reviewed every line, checked each case against tsc's getExtendsConfigPath, and ran npm run lint and the full test suite locally.

Summary by CodeRabbit

  • Bug Fixes
    • TypeScript configuration inheritance now follows TypeScript-style path resolution, including package exports, package configuration fields, extensionless config files, and parent-directory references.
    • Unsupported bare filenames are no longer treated as valid configuration paths.

@changeset-bot

changeset-bot Bot commented Nov 9, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 9cb268a

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

@coderabbitai

coderabbitai Bot commented Nov 9, 2026

Copy link
Copy Markdown

Explain Change →

No actionable comments were generated in the recent review.

Walkthrough

The plugin now normalizes and classifies tsconfig extends values. It loads absolute and relative paths from the extending config, and resolves other values with a cached resolver. New tests cover five extends-resolution cases.

Severity of issue fixed: Medium

Merge Risk: Minimal · up to 9cb26

This change makes tsconfig extends resolution match tsc for package exports, a package tsconfig field, relative files without a .json suffix, and parent directories. Regression tests cover each case. No actionable merge-blocking risk remains.

Pre-merge checks: Passed 5

Check name Status Explanation
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title clearly summarizes the main change: resolving tsconfig extends values like TypeScript.
Linked Issues check Passed Issue [#706] requires package exports subpaths, packages with a tsconfig field, relative config files without .json, and .. directory resolution. The change adds a resolver-based package looku…
Out of Scope Changes check Passed The implementation, regression fixtures and tests, changeset, and removal of the obsolete _findExtendsInNodeModules declaration all support the extends resolution change in issue [#706]. No unrela…
Docstring Coverage Passed No eligible functions found in the changed files. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentat…
Full details: Linked Issues check

Explanation

Issue [#706] requires package exports subpaths, packages with a tsconfig field, relative config files without .json, and .. directory resolution. The change adds a resolver-based package lookup with exports and tsconfig handling, preserves file resolution, and adds regression tests for all four forms. The changeset also documents the fix.

Full details: Out of Scope Changes check

Explanation

The implementation, regression fixtures and tests, changeset, and removal of the obsolete _findExtendsInNodeModules declaration all support the extends resolution change in issue [#706]. No unrelated changes are identified.

Full details: Docstring Coverage

Explanation

No eligible functions found in the changed files. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentation-only edits and deletions within surviving functions are included; unchanged diff context and fully deleted functions are excluded. Analyzed 0 functions across 7 files. (10 skipped: 10 unsupported.)

Finishing Touches

Generate unit tests (beta)

  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

Recent review info

Run configuration

  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 2224806c-9127-4b29-83e3-7c7008c096ee
Commits

Reviewing files that changed from the base of the PR and between ce269b9 and 9cb268a.

Files ignored due to path filters: 4

  • test/fixtures/tsconfig-paths/extends-resolution/pkg-exports/node_modules/base-config/configs/strict.json is excluded by !**/node_modules/**
  • test/fixtures/tsconfig-paths/extends-resolution/pkg-exports/node_modules/base-config/package.json is excluded by !**/node_modules/**
  • test/fixtures/tsconfig-paths/extends-resolution/pkg-tsconfig-field/node_modules/base-config/configs/base.json is excluded by !**/node_modules/**
  • test/fixtures/tsconfig-paths/extends-resolution/pkg-tsconfig-field/node_modules/base-config/package.json is excluded by !**/node_modules/**
Files selected for processing: 18

  • .changeset/tsconfig-extends-like-tsc.md
  • lib/TsconfigPathsPlugin.js
  • test/fixtures/tsconfig-paths/extends-resolution/bare-file-name/src/util.ts
  • test/fixtures/tsconfig-paths/extends-resolution/bare-file-name/tsconfig.base.json
  • test/fixtures/tsconfig-paths/extends-resolution/bare-file-name/tsconfig.json
  • test/fixtures/tsconfig-paths/extends-resolution/non-json-name/configs/base.tsconfig
  • test/fixtures/tsconfig-paths/extends-resolution/non-json-name/src/util.ts
  • test/fixtures/tsconfig-paths/extends-resolution/non-json-name/tsconfig.json
  • test/fixtures/tsconfig-paths/extends-resolution/parent-dir.json
  • test/fixtures/tsconfig-paths/extends-resolution/parent-dir/app/src/util.ts
  • test/fixtures/tsconfig-paths/extends-resolution/parent-dir/app/tsconfig.json
  • test/fixtures/tsconfig-paths/extends-resolution/parent-dir/tsconfig.json
  • test/fixtures/tsconfig-paths/extends-resolution/pkg-exports/src/util.ts
  • test/fixtures/tsconfig-paths/extends-resolution/pkg-exports/tsconfig.json
  • test/fixtures/tsconfig-paths/extends-resolution/pkg-tsconfig-field/src/util.ts
  • test/fixtures/tsconfig-paths/extends-resolution/pkg-tsconfig-field/tsconfig.json
  • test/tsconfig-paths.test.js
  • types/TsconfigPathsPlugin.d.ts
Files with no reviewable changes: 1

  • types/TsconfigPathsPlugin.d.ts

Included review availability: This review used your included allowance. Your plan provides up to 4 included reviews per hour; 3 remain after this review.

@codecov

codecov Bot commented Nov 12, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 98.99%. Comparing base (ce269b9) to head (9cb268a).

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #709      +/-   ##
==========================================
- Coverage   98.99%   98.99%   -0.01%     
==========================================
  Files          52       52              
  Lines       10933    10905      -28     
==========================================
- Hits        10823    10795      -28     
  Misses        110      110              
Flag Coverage Δ
integration 98.99% <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

codspeed Bot commented Nov 12, 2026

Copy link
Copy Markdown

Merging this PR will improve performance by 62.3%

2 warnings, 1 note

⚠️ 4 benchmarks are dominated by system calls

System calls cannot be simulated consistently and are left out of the measure. The reported value understates the real cost. Switch to the Walltime instrument to measure them.

Learn more about system calls

See affected benchmarks

⚠️ 2 benchmarks with a significant change ran on different hardware

Their base and head results were measured on machines with a different CPU, core count or memory, so the changes might not come from the code. Run base and head on the same runner type.

Learn more about Macro Runners

See affected benchmarks

ℹ️ 1 benchmark has no flamegraph

The flamegraph wasn't generated: most of the time was spent in runtime internals, which would leave nothing useful to analyze.

See affected benchmarks

⚡ 2 improved benchmarks
✅ 140 untouched benchmarks

Performance Changes

Mode Benchmark BASE HEAD Efficiency
⚡ Memory array-alias: @ -> [preferred, fallback] (warm) 3.6 KB 2.2 KB +64.75%
⚡ Memory node-compare: enhanced-resolve sync x 1000 (fs cache) 894.9 KB 559.7 KB +59.89%

Tip

Curious why performance improved? Comment @codspeedbot explain why performance improved on this PR, or directly use the CodSpeed MCP with your agent.


Comparing abrahamguo:fix/tsconfig-extends-like-tsc (9cb268a) with main (ce269b9)

Open in CodSpeed

@alexander-akait
alexander-akait merged commit 165a1a4 into webpack:main Nov 12, 2026
40 checks passed
alexander-akait added a commit that referenced this pull request Nov 12, 2026
#711)

**Summary**

Fixes #705.

#709 fixed `extends` lookup (#706), but configs loaded through `extends`
still resolve `paths` and `baseUrl` differently from tsc. This PR builds
on #710 by @bardrop. It applies its two commits onto current `main`,
since #710 now conflicts with #709 in `test/tsconfig-paths.test.js`. It
also adds two fixes:

- **#710 (commits `f70894d`, `5520785`)**: `paths` without `baseUrl`
resolve relative to the config that declares them (`pathsBasePath`).
`${configDir}` in an inherited `baseUrl` becomes the root config
directory. An inherited empty `baseUrl` becomes the directory of the
config that declares it.
- **Empty root `baseUrl` (`99da949`)**: `"baseUrl": ""` was used for
`paths` but never added to `modules`, so bare requests did not resolve
from it. This bug also exists on `main`.

  ```diff
- if (baseUrl && absoluteBaseUrl && !modules.includes(absoluteBaseUrl))
{
  + if (baseUrl !== undefined && !modules.includes(absoluteBaseUrl)) {
  ```

- **Generated types (`9ee297e`)**: regenerates
`types/TsconfigPathsPlugin.d.ts` for the new `pathsBasePath` JSDoc
property. Without it, CI's `build:types` check fails.

I compared enhanced-resolve with `ts.resolveModuleName` (TypeScript
6.0.3, `moduleResolution: bundler`) on 34 setups. They cover the #705
and #706 repros, multi-level and array `extends`, packages (`exports`,
the `tsconfig` field, scoped), `${configDir}`, empty `baseUrl`,
backslashes, and project references.

| Code                   | Setups that differ from tsc |
| ---------------------- | --------------------------- |
| `main`                 | 16 / 34                     |
| `main` + #710          | 1 / 34 (empty root `baseUrl`) |
| this PR                | 0 / 34                      |

The #705 repro, before and after:

```text
before: one @/foo -> Can't resolve '@/foo'   two @/foo -> Can't resolve '@/foo'   three foo -> Can't resolve 'foo'
after:  one @/foo -> src/foo.ts              two @/foo -> src/foo.ts              three foo -> src/foo.ts
```

**What kind of change does this PR introduce?**

fix

**Did you add tests for your changes?**

Yes. #710 adds 6 tests with fixtures in
`test/fixtures/tsconfig-paths/extends-paths-base/`. I added one test
that resolves `src/foo` through an empty root `baseUrl`; it fails
without `99da949` and passes with it. `npm run lint` is clean and `npm
run test:only` passes (1664 tests).

**Does this PR introduce a breaking change?**

No.

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

n/a. The changeset from #710 is included.

**Use of AI**

Yes. I used Claude Code to apply #710 onto `main`, write a differential
script against tsc, find and fix the remaining empty-`baseUrl` case, add
its test, and regenerate the types. Lint and the full test suite were
run locally.

---
_Generated by [Claude
Code](https://claude.ai/code/session_013FhutR7wqtULZXURwFF5ER)_

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
<!-- coderabbit-ai-generated:v1 -->

## Summary by CodeRabbit

* **Bug Fixes**
* Corrected resolution of inherited TypeScript path aliases when
`baseUrl` is missing or empty.
* Improved handling of `${configDir}` in inherited `baseUrl` settings
and path resolution across extended configurations.
* Prevented one extended configuration from affecting subsequent loads
that share a filesystem cache.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Mradul Singh <msingh@dealmeridian.com>
Co-authored-by: Claude <noreply@anthropic.com>
alexander-akait pushed a commit that referenced this pull request Nov 12, 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.26.2

### Patch Changes

- Export the `UnsafeCache` type of the `unsafeCache` option. (by
[@alexander-akait](https://github.com/alexander-akait) in
[#703](#703))

- Resolve tsconfig `extends` the way tsc does, including package
`exports`. (by [@abrahamguo](https://github.com/abrahamguo) in
[#709](#709))

- Resolve `paths` inherited through `extends` (with no `baseUrl`)
relative to the tsconfig that declares them, substitute `${configDir}`
in an inherited `baseUrl`, treat an empty `baseUrl` as the directory of
its tsconfig, like tsc, and stop extending a tsconfig from changing it
for later loads that share the file system cache. (by
[@alexander-akait](https://github.com/alexander-akait) in
[#711](#711))

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
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.

TsconfigPathsPlugin: extends ignores package exports, the tsconfig field, non-.json names and ".."

2 participants