import { extractParamsFromPattern, formatRouteEntry, } from "./param-extraction.js"; import { isAutoGeneratedRouteName } from "../../route-name.js"; // --------------------------------------------------------------------------- // Code generation // --------------------------------------------------------------------------- /** * Generate a per-module types file from extracted routes. * Output has zero imports, preventing circular references. */ export function generatePerModuleTypesSource( routes: Array<{ name: string; pattern: string; params?: Record; search?: Record; }>, ): string { const valid = routes.filter(({ name }) => { if (!name || /["'\\`\n\r]/.test(name)) { console.warn( `[rango] Skipping route with invalid name: ${JSON.stringify(name)}`, ); return false; } return true; }); // Deduplicate by name (first definition wins -- primary route before variants) const deduped = new Map< string, { pattern: string; params?: Record; search?: Record; } >(); for (const { name, pattern, params, search } of valid) { if (deduped.has(name)) { console.warn( `[rango] Duplicate route name "${name}" — keeping first definition`, ); continue; } deduped.set(name, { pattern, params, search }); } const sorted = [...deduped.entries()].sort(([a], [b]) => a.localeCompare(b)); const body = sorted .map(([name, { pattern, params, search }]) => { const key = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name) ? name : `"${name}"`; return formatRouteEntry(key, pattern, params, search); }) .join("\n"); return `// Auto-generated by @rangojs/router - do not edit\nexport const routes = {\n${body}\n} as const;\nexport type routes = typeof routes;\n`; } /** * Generates a .ts file that augments Rango.GeneratedRouteMap * with route name -> pattern mappings. This enables Handler<"routeName"> * without circular references since the file has no imports from the app. */ export function generateRouteTypesSource( routeManifest: Record, searchSchemas?: Record>, ): string { const entries = Object.entries(routeManifest) .filter(([name]) => !isAutoGeneratedRouteName(name)) .sort(([a], [b]) => a.localeCompare(b)); const filteredSearchSchemas = searchSchemas ? Object.fromEntries( Object.entries(searchSchemas).filter( ([name]) => !isAutoGeneratedRouteName(name), ), ) : undefined; const objectBody = entries .map(([name, pattern]) => { const key = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name) ? name : `"${name}"`; const params = extractParamsFromPattern(pattern); const search = filteredSearchSchemas?.[name]; return formatRouteEntry(key, pattern, params, search); }) .join("\n"); return `// Auto-generated by @rangojs/router - do not edit export const NamedRoutes = { ${objectBody} } as const; declare global { namespace Rango { interface GeneratedRouteMap extends Readonly {} } } `; }