refactor(alias): compile fallback aliases once at construction#1264
Conversation
`load_alias_by_options` re-ran `compile_alias` on every failed resolution. Compile the fallback list once into a `ResolverImpl.fallback` field, mirroring how `alias` is handled, and delete the helper. Also simplify the yarn_pnp cache-reuse check in `clone_with_options` to a plain equality and drop its stale `#[allow(clippy::unused_self)]`.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #1264 +/- ##
==========================================
- Coverage 93.85% 93.83% -0.02%
==========================================
Files 21 21
Lines 4313 4301 -12
==========================================
- Hits 4048 4036 -12
Misses 265 265 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Merging this PR will not alter performance
Warning Please fix the performance issues or acknowledge them on CodSpeed. Performance Changes
Tip Investigate this regression by commenting Comparing Footnotes
|
Two cross-file dedups plus stale-allow removal: - **`push_normalized_component`** existed twice — `path.rs` and `cache/cached_path.rs` — identical except the cached-path copy trims the trailing `\0` that uvwasi appends to directory entries on wasm ([nodejs/uvwasi#262](nodejs/uvwasi#262)). This keeps one `pub` wasm-aware version in `path.rs`. Note this means `path.rs` callers (tsconfig path joins) now also get the `\0`-trim on wasm; on native targets the generated code is unchanged, and the helper stays `#[inline]` so the hot `normalize_with` path keeps its codegen. - **`manual_tsconfig()`**: the "manually configured tsconfig, deliberately skipping `Auto` discovery" match block was duplicated verbatim in `lib.rs` (`resolve`) and `dts_resolver.rs` (`dts_resolve_tsconfig_paths`). One helper in `tsconfig_resolver.rs` now owns it, and `find_tsconfig_manual` drops its `pub(crate)`. - **Stale allows**: `clippy::cognitive_complexity` on `extend_tsconfig` and `clippy::too_many_lines` on `dts_try_extensions`' neighbor no longer suppress anything (verified by removing them and running the CI clippy command); `extend_tsconfig` keeps `too_many_lines` (139/100). Part of a second cleanup pass; follows #1264–#1268.
…#1270) The two "resolve from symlinks" benches did, per timed iteration, 10,000 eager `format!("./file{i}")` allocations plus an `assert!(.. .is_ok())` branch — unlike every sibling bench in the file, which iterates precomputed data and discards results with `_ =`. The asserts also re-checked an invariant already validated once before the benchmark group runs. This precomputes the specifiers once (mirroring the `find tsconfig` bench's hoisting pattern) and drops the in-loop asserts, so the benches measure symlink resolution rather than resolution + allocation noise. The validation pass also now constructs one resolver instead of 10,000. **Heads-up:** this will step-change the CodSpeed series for `resolver_memory/resolve from symlinks` and `resolver_real/resolve from symlinks` downward — that's the removed allocation/assert overhead, not a resolver perf change. Part of a second cleanup pass; follows #1264–#1268.
Small leftovers found while sweeping the test suite and the JS side: - `src/tests/resolve.rs`: the "file in module with query and fragment" table row appeared twice, byte-identical — the upstream enhanced-resolve `resolve.test.js` has the case exactly once, so the second row was a copy-paste in the port. The loop just ran the same assertion twice. - `src/tests/alias.rs`: `#[allow(clippy::too_many_lines)]` no longer suppresses anything (verified by removing it and running the CI clippy command). The same allow in `imports_field.rs` is still load-bearing (1191/100) and stays. - `vite.config.ts`: `"napi/browser.js"` was listed twice in `fmt.ignorePatterns`. - `napi/test.mjs`: removed a stale comment about a `.ts` extension next to code that adds `.mjs` and resolves an already-suffixed specifier. - `napi/webcontainer-fallback.js`: removed an inert lint directive (`// eslint-disable-next-line: no-console`) — the project lints with oxlint, the syntax is invalid even for ESLint, and `no-console` isn't enabled. Part of a second cleanup pass; follows #1264–#1268.
## 🤖 New release * `oxc_resolver`: 11.22.0 -> 11.23.0 * `oxc_resolver_napi`: 11.22.0 -> 11.23.0 <details><summary><i><b>Changelog</b></i></summary><p> ## `oxc_resolver` <blockquote> ## [11.23.0](v11.22.0...v11.23.0) - 2026-07-02 ### <!-- 0 -->🚀 Features - *(tsconfig)* expose outDir, declarationDir, resolveJsonModule, checkJs ([#1257](#1257)) (by @Boshen) ### <!-- 1 -->🐛 Bug Fixes - *(exports)* propagate the last error in array target resolution ([#1267](#1267)) (by @Boshen) ### <!-- 2 -->🚜 Refactor - deduplicate cross-file helpers, drop stale lint allows ([#1269](#1269)) (by @Boshen) - *(dts)* deduplicate package entry selection and remove dead code ([#1266](#1266)) (by @Boshen) - *(tsconfig)* skip TsConfig deep clone for non-root configs ([#1265](#1265)) (by @Boshen) - *(alias)* compile fallback aliases once at construction ([#1264](#1264)) (by @Boshen) ### <!-- 4 -->⚡ Performance - *(alias)* scan a packed first-byte array instead of striding over alias entries ([#1260](#1260)) (by @Boshen) ### Contributors * @Boshen </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>
load_alias_by_optionsre-rancompile_alias(allocating and cloning everyAliasValue) on every failed resolution, while the primaryaliaslist is compiled once at construction. This compilesfallbackonce into aResolverImpl.fallbackfield, usesload_aliasdirectly at the fallback call site, and deletesload_alias_by_options(its only caller).compile_aliasis pure over the post-sanitizeoptions, so behavior is identical; with an empty fallback list the new field is an empty box + 32 zero bytes.Also tidies
clone_with_optionswhile touching it: the(a && !b) || (!a && b)yarn_pnp cache check becomes a plain==comparison, and the stale#[allow(clippy::unused_self)]is removed (both cfg branches useself).Split out of #1263.