Skip to content

fix(profiler): keep all same-named components and surface platform variants#368

Draft
latekvo wants to merge 1 commit into
mainfrom
fix/ast-index-name-collisions
Draft

fix(profiler): keep all same-named components and surface platform variants#368
latekvo wants to merge 1 commit into
mainfrom
fix/ast-index-name-collisions

Conversation

@latekvo

@latekvo latekvo commented Jun 18, 2026

Copy link
Copy Markdown
Member

Problem

The AST component index (buildAstIndexWithDiagnostics) is keyed by component name. When two files define a component with the same name — most commonly platform variants, e.g. List in List.tsx and List in List.web.tsx — the index kept whichever file the directory walk happened to reach first (if (!index.has(name))) and threw the rest away.

So a lookup for List could return the wrong platform variant's source, with no signal that other matches even existed. And because it depended on filesystem walk order, which List survived was effectively arbitrary.

Raised in review on #287.

Fix

  • Collect every match per name instead of keeping the first walked.
  • Choose a deterministic primary: base files are preferred over .web / .ios / .android / .native variants (those only override the base when bundling for that platform), then ties break on path → line → column. The result no longer depends on directory-walk order.
  • Surface the rest as otherMatches on the index entry. react-profiler-component-source now returns otherMatches: [{ file, line, col }] alongside the primary, so a caller asking for List can tell the locations apart instead of trusting a single arbitrary file.

react-profiler-analyze needs no change — it enriches findings with the (now deterministic) primary location, which is strictly better than before; surfacing per-finding alternates there is out of scope.

Tests

packages/tool-server/test/react-profiler/ast-index.test.ts (index layer) and a new component-source-duplicates.test.ts (end-to-end through the tool's execute):

  • same name in List.tsx + List.web.tsx → primary is the base file, its source is returned, List.web.tsx is under otherMatches;
  • only platform variants (.android + .ios, no base) → deterministic primary by path;
  • uniquely-named component → no otherMatches.

The two collision assertions fail on pre-fix code (no otherMatches, arbitrary primary) and pass with the fix. Verified locally with Node 20: vitest run test/react-profiler/ — 80 passed; tsc --build clean; prettier + eslint clean.

…riants

The AST component index is keyed by name, and on a collision (e.g. List in
List.tsx and List.web.tsx) it kept whichever file the directory walk reached
first and discarded the rest. A lookup for that name could then return the
wrong platform variant's source, with no signal that other matches existed.

Collect every match per name, choose a deterministic primary — base files
preferred over .web/.ios/.android/.native variants, then by path/line/col so
the result no longer depends on walk order — and expose the remaining matches
as otherMatches on the entry. react-profiler-component-source now returns
otherMatches alongside the primary so callers can disambiguate.
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