Skip to content

fix(jsx): a .jsx component links to the file-scope values it reads, as in .js and .tsx - #2561

Merged
colbymchenry merged 4 commits into
mainfrom
claude/heuristic-yalow-50bd8b
Oct 14, 2026
Merged

colbymchenry merged 4 commits into
mainfrom
claude/heuristic-yalow-50bd8b

Conversation

@colbymchenry

Copy link
Copy Markdown
Owner

Summary

A .jsx file got no same-file value-reference edges: the references edges (metadata valueRef: true) from a reader to the file-scope const/var it reads, which codegraph impact and the impact trail in codegraph_explore / codegraph_node follow. TreeSitterExtractor.VALUE_REF_LANGS listed typescript, javascript and tsx, but not jsx, and the kernel mirrored the gap. So a component in a .jsx file had no edge to the lazy page it renders (<LoginPage /> under const LoginPage = Loadable(lazy(…))), the route table it builds or the config it maps over, while the same code in .js or .tsx had all of them.

Probe: the same React source through extractFromSource as .jsx, .tsx and .js:

.jsx .tsx .js
main, wasm path 0 4 4
main, kernel path 0 4 4
this PR, both paths 4 4 4

Was leaving out jsx deliberate?

No sign of it. git log -G VALUE_REF_LANGS: the set was introduced in ec90ddf (2026-06-13) as ['typescript', 'javascript', 'tsx'], and #897 and #1186 (ArkTS) extended it. None of them mentions jsx. The design doc's coverage table says "TypeScript, JavaScript, tsx … the original languages" and never names jsx. Every other JS-family gate in the extractor includes jsx: declarators, import bindings, re-exports, store detection and the fn-ref spec. The kernel's Variant::value_refs() existed only to copy the TS set bug-for-bug, with the comment "VALUE_REF_LANGS includes typescript/tsx/javascript but NOT jsx". .jsx uses the same JavaScript grammar as .js, so nothing about it needs different handling.

Change

  • src/extraction/tree-sitter.ts: add 'jsx' to VALUE_REF_LANGS.
  • codegraph-kernel/src/tsjs/mod.rs: drop Variant::value_refs() and its two early returns, so every TS/JS variant runs value refs, as on the wasm path.
  • Docs: the value-reference design doc and playbook now list jsx (and the playbook's list also gains arkts, already in the set).

Tests

  • value-reference-edges.test.ts: a .jsx file indexed end to end: AuthRoutes → LoginPage (a lazy page rendered as a tag) and Nav → NAV_ITEMS (a table mapped inside JSX).
  • kernel-tsjs-parity.test.ts: the same source as tsx, js and jsx through the kernel and the wasm path, asserting parity and the exact four value-ref edges.
  • On main both jsx cases fail and the tsx/js ones pass. With the fixed TypeScript but main's kernel (CODEGRAPH_KERNEL_PATH), the parity case fails (kernel 8 edges, wasm 12), so a fix on one side only can't pass.

Validation

Before/after full indexes of the same repos, kernel-routed as users run them, dumped with scripts/dump-graph.mjs and diffed by natural keys. The pair was rebuilt and rerun on each main this branch merged: d8a7f868, ed199e60, e47cb258, and finally d0bb9be5, which includes #2420. Every run added the same edge set, line for line.

Repo .jsx files nodes refs edges removed edges added
codedthemes/mantis-free-react-admin-template 155 same same 0 35
uilibrary/matx-react 136 same same 0 56
codedthemes/berry-free-react-admin-template 103 same same 0 15
mastodon/mastodon (Rails + React, mostly .tsx now) 92 same same 0 28
bigbluebutton/bigbluebutton, bigbluebutton-html5 201 same same 0 266
alan2207/bulletproof-react (control, no .jsx) 0 same same 0 0, byte-identical

Every added edge was checked against the source with a parse (the grammar the extractor uses for .jsx). Each one:

  • is a references edge with {"valueRef":true}, with both ends in the same .jsx file,
  • targets a file-scope const/let/var,
  • has an identifier read of the target inside the reader's span,
  • and its target is the only binding of that name in the file: no parameter, destructured local, import or catch binding of the same name. That matters because the shadow prune counts only const/let/var declarators, so a same-named parameter would get past it. That gap predates this PR and shows in .js and .tsx with a synthetic function F({ headCells }). An audit of all 2,201 JS/TS value-ref edges in these repos, including the ones .js/.tsx files already had, found no real case. A default value like { error = defaultError } is a read, so its edge is correct.

All 400 pass. By what the target is:

Target Edges Example
intl message table (defineMessages) 149 bbb's intlMessages, read by render and handlers
scalar constant 62 DEBOUNCE_TIME, ANIMATION_NONE
object table / sx style object 56 mantis avatarSX, matx initialAuthState
array table 47 mantis headCells, chart data like monthlyLabels
lazy page (Loadable(lazy(() => import(…)))) 35 matx materialRoutes → AppButton, berry MainRoutes → DashboardDefault
context (createContext) 14 ConfigProvider → ConfigContext (<ConfigContext.Provider>)
module-level let assigned later 6 mastodon let EmojiPicker, Emoji;, set in onShowDropdown, rendered in render
other (regexes, selectors, a Yup schema, new DOMParser()) 31

In 201 of the 400, the reader uses the value inside JSX, as a tag (<LoginPage />) or in an expression ({rows.map(…)}). In the other 199 it's plain code, such as a handler reading a message table.

What a user sees (codegraph impact, before → after):

  • matx AppButton (const AppButton = Loadable(lazy(() => import("./buttons/AppButton")))): 1 → 3 affected. The materialRoutes table that renders <AppButton /> is now affected, and so is routes.jsx, which imports that table.
  • mantis headCells (a column table): 1 → 3 affected. OrderTableHead, which maps over it, is now affected, and so is OrderTable, which renders <OrderTableHead>.

scripts/kernel-parity.mjs --lang jsx,javascript,tsx,typescript over the five .jsx repos with the fixed build: 0 files with diffs. 2,556 files are byte-identical and 37 defer to wasm on parse errors, the same 37 as with the unmodified build. Nodes and refs are unchanged; only edge totals grow.

Test suite. On the final branch (main d0bb9be5 + this change), with the kernel staged and CODEGRAPH_KERNEL_EXPECT=1:

  • full suite: 7,091 passed, 0 failed, 80 skipped;
  • the build's tsc is clean.

An earlier full run, on a box saturated by other sessions, had 5 failures in daemon/MCP/process tests (mcp-daemon, mcp-status-freshness, mcp-writer-lock, daemon-pid-reuse, and function-ref's 60 s #1820 case). None of those tests indexes a .jsx file. Each failure reproduced on main under the same load in interleaved runs, or passed on rerun.

Overlap

🤖 Generated with Claude Code

colbymchenry and others added 4 commits October 7, 2026 05:38
…s in .js and .tsx

Value-reference edges (a reader symbol -> the file-scope const/var it
reads, which impact follows) ran for typescript, tsx and javascript but
not jsx. A .jsx file is its own language, parsed with the JavaScript
grammar, so the same component had no edge to the lazy page it renders
(`<LoginPage />` under `const LoginPage = Loadable(lazy(...))`), the route
table or the config it maps over as .jsx, and all of them as .js or .tsx.
Every other JS-family gate in the extractor lists jsx, and nothing in the
history says this one was left out on purpose: the kernel mirrored the gap
with an explicit "NOT jsx" check for parity.

Add jsx to VALUE_REF_LANGS and drop the kernel's jsx exclusion.

On mantis, matx, berry, mastodon and bigbluebutton-html5 the index only
gains edges (35, 56, 15, 28 and 266): no node, ref or file changes and no
edge removed. Every added edge is a same-file read of a file-scope value
that is the only binding of its name in the file. A repo with no .jsx
files indexes byte-identically, and kernel-parity.mjs shows 0 diffs over
those repos with the same deferrals as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…w-50bd8b

# Conflicts:
#	__tests__/kernel-tsjs-parity.test.ts
@colbymchenry
colbymchenry merged commit 3f714bc into main Oct 14, 2026
@colbymchenry
colbymchenry deleted the claude/heuristic-yalow-50bd8b branch October 14, 2026 23:23
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