test(tsconfig): port lookup scenarios from typescript-go#1155
Conversation
Adds src/tests/tsconfig_lookup.rs with 26 scenarios (25 passing, 1
ignored) plus matching fixtures, covering every place the resolver
needs to find or apply a tsconfig:
- Error paths: circular extends, self-extends, self-reference in
`references`, empty `extends` string.
- Extends path resolution: node_modules subpath (`pkg/sub/...json`),
`@scoped/pkg`, package's `tsconfig.json`, explicit `.json`,
folder-style (`./base` -> `./base/tsconfig.json`).
- Extends array semantics: diamond inheritance, and `last-wins`
ignored as it uncovers a divergence from typescript-go
(`applyExtendedConfig` + `mergeCompilerOptions` apply later-wins;
oxc-resolver currently applies first-wins).
- `paths`: empty array, exact-vs-wildcard, longest-prefix,
multi-substitution first-existing wins, child overrides extended,
inherited paths_base anchors at the parent that declared paths,
no-baseUrl anchors at config dir, explicit-extension target,
prefix-`*`-suffix wildcard, single `*` pattern.
- `${configDir}` substitution in `rootDirs`.
- Edge cases: empty `extends: []`, extending a config without
`compilerOptions`, `baseUrl`-only resolution.
- Project references: two refs each resolving `@shared/*` through
their own paths configs.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #1155 +/- ##
==========================================
+ Coverage 93.17% 93.34% +0.16%
==========================================
Files 22 22
Lines 4177 4177
==========================================
+ Hits 3892 3899 +7
+ Misses 285 278 -7 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
Merging this PR will improve performance by 3.54%
|
| Benchmark | BASE |
HEAD |
Efficiency | |
|---|---|---|---|---|
| ⚡ | resolver_memory[multi-thread] |
395.9 µs | 383.3 µs | +3.3% |
| ⚡ | resolver_real[multi-thread] |
401.4 µs | 386.8 µs | +3.78% |
Tip
Curious why this is faster? Comment @codspeedbot explain why this is faster on this PR, or directly use the CodSpeed MCP with your agent.
Comparing test-tsconfig-lookup-scenarios (e92a80a) with main (120a369)
Footnotes
-
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. ↩
## Summary Per TypeScript spec, when `extends` is an array, later configurations override earlier ones. typescript-go implements this by accumulating into a result that each base overwrites in turn (`applyExtendedConfig` → `mergeCompilerOptions`). oxc-resolver's `extend_tsconfig` only fills `None` fields, so iterating forward made the first base set a value, blocking later bases from overriding it — **first-wins** instead of later-wins. **Fix:** iterate `extended_tsconfig_paths` in reverse in both extend sites (`load_tsconfig` and the project-references `extend_tsconfig`). The last base now sets fields first; earlier bases see them set and skip; the child's own explicit fields (set before any extend) still win because they're populated before either loop runs. Removes the `#[ignore]` on `tsconfig_lookup::extends_array_last_wins` (added in #1155) which now passes. The existing `extends-multiple` test still passes because it resolves via `baseUrl` fall-through, not via the order of inherited `paths`. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
## 🤖 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>
Summary
Adds
src/tests/tsconfig_lookup.rs(26 tests; 25 passing, 1 ignored) plus matching fixtures underfixtures/tsconfig/cases/, covering every place the resolver needs to find or apply a tsconfig.references, emptyextendsstring.node_modulessubpath (pkg/sub/...json),@scoped/pkg, packagetsconfig.json, explicit.jsonextension, folder-style (./base→./base/tsconfig.json).extends_array_last_winsthat documents a divergence from typescript-go:applyExtendedConfig+mergeCompilerOptionsapply later-wins, but oxc-resolver'sextend_tsconfigonly fillsNonefields, so the first base wins.pathssemantics — empty array, exact vs wildcard, longest-prefix, multi-substitution first-existing wins, child overrides extended, inheritedpaths_baseanchors at the parent that declaredpaths, no-baseUrlanchors at config dir, explicit-extension target, prefix-*-suffix wildcard, single*pattern.${configDir}substitution — inrootDirs.extends: [], extending a config withoutcompilerOptions,baseUrl-only resolution.@shared/*through their own paths configs.🤖 Generated with Claude Code