{"version":3,"file":"route-builder.mjs","sources":["../../../src/lib/routing/route-builder.ts"],"sourcesContent":["/**\n * @file Type-Safe Route Builder\n * @description Factory functions for creating type-safe route path builders and route objects\n */\n\nimport type { RouteObject } from 'react-router-dom';\nimport {\n  lazy,\n  createElement,\n  type ComponentType,\n  type LazyExoticComponent,\n  type ReactNode,\n} from 'react';\nimport type {\n  DiscoveredRoute,\n  RouteDefinition,\n  RouteMetadata,\n  RouteAccessConfig,\n  RouteModule,\n  RouteParams,\n} from './types';\nimport { extractParamNames, generateRouteId, generateDisplayName } from './scanner';\nimport {\n  buildPath as coreBuildPath,\n  matchPathPattern,\n  parsePathParams,\n  getLastSegment,\n} from './core';\n\n// =============================================================================\n// Path Building (delegating to core)\n// =============================================================================\n\n/**\n * Build a parameterized path from a pattern and params\n * @see core/path-utils.ts for implementation details\n *\n * @example\n * buildRoutePath('/users/:id', { id: '123' }) // '/users/123'\n * buildRoutePath('/users/:id?', {}) // '/users'\n * buildRoutePath('/users/:id', { id: '123' }, { tab: 'settings' }) // '/users/123?tab=settings'\n */\nexport function buildRoutePath(\n  pattern: string,\n  params?: Record<string, string>,\n  query?: Record<string, string | undefined>\n): string {\n  return coreBuildPath(pattern, params, query);\n}\n\n/**\n * Create a type-safe route builder function for a specific path pattern\n *\n * @example\n * const userRoute = createRouteBuilder('/users/:id');\n * userRoute({ id: '123' }) // '/users/123'\n *\n * const homeRoute = createRouteBuilder('/');\n * homeRoute() // '/'\n */\nexport function createRouteBuilder<TPath extends string>(\n  path: TPath\n): (params?: RouteParams<TPath>, query?: Record<string, string | undefined>) => string {\n  const paramNames = extractParamNames(path);\n  const requiredParams = paramNames.filter((p) => !path.includes(`:${p}?`));\n\n  return (params?: RouteParams<TPath>, query?: Record<string, string | undefined>): string => {\n    // Validate required params\n    if (requiredParams.length > 0) {\n      if (params === null || params === undefined) {\n        throw new Error(`Route \"${path}\" requires parameters: ${requiredParams.join(', ')}`);\n      }\n\n      for (const param of requiredParams) {\n        if (!(param in params) || (params as Record<string, string>)[param] === undefined) {\n          throw new Error(`Route \"${path}\" requires parameter \"${param}\"`);\n        }\n      }\n    }\n\n    return buildRoutePath(path, params as Record<string, string>, query);\n  };\n}\n\n/**\n * Create a typed route registry from a routes object\n *\n * @example\n * const routes = {\n *   home: '/',\n *   users: '/users',\n *   userDetail: '/users/:id',\n * } as const;\n *\n * const builders = createRouteBuilders(routes);\n * builders.home() // '/'\n * builders.userDetail({ id: '123' }) // '/users/123'\n */\nexport function createRouteBuilders<T extends Record<string, string>>(\n  routes: T\n): {\n  [K in keyof T]: (\n    params?: RouteParams<T[K]>,\n    query?: Record<string, string | undefined>\n  ) => string;\n} {\n  const builders = {} as {\n    [K in keyof T]: (\n      params?: RouteParams<T[K]>,\n      query?: Record<string, string | undefined>\n    ) => string;\n  };\n\n  for (const [key, path] of Object.entries(routes)) {\n    builders[key as keyof T] = createRouteBuilder(path) as (\n      params?: RouteParams<T[keyof T]>,\n      query?: Record<string, string | undefined>\n    ) => string;\n  }\n\n  return builders;\n}\n\n// =============================================================================\n// Route Object Building\n// =============================================================================\n\n/**\n * Import function type for dynamic imports\n */\nexport type RouteImportFn = () => Promise<\n  { default: ComponentType<unknown> } & Partial<RouteModule>\n>;\n\n/**\n * Create a lazy component with preload capability\n */\nexport function createLazyRoute(\n  importFn: RouteImportFn\n): LazyExoticComponent<ComponentType<unknown>> & { preload: () => Promise<RouteModule> } {\n  const LazyComponent = lazy(importFn) as LazyExoticComponent<ComponentType<unknown>> & {\n    preload: () => Promise<RouteModule>;\n  };\n\n  LazyComponent.preload = async () => {\n    const module = await importFn();\n    return module as RouteModule;\n  };\n\n  return LazyComponent;\n}\n\n/**\n * Build a RouteObject from a discovered route\n */\nexport function buildRouteObject(\n  route: DiscoveredRoute,\n  importFn: RouteImportFn,\n  options: {\n    errorElement?: ReactNode;\n    loadingElement?: ReactNode;\n  } = {}\n): RouteObject {\n  // For React Router, we use the lazy() API for code splitting\n  const baseRoute = {\n    id: generateRouteId(route),\n    lazy: async () => {\n      const module = await importFn();\n      return {\n        Component: module.default,\n        loader: module.loader,\n        action: module.action,\n        handle: module.handle,\n        errorElement: module.ErrorBoundary\n          ? createElement(module.ErrorBoundary, { error: new Error('Route error') })\n          : options.errorElement,\n      };\n    },\n  };\n\n  // Set path or index\n  if (route.isIndex) {\n    return { ...baseRoute, index: true } as RouteObject;\n  } else if (route.urlPath !== '/') {\n    // Remove leading slash for nested routes\n    const lastSegment = getLastSegment(route.urlPath);\n    return { ...baseRoute, path: lastSegment || undefined } as RouteObject;\n  }\n\n  return baseRoute as RouteObject;\n}\n\n/**\n * Build a complete route tree from discovered routes\n */\nexport function buildRouteTree(\n  routes: DiscoveredRoute[],\n  importMap: Map<string, RouteImportFn>,\n  options: {\n    errorElement?: ReactNode;\n    loadingElement?: ReactNode;\n  } = {}\n): RouteObject[] {\n  const tree: RouteObject[] = [];\n  const layoutMap = new Map<string, RouteObject>();\n  const routesByPath = new Map<string, RouteObject>();\n\n  // First pass: create all route objects\n  for (const route of routes) {\n    const importFn = importMap.get(route.filePath);\n    if (!importFn) {\n      console.warn(`No import function found for route: ${route.filePath}`);\n      continue;\n    }\n\n    const routeObj = buildRouteObject(route, importFn, options);\n    routesByPath.set(route.filePath, routeObj);\n\n    if (route.isLayout) {\n      layoutMap.set(route.filePath, routeObj);\n      // Set the layout path\n      const lastSegment = getLastSegment(route.urlPath);\n      if (lastSegment) {\n        routeObj.path = lastSegment;\n      }\n    }\n  }\n\n  // Second pass: build hierarchy\n  for (const route of routes) {\n    if (route.isLayout) continue;\n\n    const routeObj = routesByPath.get(route.filePath);\n    if (!routeObj) continue;\n\n    // Find parent layout\n    if (route.parentLayout != null) {\n      const parentLayout = layoutMap.get(route.parentLayout);\n      if (parentLayout) {\n        parentLayout.children = parentLayout.children ?? [];\n        parentLayout.children.push(routeObj);\n        continue;\n      }\n    }\n\n    // No parent layout - add to root\n    tree.push(routeObj);\n  }\n\n  // Add root layouts to tree\n  for (const route of routes) {\n    if (!route.isLayout) continue;\n    if (route.parentLayout != null) continue; // Nested layout already handled\n\n    const layoutObj = layoutMap.get(route.filePath);\n    if (layoutObj) {\n      tree.push(layoutObj);\n    }\n  }\n\n  return tree;\n}\n\n// =============================================================================\n// Route Definition Building\n// =============================================================================\n\n/**\n * Create a RouteDefinition from a discovered route\n */\nexport function createRouteDefinition(\n  route: DiscoveredRoute,\n  meta?: RouteMetadata,\n  access?: RouteAccessConfig\n): RouteDefinition {\n  const paramNames = extractParamNames(route.urlPath);\n\n  return {\n    id: generateRouteId(route),\n    path: route.urlPath,\n    displayName: generateDisplayName(route),\n    sourceFile: route.filePath,\n    paramNames: paramNames as readonly string[],\n    layout: route.parentLayout,\n    meta: meta ?? {},\n    access: access ?? { isPublic: true },\n    hasLoader: false, // Will be updated when module is loaded\n    hasAction: false, // Will be updated when module is loaded\n  };\n}\n\n/**\n * Create route definitions for all discovered routes\n */\nexport function createRouteDefinitions(routes: DiscoveredRoute[]): RouteDefinition[] {\n  return routes.filter((r) => !r.isLayout).map((r) => createRouteDefinition(r));\n}\n\n// =============================================================================\n// Route Matching Utilities (delegating to core)\n// =============================================================================\n\n/**\n * Check if a path matches a route pattern\n * @see core/path-utils.ts for implementation details\n */\nexport function matchRoute(pattern: string, path: string): boolean {\n  return matchPathPattern(pattern, path);\n}\n\n/**\n * Extract parameters from a path using a pattern\n * @see core/path-utils.ts for implementation details\n */\nexport function extractRouteParams(pattern: string, path: string): Record<string, string> | null {\n  return parsePathParams(pattern, path);\n}\n\n// =============================================================================\n// Type Generators (for build-time code generation)\n// =============================================================================\n\n/**\n * Generate TypeScript type definitions for routes\n */\nexport function generateRouteTypeDefinitions(routes: DiscoveredRoute[]): string {\n  const lines: string[] = [\n    '/**',\n    ' * @file Auto-Generated Route Types',\n    ' * @description DO NOT EDIT - Generated by route discovery',\n    ` * @generated ${new Date().toISOString()}`,\n    ' */',\n    '',\n    '// Route path constants',\n    'export const ROUTE_PATHS = {',\n  ];\n\n  const nonLayoutRoutes = routes.filter((r) => !r.isLayout);\n\n  for (const route of nonLayoutRoutes) {\n    const id = generateRouteId(route);\n    lines.push(`  ${id}: '${route.urlPath}' as const,`);\n  }\n\n  lines.push('} as const;');\n  lines.push('');\n  lines.push('export type RoutePath = typeof ROUTE_PATHS[keyof typeof ROUTE_PATHS];');\n  lines.push('');\n\n  // Generate RouteParams interface\n  lines.push('// Route parameter types');\n  lines.push('export interface RouteParams {');\n\n  for (const route of nonLayoutRoutes) {\n    const paramNames = extractParamNames(route.urlPath);\n    if (paramNames.length > 0) {\n      const paramType = paramNames.map((p) => `${p}: string`).join('; ');\n      lines.push(`  '${route.urlPath}': { ${paramType} };`);\n    }\n  }\n\n  lines.push('}');\n  lines.push('');\n\n  return lines.join('\\n');\n}\n\n/**\n * Generate route registry code\n */\nexport function generateRouteRegistry(routes: DiscoveredRoute[], rootDir: string): string {\n  const lines: string[] = [\n    '/**',\n    ' * @file Auto-Generated Route Registry',\n    ' * @description DO NOT EDIT - Generated by route discovery',\n    ` * @generated ${new Date().toISOString()}`,\n    ' */',\n    '',\n    \"import { lazy } from 'react';\",\n    '',\n    '// Lazy-loaded route components',\n    'export const routeComponents = {',\n  ];\n\n  const nonLayoutRoutes = routes.filter((r) => !r.isLayout);\n\n  for (const route of nonLayoutRoutes) {\n    const id = generateRouteId(route);\n    const importPath = route.filePath\n      .replace(rootDir, '')\n      .replace(/^\\/src\\//, '@/')\n      .replace(/\\.tsx?$/, '');\n\n    lines.push(`  ${id}: lazy(() => import('${importPath}')),`);\n  }\n\n  lines.push('} as const;');\n  lines.push('');\n\n  // Generate preload functions\n  lines.push('// Route preloading');\n  lines.push('export const preloadRoute = {');\n\n  for (const route of nonLayoutRoutes) {\n    const id = generateRouteId(route);\n    const importPath = route.filePath\n      .replace(rootDir, '')\n      .replace(/^\\/src\\//, '@/')\n      .replace(/\\.tsx?$/, '');\n\n    lines.push(`  ${id}: () => import('${importPath}'),`);\n  }\n\n  lines.push('} as const;');\n  lines.push('');\n\n  return lines.join('\\n');\n}\n"],"names":["buildRoutePath","pattern","params","query","coreBuildPath","createRouteBuilder","path","requiredParams","extractParamNames","p","param","createRouteBuilders","routes","builders","key","createLazyRoute","importFn","LazyComponent","lazy","buildRouteObject","route","options","baseRoute","generateRouteId","module","createElement","lastSegment","getLastSegment","buildRouteTree","importMap","tree","layoutMap","routesByPath","routeObj","parentLayout","layoutObj","createRouteDefinition","meta","access","paramNames","generateDisplayName","createRouteDefinitions","r","matchRoute","matchPathPattern","extractRouteParams","parsePathParams","generateRouteTypeDefinitions","lines","nonLayoutRoutes","id","paramType","generateRouteRegistry","rootDir","importPath"],"mappings":"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"}