fix(react): recognize forwardRef/memo/styled components + index JSX-file routes (#841)
forwardRef/memo/styled-wrapped component consts were classified as plain `constant` nodes (the initializer is a call/tagged-template, not a bare arrow), so the JSX-render synthesizer and component resolution skipped them — callers and impact returned empty for the entire shadcn/ui-style UI layer. Recognize them in the tree-sitter extractor as `component` nodes (correct body range + callee capture), PascalCase-gated so a memoization util stays a constant. Separately, the `react` resolver's `languages` lacked 'tsx'/'jsx', so its `extract()` never ran on JSX files — React Router `<Route>`/createBrowserRouter and Next.js page routes (which only live in .tsx/.jsx) were never indexed. Add 'tsx'/'jsx' and make `extract()` route-only: the component/hook regex it carried duplicated tree-sitter nodes (a `useAuth` became two `function` nodes) and is fully superseded by the extractor now. Validated before/after: taxonomy 0->99 component nodes (35 w/ callers) + 1->15 routes; radix 0->262 components (80 w/ callers); cypress-realworld-app 45->52 routes (7 <Route> tags from .tsx); non-React control unchanged; node count stable. New tests: react-hoc-component.test.ts + a route e2e in frameworks-integration.test.ts. Root-caused by @maxmilian (#846); reported by @Arlandaren. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
b5090cbad5
commit
64426cad93
@@ -908,3 +908,56 @@ describe('Go gRPC stub→impl synthesis', () => {
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('React Router end-to-end route extraction (.tsx/.jsx)', () => {
|
||||
let tmpDir: string | undefined;
|
||||
afterEach(() => {
|
||||
if (tmpDir) fs.rmSync(tmpDir, { recursive: true, force: true });
|
||||
tmpDir = undefined;
|
||||
});
|
||||
|
||||
// Regression for the resolver language-gate bug: the `react` resolver's
|
||||
// `extract()` was filtered out of the .tsx/.jsx grammars, so `<Route>` routes
|
||||
// — which only live in JSX files — were never indexed through the real
|
||||
// indexing path (the unit tests call extract() directly and so missed this).
|
||||
it('indexes <Route element={<X/>}> routes from a .tsx file and links them to the component', async () => {
|
||||
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'cg-rr-'));
|
||||
fs.writeFileSync(
|
||||
path.join(tmpDir, 'package.json'),
|
||||
'{"dependencies":{"react":"^18.0.0","react-router-dom":"^6.0.0"}}'
|
||||
);
|
||||
fs.writeFileSync(
|
||||
path.join(tmpDir, 'Home.tsx'),
|
||||
'export function Home() { return null; }\n'
|
||||
);
|
||||
fs.writeFileSync(
|
||||
path.join(tmpDir, 'routes.tsx'),
|
||||
`import { Routes, Route } from 'react-router-dom';
|
||||
import { Home } from './Home';
|
||||
export function AppRoutes() {
|
||||
return (
|
||||
<Routes>
|
||||
<Route path="/home" element={<Home/>} />
|
||||
</Routes>
|
||||
);
|
||||
}
|
||||
`
|
||||
);
|
||||
|
||||
const cg = CodeGraph.initSync(tmpDir);
|
||||
await cg.indexAll();
|
||||
try {
|
||||
// The route node from the .tsx file exists (the bug: it didn't).
|
||||
const route = cg.getNodesByKind('route').find((n) => n.name === '/home');
|
||||
expect(route, '/home route from .tsx should be indexed').toBeDefined();
|
||||
|
||||
// ...and it links to the Home component.
|
||||
const home = cg.getNodesByName('Home').find((n) => n.kind === 'function');
|
||||
expect(home).toBeDefined();
|
||||
const toHome = cg.getOutgoingEdges(route!.id).find((e) => e.target === home!.id);
|
||||
expect(toHome, 'route → Home component edge').toBeDefined();
|
||||
} finally {
|
||||
cg.close();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user