/** * React Framework Resolver * * Handles React patterns: React Router routes, components, hooks, contexts. * Next.js pages, route handlers and navigation are `nextjs.ts`'s. */ import { Language, Node } from '../../types'; import { FrameworkExtractionResult, FrameworkResolver, UnresolvedRef, ResolvedRef, ResolutionContext } from '../types'; import { dependsOn } from './package-deps'; import { resolveImportPath } from '../import-resolver'; import { extractLocalExportAliases } from '../alias-binding'; import { stripCommentsForRegex } from '../strip-comments'; import { makeLineAt } from '../synth-utils'; import { isTestPath } from '../../search/query-utils'; import { matchBracket, skipString } from './object-literal'; /** The languages React components, hooks and contexts are written and used in. */ const REACT_SCRIPT_LANGUAGES: ReadonlySet = new Set(['typescript', 'javascript', 'tsx', 'jsx']); export const reactResolver: FrameworkResolver = { name: 'react', // Includes 'tsx'/'jsx' so route extraction runs on JSX files (where // `}>` routes live) — without them the .tsx/.jsx grammars // were filtered out of the extract pass and those routes were never indexed. languages: ['javascript', 'typescript', 'tsx', 'jsx'], detect(context: ResolutionContext): boolean { // React in a package.json — the root's, or a workspace's (`frontend/`, `apps/web/`). if (dependsOn(context, 'react', 'next', 'react-native')) return true; // Check for .jsx/.tsx files const allFiles = context.getAllFiles(); return allFiles.some((f) => f.endsWith('.jsx') || f.endsWith('.tsx')); }, // A data-router `lazy: () => import('./routes/x')` route names a module, not // a symbol; a route's `layout:MainLayout` names the component around it. claimsReference(name: string): boolean { return name.startsWith(LAZY_ROUTE_PREFIX) || name.startsWith(LAYOUT_PREFIX); }, resolve(ref: UnresolvedRef, context: ResolutionContext): ResolvedRef | null { // Components, hooks and contexts are a script's: halo's Java // `import org.springframework…SecurityContext` is no React context. if (!REACT_SCRIPT_LANGUAGES.has(ref.language)) return null; if (ref.referenceName.startsWith(LAZY_ROUTE_PREFIX)) { const target = lazyRouteComponent(ref.referenceName.slice(LAZY_ROUTE_PREFIX.length), ref.filePath, context); return target ? { original: ref, targetNodeId: target, confidence: 0.9, resolvedBy: 'framework' } : null; } // The layout a route renders inside: what happens in it — its header's // links, its logout — happens on the route's screen too. if (ref.referenceName.startsWith(LAYOUT_PREFIX)) { const target = layoutComponent(ref.referenceName.slice(LAYOUT_PREFIX.length), ref, context); return target ? { original: ref, targetNodeId: target, confidence: 0.9, resolvedBy: 'framework', metadata: { layout: true } } : null; } // A component, hook or context the file IMPORTS is the import's: the // package's (`useQuery` from `@tanstack/react-query`, `