Repository navigation
Regression since 0.10.0: JSX render edges dropped from arrow-function components (const X = () => <Child/>) #2559
Description
Version
0.11.0. Also reproduced on 0.10.0 and 0.10.8. Works on 0.9.0.
Platform
Linux (arm64), the linux-arm64-portable build in a Debian bookworm container. The darwin-arm64 build on macOS (Apple Silicon) behaves the same.
Install channel
GitHub release archive. Checksums verified against checksums.txt.
Binary variant
standard
What happened, and what did you expect?
Since 0.10.0, a React component written as an arrow function assigned to a const no longer gets a CALLS edge to the components it renders in JSX. Plain function calls from the same component are still recorded. Components written as function declarations, async or not, keep both kinds of edge.
Expected: the same edges as 0.9.0. ArrowParent -> Child and AsyncArrowParent -> Child should be CALLS edges, just as FnParent -> Child is.
Impact: in a typical Next.js/React codebase most components are arrow consts. In one such codebase there are about 1,075 arrow components and 69 function components. On 0.11.0, trace_path --direction inbound returns 0 callers for widely used components, where 0.9.0 returns between 1 and 78.
The cause is not threading or LSP. The result is identical with CBM_INDEX_SINGLE_THREAD=1, with CBM_WORKERS=1 and with CBM_LSP_DISABLED=1. It is also identical with --mode fast and with --mode full.
This looks related to #1220, but it is a separate problem. #1220 reports that arrow consts are labelled Variable. That was already the case on 0.9.0, yet 0.9.0 still recorded these JSX edges, and in the repro below 0.11.0 labels the arrow component Function and still drops its JSX edge.
Reproduction
mkdir -p /tmp/cbm-jsx/src && cd /tmp/cbm-jsx
printf '{"compilerOptions":{"jsx":"preserve","strict":true}}\n' > tsconfig.json
printf '{"name":"cbm-jsx","version":"0.0.0"}\n' > package.json
cat > src/child.tsx <<'EOF'
export default function Child({ label }: { label: string }) {
return <span>{label}</span>;
}
EOF
cat > src/helper.ts <<'EOF'
export function helper(n: number) { return n * 2; }
EOF
cat > src/parents.tsx <<'EOF'
import Child from './child';
import { helper } from './helper';
export function FnParent() {
return <Child label="a" />;
}
export const ArrowParent = () => {
const n = helper(2);
return <Child label={String(n)} />;
};
export const AsyncArrowParent = async ({ lang }: { lang: string }) => {
const x = await Promise.resolve(lang);
return <Child label={x} />;
};
export async function AsyncFnParent() {
return <Child label="d" />;
}
EOF
codebase-memory-mcp cli index_repository --repo-path /tmp/cbm-jsx --mode full
codebase-memory-mcp cli query_graph --project tmp-cbm-jsx \
--query 'MATCH (a)-[r]->(b) WHERE b.name IN ["Child", "helper"] RETURN a.name, labels(a), type(r), b.name'On 0.9.0, every parent has CALLS -> Child, and ArrowParent also has CALLS -> helper.
On 0.11.0 (macOS arm64 build, verbatim):
rows: 5 (cols: a.name "labels(a)" "type(r)" b.name)
child.tsx "[\"File\"]" DEFINES Child
helper.ts "[\"File\"]" DEFINES helper
FnParent "[\"Function\"]" CALLS Child
ArrowParent "[\"Function\"]" CALLS helper
AsyncFnParent "[\"Function\"]" CALLS Child
The CALLS -> Child edges from ArrowParent and AsyncArrowParent are missing.
(The project name above follows the default path-derived naming; use list_projects if it differs.)
Project scale (if relevant)
The repro is three files. The real-world impact was observed on a Next.js codebase of about 14k nodes and 43k edges.
Confirmations
- I searched existing issues and this is not a duplicate.
- My reproduction uses shareable code (a dummy snippet or a public OSS repository), not proprietary code.
Activity