Skip to content

forwardRef components get no JSX render edges — callers/impact silently return empty #841

Description

@Arlandaren

Version: 0.9.9 (linux x64, node:sqlite backend)
Language: TypeScript/TSX (Turborepo monorepo, ~490 indexed files)

Summary

Components declared as const X = React.forwardRef(...) are indexed as
constant nodes, and no render/call edges are created for their JSX
usages
(<X ...>). As a result codegraph_callers / codegraph_impact
return "No callers found" for any shadcn/ui-style component, even when
dozens of files render it.

Function-declared components are fine: for export function ThemeProvider()
the JSX render edges exist and callers are reported correctly. The problem
is specific to the declaration style.

Repro

// packages/ui/src/button.tsx
import * as React from "react";
export const Button = React.forwardRef<
  HTMLButtonElement,
  React.ButtonHTMLAttributes<HTMLButtonElement>
>((props, ref) => <button ref={ref} {...props} />);
Button.displayName = "Button";

// apps/web/page.tsx
import { Button } from "../../packages/ui/src/button";
export function Page() {
  return <Button>Click</Button>;
}
codegraph init -i
codegraph callers Button
Expected: Page (function)  apps/web/page.tsx
Actual: No callers found for "Button"

Also observed:

codegraph_search with kind: "component" does not return Button at all (it only exists as a constant node).
The import { Button } ... edges ARE indexed, so the data loss is only in the render/call edge extraction, not in import tracking.
Real-world impact
In our monorepo is rendered in 81 files across 3 apps
(verified with grep), yet codegraph_callers Button and
codegraph_impact Button report nothing. This is a dangerous silent false
negative: "no callers" reads as "safe to change" right before a refactor of
a design-system component. Every shadcn/ui component uses the
const X = React.forwardRef(...) pattern, so for typical React design
systems the entire UI layer is invisible to impact analysis.

Likely related (untested): React.memo(...), styled(...) and other
HOC-wrapped const components.

Suggested fix

Treat const X = React.forwardRef(...) / React.memo(...) initializers as
component declarations in the tree-sitter extraction queries (kind:
component), and emit the same JSX render edges that function-declared
components get.

For comparison: function-declared components in the same codebase resolve
correctly through barrel re-exports and workspace aliases (@repo/ui), so
the resolution layer itself looks solid — it's only the component
recognition for wrapped declarations that's missing.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions