Skip to content

fix(tsconfig): walk past a tsconfig that doesn't claim the file#1154

Merged
graphite-app[bot] merged 1 commit into
mainfrom
fix-tsconfig-walk-up-on-miss
May 25, 2026
Merged

fix(tsconfig): walk past a tsconfig that doesn't claim the file#1154
graphite-app[bot] merged 1 commit into
mainfrom
fix-tsconfig-walk-up-on-miss

Conversation

@Boshen

@Boshen Boshen commented May 25, 2026

Copy link
Copy Markdown
Member

Summary

find_tsconfig_auto previously returned the nearest tsconfig.json unconditionally. If that nearest tsconfig had narrow files / include / exclude that didn't cover the file under resolution, its compilerOptions.paths were still applied — leaking the project's aliases into files the project doesn't actually own.

The fix: after finding a tsconfig.json, check claims_ownership_of (does its files/include/exclude cover the file, or does any matching reference?). If no, remember it as a fallback and walk up to the next ancestor. Mirrors TypeScript 6.0 tsserver and typescript-go's findDefaultConfiguredProjectWorker.

Concrete repros (now fixed)

fixtures/tsconfig/cases/project-references-walk-up/
├── files-misses/      pkg-a has files: ["src/foo.ts"] + paths: { "@/*": [...] }
│                      → import "@/foo" from pkg-a/src/bar.ts no longer resolves
│                        via pkg-a's paths (bar.ts isn't in pkg-a's program)
└── exclude-pattern/   pkg-a has include + exclude that removes src/excluded/bar.ts
                       → same fix

Alignment with TypeScript 6.0

Verified all 15 hand-crafted probe scenarios against tsserver from TypeScript 6.0.3 — 15 / 15 match, including:

  • solution-style root with matching / non-matching reference
  • multi-ref priority (first declared wins)
  • nested / cross-directory include
  • files / include + exclude permutations

Known divergences from typescript-go (out of scope here)

  • Transitive references — typescript-go does BFS through the full reference graph; we only check direct references_resolved. Triggers only with deep nested references claiming cross-directory files; not observed in real projects.
  • Fallback in the no-claim-anywhere case — typescript-go returns nil (inferred project); we return the topmost ancestor tsconfig. This matches TypeScript 6.0 tsserver but not typescript-go.

@codecov

codecov Bot commented May 25, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 93.17%. Comparing base (cf9a91c) to head (120a369).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1154      +/-   ##
==========================================
+ Coverage   93.14%   93.17%   +0.03%     
==========================================
  Files          22       22              
  Lines        4158     4177      +19     
==========================================
+ Hits         3873     3892      +19     
  Misses        285      285              

☔ View full report in Codecov by Sentry.
📢 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.

@Boshen Boshen added the merge label May 25, 2026

Boshen commented May 25, 2026

Copy link
Copy Markdown
Member Author

Merge activity

## Summary

`find_tsconfig_auto` previously returned the nearest `tsconfig.json` unconditionally. If that nearest tsconfig had narrow `files` / `include` / `exclude` that didn't cover the file under resolution, its `compilerOptions.paths` were still applied — leaking the project's aliases into files the project doesn't actually own.

The fix: after finding a `tsconfig.json`, check `claims_ownership_of` (does its `files`/`include`/`exclude` cover the file, or does any matching reference?). If no, remember it as a fallback and walk up to the next ancestor. Mirrors TypeScript 6.0 tsserver and typescript-go's `findDefaultConfiguredProjectWorker`.

## Concrete repros (now fixed)

```
fixtures/tsconfig/cases/project-references-walk-up/
├── files-misses/      pkg-a has files: ["src/foo.ts"] + paths: { "@/*": [...] }
│                      → import "@/foo" from pkg-a/src/bar.ts no longer resolves
│                        via pkg-a's paths (bar.ts isn't in pkg-a's program)
└── exclude-pattern/   pkg-a has include + exclude that removes src/excluded/bar.ts
                       → same fix
```

## Alignment with TypeScript 6.0

Verified all 15 hand-crafted probe scenarios against `tsserver` from TypeScript 6.0.3 — **15 / 15 match**, including:

- solution-style root with matching / non-matching reference
- multi-ref priority (first declared wins)
- nested / cross-directory `include`
- `files` / `include` + `exclude` permutations

## Known divergences from typescript-go (out of scope here)

- **Transitive references** — typescript-go does BFS through the full reference graph; we only check direct `references_resolved`. Triggers only with deep nested references claiming cross-directory files; not observed in real projects.
- **Fallback in the no-claim-anywhere case** — typescript-go returns `nil` (inferred project); we return the topmost ancestor tsconfig. This matches TypeScript 6.0 tsserver but not typescript-go.
@graphite-app graphite-app Bot force-pushed the fix-tsconfig-walk-up-on-miss branch from 5335546 to 120a369 Compare May 25, 2026 05:05
@graphite-app graphite-app Bot merged commit 120a369 into main May 25, 2026
18 checks passed
@graphite-app graphite-app Bot removed the merge label May 25, 2026
@graphite-app graphite-app Bot deleted the fix-tsconfig-walk-up-on-miss branch May 25, 2026 05:09
@oxc-guard oxc-guard Bot mentioned this pull request May 25, 2026
@codspeed-hq

codspeed-hq Bot commented May 25, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 13 untouched benchmarks
⏩ 5 skipped benchmarks1


Comparing fix-tsconfig-walk-up-on-miss (120a369) with main (cf9a91c)2

Open in CodSpeed

Footnotes

  1. 5 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

  2. No successful run was found on main (120a369) during the generation of this report, so cf9a91c was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

Boshen added a commit that referenced this pull request May 25, 2026
## 🤖 New release

* `oxc_resolver`: 11.19.1 -> 11.19.2
* `oxc_resolver_napi`: 11.19.1 -> 11.19.2

<details><summary><i><b>Changelog</b></i></summary><p>

## `oxc_resolver`

<blockquote>

##
[11.19.2](v11.19.1...v11.19.2)
- 2026-05-25

### <!-- 1 -->🐛 Bug Fixes

- *(tsconfig)* apply later-wins semantics for extends array
([#1156](#1156)) (by
@Boshen)
- *(tsconfig)* walk past a tsconfig that doesn't claim the file
([#1154](#1154)) (by
@Boshen)
- *(tsconfig)* let project references take priority over their parent
([#1151](#1151)) (by
@Boshen)
- *(tsconfig)* resolve `rootDirs` against the config that declared them
([#1150](#1150)) (by
@Boshen)
- *(tsconfig)* resolve `baseUrl` / `paths` against the canonical
tsconfig path
([#1148](#1148)) (by
@Boshen)
- strip query fragments when calling `find_tsconfig`
([#1147](#1147)) (by
@Boshen)
- avoid panic in resolve_file for parentless paths
([#1053](#1053)) (by
@Boshen)
- *(dts)* strip ./ prefix from package entry when matching typesVersions
([#1051](#1051)) (by
@Boshen)
- *(dts)* expand Declaration to TypeScript|Declaration for package entry
resolution
([#1050](#1050)) (by
@Boshen)
- *(dts)* prefer declaration extensions over JS in exports-resolved
paths ([#1047](#1047))
(by @Boshen)
- avoid wasm/wasi dead-code lint in NodePath
([#1043](#1043)) (by
@Boshen)
- *(napi)* replace panics with error returns to prevent WASM traps
([#1055](#1055)) (by
@Boshen)

### <!-- 2 -->🚜 Refactor

- remove clear_cache test that dynamically creates fixtures (by @Boshen)
- move resolve and misc fixtures into fixtures/integration (by @Boshen)
- replace ignored doctest with link to example (by @Boshen)
- consolidate fixture directories for better test file mapping (by
@Boshen)
- replace `url` crate with `percent-encoding`
([#1065](#1065)) (by
@Boshen)

### <!-- 4 -->⚡ Performance

- *(cache)* pack CachedPathImpl::meta into a CachedMeta byte
([#1144](#1144)) (by
@Boshen)
- *(cache)* store canonical path as Box<Path> instead of PathBuf
([#1143](#1143)) (by
@Boshen)
- *(alias)* fast-reject alias entries by cached first byte
([#1142](#1142)) (by
@Boshen)

### <!-- 6 -->🧪 Testing

- *(tsconfig)* port lookup scenarios from typescript-go
([#1155](#1155)) (by
@Boshen)
- add 28 tests to improve coverage (92% → 93%)
([#1082](#1082)) (by
@Boshen)

### Contributors

* @Boshen
* @renovate[bot]
</blockquote>



</p></details>

---
This PR was generated with
[release-plz](https://github.com/release-plz/release-plz/).

Co-authored-by: oxc-guard[bot] <276638029+oxc-guard[bot]@users.noreply.github.com>
shulaoda added a commit to rolldown/rolldown that referenced this pull request Jun 11, 2026
…#9714)

## What

Corrects the `tsconfig` auto-discovery documentation to match how Rolldown actually resolves `tsconfig.json` since v1.1.0.

## Why

The v1.1.0 release notes covered the in-config reference changes (oxc-resolver #1151 reference priority, #1198 allowJs) but omitted [oxc-resolver #1154](oxc-project/oxc-resolver#1154) ("walk past a tsconfig that doesn't claim the file"), which shipped in oxc_resolver v11.19.2 and is part of the 11.19.1 -> 11.21.0 upgrade.

As a result the docs still described the old behavior:

- "find the nearest tsconfig.json"
- "if no referenced project includes the file, fall back to the root tsconfig"

Both are inaccurate now. `find_tsconfig_auto` walks up ancestor directories, uses the first tsconfig that owns the file (its `files`/`include`/`exclude`, or a matching `references` entry), and falls back to the outermost (topmost) tsconfig when nothing claims it. This matches TypeScript 6.0 tsserver. A solution-style root (`files: []` plus only `references`) never claims files on its own, so resolution continues upward unless one of its references matches.

## Changes

- Rewrite the auto-discovery paragraph: upward, ownership-based search with topmost fallback instead of "nearest".
- Fix the references paragraph: root's own `include` vs. a solution-style root, first matching reference wins, per-project `allowJs`.
- Drop the "(similar to Vite)" qualifier; the semantics follow TypeScript, not Vite.
- Add a `::: warning` callout flagging the v1.1.0 behavior change.
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