fs-routes-next
Version:
Enhanced file-based routing for React Router 7+ with nested directories, automatic layout inheritance, and layout overrides
1 lines • 20.7 kB
Source Map (JSON)
{"version":3,"sources":["../src/index.ts","../src/layouts/resolver.ts","../src/utils/index.ts","../src/routes/scanner.ts"],"sourcesContent":["import type { RouteConfig, RouteConfigEntry } from \"@react-router/dev/routes\";\nimport { flatRoutes } from \"@react-router/fs-routes\";\nimport { join } from \"path\";\n\nimport { buildLayoutHierarchy } from \"./layouts/resolver\";\nimport { scanNestedRoutes } from \"./routes/scanner\";\n\n/**\n * Enhanced file-based routing for React Router with support for:\n * - Nested directories and files\n * - Automatic layout inheritance\n * - Layout overrides at any nesting level\n * - Pathless layouts for root-level overrides\n *\n * @returns Promise<RouteConfig> - Enhanced route configuration\n */\nconst enhancedFlatRoutes = async (): Promise<RouteConfig> => {\n\tconst standardRoutes = await flatRoutes();\n\tconst routesDir = join(process.cwd(), \"app/routes\");\n\n\t// Scan for nested routes and layouts\n\tconst { nestedRoutesByLayout, layoutsByPath } = scanNestedRoutes(routesDir);\n\n\t// Function to recursively find and enhance layout routes\n\tconst enhanceRoutes = (routes: RouteConfigEntry[]): RouteConfigEntry[] => {\n\t\treturn routes.map((routeConfig) => {\n\t\t\t// Check if this route has children (process them recursively)\n\t\t\tif (routeConfig.children) {\n\t\t\t\trouteConfig.children = enhanceRoutes(routeConfig.children);\n\t\t\t}\n\n\t\t\t// Check if this is a layout route that should get additional nested routes\n\t\t\tif (routeConfig.file) {\n\t\t\t\tconst layoutMatch = routeConfig.file.match(/^routes\\/(_[^.]+)\\.tsx?$/);\n\t\t\t\tif (layoutMatch) {\n\t\t\t\t\tconst layoutPrefix = layoutMatch[1];\n\t\t\t\t\tconst additionalRoutes = nestedRoutesByLayout.get(layoutPrefix) || [];\n\n\t\t\t\t\tif (additionalRoutes.length > 0) {\n\t\t\t\t\t\t// Build hierarchy with layout overrides\n\t\t\t\t\t\tconst hierarchicalRoutes = buildLayoutHierarchy(\n\t\t\t\t\t\t\tadditionalRoutes,\n\t\t\t\t\t\t\tstandardRoutes,\n\t\t\t\t\t\t\tlayoutsByPath,\n\t\t\t\t\t\t);\n\n\t\t\t\t\t\t// Add our nested routes as children of this layout\n\t\t\t\t\t\trouteConfig.children = [\n\t\t\t\t\t\t\t...(routeConfig.children || []),\n\t\t\t\t\t\t\t...hierarchicalRoutes,\n\t\t\t\t\t\t];\n\n\t\t\t\t\t\t// Remove from map so we don't create duplicates\n\t\t\t\t\t\tnestedRoutesByLayout.delete(layoutPrefix);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn routeConfig;\n\t\t});\n\t};\n\n\t// Enhance existing routes instead of creating new ones\n\tconst enhancedRoutes = enhanceRoutes(standardRoutes);\n\treturn enhancedRoutes;\n};\n\nexport { enhancedFlatRoutes as flatRoutes };\nexport type { LayoutInfo, ProcessedRoute, RouteInfo } from \"./types\";\n","import type { RouteConfigEntry } from \"@react-router/dev/routes\";\nimport { route } from \"@react-router/dev/routes\";\nimport type { LayoutInfo, ProcessedRoute } from \"../types\";\nimport { findLayoutForRoute, generateRouteId } from \"../utils\";\n\n/**\n * Build layout hierarchy for nested routes with override support\n */\nexport const buildLayoutHierarchy = (\n\troutes: ProcessedRoute[],\n\texistingRoutes: RouteConfigEntry[],\n\tlayoutsByPath: Map<string, LayoutInfo>,\n): RouteConfigEntry[] => {\n\tconst layoutRoutes = new Map();\n\n\t// Get existing route paths to avoid conflicts\n\tconst existingPaths = new Set<string>();\n\tconst addExistingPaths = (routeList: RouteConfigEntry[]) => {\n\t\tfor (const route of routeList) {\n\t\t\tif (route.path) {\n\t\t\t\texistingPaths.add(route.path);\n\t\t\t}\n\t\t\tif (route.children) {\n\t\t\t\taddExistingPaths(route.children);\n\t\t\t}\n\t\t}\n\t};\n\taddExistingPaths(existingRoutes);\n\n\t// Create layout route configs\n\tfor (const [layoutPath, layoutInfo] of layoutsByPath.entries()) {\n\t\tconst layoutPattern = layoutInfo.directoryPrefix\n\t\t\t.replace(/^_app\\./, \"\")\n\t\t\t.replace(/\\./g, \"/\");\n\t\tconst fullLayoutPattern = layoutInfo.path\n\t\t\t? `${layoutPattern}/${layoutInfo.path}`\n\t\t\t: layoutPattern;\n\n\t\t// Root-level layouts (path === '') should be pathless to avoid conflicts\n\t\tconst isRootLayoutInDirectory = layoutInfo.path === \"\";\n\n\t\t// Skip layouts that conflict with existing routes (unless they're pathless)\n\t\tif (!isRootLayoutInDirectory && existingPaths.has(fullLayoutPattern)) {\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Generate a unique ID for the layout route\n\t\tconst layoutId = `routes/${layoutPath.replace(/\\//g, \".\")}`.replace(\n\t\t\t/\\.tsx$/,\n\t\t\t\"\",\n\t\t);\n\n\t\tlet layoutRoute: RouteConfigEntry;\n\t\tif (isRootLayoutInDirectory) {\n\t\t\t// Root layouts are pathless - they don't add a route segment\n\t\t\tlayoutRoute = {\n\t\t\t\tid: layoutId,\n\t\t\t\tfile: `./routes/${layoutPath}`,\n\t\t\t\tchildren: [],\n\t\t\t};\n\t\t} else {\n\t\t\tconst routeConfig = route(fullLayoutPattern, `./routes/${layoutPath}`);\n\t\t\tlayoutRoute = {\n\t\t\t\t...routeConfig,\n\t\t\t\tid: layoutId,\n\t\t\t\tchildren: [],\n\t\t\t};\n\t\t}\n\n\t\tlayoutRoutes.set(layoutPath, layoutRoute);\n\t}\n\n\t// Assign routes to their most specific layouts\n\tconst rootRoutes: RouteConfigEntry[] = [];\n\n\tfor (const routeConfig of routes) {\n\t\tconst layout = findLayoutForRoute(\n\t\t\trouteConfig.originalPath || \"\",\n\t\t\trouteConfig.directoryPrefix || \"\",\n\t\t\tlayoutsByPath,\n\t\t);\n\n\t\tif (layout) {\n\t\t\tconst layoutPath = `${routeConfig.directoryPrefix}/${layout.file}`;\n\t\t\tconst layoutRoute = layoutRoutes.get(layoutPath);\n\t\t\tif (layoutRoute) {\n\t\t\t\t// Generate a unique ID for the route if it doesn't have one\n\t\t\t\tif (!routeConfig.id) {\n\t\t\t\t\trouteConfig.id = generateRouteId(routeConfig.file);\n\t\t\t\t}\n\n\t\t\t\t// Adjust the route path to be relative to the layout\n\t\t\t\tconst layoutBasePath = layout.path;\n\t\t\t\tconst routeOriginalPath = routeConfig.originalPath || \"\";\n\n\t\t\t\t// Remove the layout path prefix from the route path\n\t\t\t\tif (\n\t\t\t\t\tlayoutBasePath &&\n\t\t\t\t\trouteOriginalPath.startsWith(`${layoutBasePath}/`)\n\t\t\t\t) {\n\t\t\t\t\trouteConfig.path = routeOriginalPath.slice(layoutBasePath.length + 1);\n\t\t\t\t} else if (layoutBasePath && routeOriginalPath === layoutBasePath) {\n\t\t\t\t\t// If the route path equals the layout path, make it an index route\n\t\t\t\t\trouteConfig.path = undefined;\n\t\t\t\t\trouteConfig.index = true;\n\t\t\t\t} else if (layoutBasePath) {\n\t\t\t\t\t// Route is under this layout but not a direct match\n\t\t\t\t\trouteConfig.path = routeOriginalPath.startsWith(`${layoutBasePath}/`)\n\t\t\t\t\t\t? routeOriginalPath.slice(layoutBasePath.length + 1)\n\t\t\t\t\t\t: routeOriginalPath;\n\t\t\t\t}\n\n\t\t\t\tif (!layoutRoute.children) layoutRoute.children = [];\n\t\t\t\tlayoutRoute.children.push(routeConfig);\n\t\t\t} else {\n\t\t\t\t// Layout was skipped due to conflict, add route directly\n\t\t\t\tif (!routeConfig.id) {\n\t\t\t\t\trouteConfig.id = generateRouteId(routeConfig.file);\n\t\t\t\t}\n\t\t\t\trootRoutes.push(routeConfig);\n\t\t\t}\n\t\t} else {\n\t\t\t// Generate a unique ID for the route if it doesn't have one\n\t\t\tif (!routeConfig.id) {\n\t\t\t\trouteConfig.id = generateRouteId(routeConfig.file);\n\t\t\t}\n\t\t\trootRoutes.push(routeConfig);\n\t\t}\n\t}\n\n\t// Add layout routes that have children\n\tfor (const [, layoutRoute] of layoutRoutes.entries()) {\n\t\tif (layoutRoute.children && layoutRoute.children.length > 0) {\n\t\t\trootRoutes.push(layoutRoute);\n\t\t}\n\t}\n\n\treturn rootRoutes;\n};\n","import type { LayoutInfo } from \"../types\";\n\n/**\n * Generate unique route IDs for React Router\n */\nexport const generateRouteId = (filePath: string): string => {\n\tconst cleanPath = filePath\n\t\t.replace(\"./routes/\", \"\")\n\t\t.replace(/\\.(tsx?|jsx?)$/, \"\");\n\treturn `routes.${cleanPath}`.replace(/\\//g, \".\");\n};\n\n/**\n * Extract layout name from filename (removes leading underscore)\n */\nexport const getLayoutName = (fileName: string): string | null => {\n\tif (!fileName.startsWith(\"_\")) return null;\n\treturn fileName.slice(1); // Remove leading underscore\n};\n\n/**\n * Check if a layout should apply to a specific route\n */\nexport const shouldLayoutApplyToRoute = (\n\troutePath: string,\n\tlayoutInfo: LayoutInfo,\n): boolean => {\n\tconst layoutFileName =\n\t\tlayoutInfo.file\n\t\t\t.split(\"/\")\n\t\t\t.pop()\n\t\t\t?.replace(/\\.tsx?$/, \"\") || \"\";\n\tconst layoutName = getLayoutName(layoutFileName);\n\tif (!layoutName) return false;\n\n\tconst routeSegments = routePath.split(\"/\").filter(Boolean);\n\n\tif (layoutInfo.path === \"\") {\n\t\t// Root-level layout: only apply to routes matching the layout name\n\t\tconst firstSegment = routeSegments[0] || \"\";\n\t\treturn (\n\t\t\tfirstSegment === layoutName || routePath.startsWith(`${layoutName}/`)\n\t\t);\n\t}\n\n\t// Nested layout: apply to routes deeper than the layout's path\n\tconst layoutSegments = layoutInfo.path.split(\"/\").filter(Boolean);\n\tconst isDeeper = routeSegments.length > layoutSegments.length;\n\treturn isDeeper && routePath.startsWith(`${layoutInfo.path}/`);\n};\n\n/**\n * Find the most specific layout for a given route\n */\nexport const findLayoutForRoute = (\n\troutePath: string,\n\tdirectoryPrefix: string,\n\tlayoutsByPath: Map<string, LayoutInfo>,\n): LayoutInfo | null => {\n\tlet mostSpecificLayout: LayoutInfo | null = null;\n\n\tfor (const [layoutPath, layoutInfo] of layoutsByPath.entries()) {\n\t\tif (!layoutPath.startsWith(`${directoryPrefix}/`)) continue;\n\n\t\tif (shouldLayoutApplyToRoute(routePath, layoutInfo)) {\n\t\t\t// Choose the most specific layout (deepest path)\n\t\t\tif (\n\t\t\t\t!mostSpecificLayout ||\n\t\t\t\tlayoutInfo.path.split(\"/\").length >\n\t\t\t\t\tmostSpecificLayout.path.split(\"/\").length\n\t\t\t) {\n\t\t\t\tmostSpecificLayout = layoutInfo;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn mostSpecificLayout;\n};\n","import { readdirSync, statSync } from \"fs\";\nimport { join } from \"path\";\nimport type { RouteInfo } from \"../types\";\n\n/**\n * Recursively scan directories for route files\n * @param dirPath - The directory path to scan\n * @param relativePath - The relative path from the routes directory\n * @returns Array of RouteInfo objects\n */\nexport const scanDirectory = (\n\tdirPath: string,\n\trelativePath = \"\",\n): RouteInfo[] => {\n\tconst routes: RouteInfo[] = [];\n\n\ttry {\n\t\tconst items = readdirSync(dirPath);\n\n\t\tfor (const item of items) {\n\t\t\tconst itemPath = join(dirPath, item);\n\t\t\tconst stat = statSync(itemPath);\n\n\t\t\tif (stat.isFile() && (item.endsWith(\".tsx\") || item.endsWith(\".ts\"))) {\n\t\t\t\tconst routeName = item.replace(/\\.(tsx?|jsx?)$/, \"\");\n\t\t\t\tconst isLayout = routeName.startsWith(\"_\");\n\n\t\t\t\t// Skip root index files (handled by flatRoutes)\n\t\t\t\tif (routeName === \"index\" && !relativePath) continue;\n\n\t\t\t\tif (isLayout) {\n\t\t\t\t\troutes.push({\n\t\t\t\t\t\tpath: relativePath,\n\t\t\t\t\t\tfile: join(relativePath, item).replace(/\\\\/g, \"/\"),\n\t\t\t\t\t\tisLayout: true,\n\t\t\t\t\t});\n\t\t\t\t} else if (routeName === \"index\") {\n\t\t\t\t\troutes.push({\n\t\t\t\t\t\tpath: relativePath,\n\t\t\t\t\t\tfile: join(relativePath, item).replace(/\\\\/g, \"/\"),\n\t\t\t\t\t\tisLayout: false,\n\t\t\t\t\t});\n\t\t\t\t} else {\n\t\t\t\t\t// Handle parameter routes (files starting with $)\n\t\t\t\t\tlet routePath: string;\n\t\t\t\t\tif (routeName.startsWith(\"$\")) {\n\t\t\t\t\t\t// Convert $param to :param for React Router\n\t\t\t\t\t\tconst paramName = routeName.slice(1); // Remove the $\n\t\t\t\t\t\tconst paramRoute = `:${paramName}`;\n\t\t\t\t\t\troutePath = relativePath\n\t\t\t\t\t\t\t? `${relativePath}/${paramRoute}`\n\t\t\t\t\t\t\t: paramRoute;\n\t\t\t\t\t} else {\n\t\t\t\t\t\troutePath = relativePath\n\t\t\t\t\t\t\t? `${relativePath}/${routeName}`\n\t\t\t\t\t\t\t: routeName;\n\t\t\t\t\t}\n\n\t\t\t\t\troutes.push({\n\t\t\t\t\t\tpath: routePath,\n\t\t\t\t\t\tfile: join(relativePath, item).replace(/\\\\/g, \"/\"),\n\t\t\t\t\t\tisLayout: false,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t} else if (stat.isDirectory()) {\n\t\t\t\tconst subPath = relativePath ? `${relativePath}/${item}` : item;\n\t\t\t\troutes.push(...scanDirectory(itemPath, subPath));\n\t\t\t}\n\t\t}\n\t} catch (_error) {\n\t\t// Can't read directory, skip silently\n\t}\n\n\treturn routes;\n};\n\n/**\n * Scan the routes directory for nested directory structures\n * @param routesDir - The absolute path to the routes directory\n * @returns Object containing nested routes by layout and layouts by path\n */\nexport const scanNestedRoutes = (routesDir: string) => {\n\tconst nestedRoutesByLayout = new Map();\n\tconst layoutsByPath = new Map();\n\n\ttry {\n\t\tconst items = readdirSync(routesDir);\n\n\t\tfor (const item of items) {\n\t\t\tconst itemPath = join(routesDir, item);\n\t\t\tconst stat = statSync(itemPath);\n\n\t\t\tif (stat.isDirectory() && item.startsWith(\"_\")) {\n\t\t\t\t// Extract layout prefix (e.g., \"_app\" from \"_app.example\")\n\t\t\t\tconst layoutPrefix = item.split(\".\")[0];\n\n\t\t\t\t// Scan the directory recursively\n\t\t\t\tconst routes = scanDirectory(itemPath);\n\n\t\t\t\t// Separate layouts from regular routes\n\t\t\t\tconst layoutRoutes = routes.filter((r) => r.isLayout);\n\t\t\t\tconst regularRoutes = routes.filter((r) => !r.isLayout);\n\n\t\t\t\t// Store layout files for hierarchy building\n\t\t\t\tfor (const layoutRoute of layoutRoutes) {\n\t\t\t\t\tconst layoutPath = `${item}/${layoutRoute.file}`;\n\t\t\t\t\tlayoutsByPath.set(layoutPath, {\n\t\t\t\t\t\tpath: layoutRoute.path,\n\t\t\t\t\t\tfile: layoutRoute.file,\n\t\t\t\t\t\tdirectoryPrefix: item,\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\t// Process regular routes\n\t\t\t\tfor (const routeInfo of regularRoutes) {\n\t\t\t\t\t// Create the route pattern\n\t\t\t\t\tconst routePattern = item\n\t\t\t\t\t\t.replace(/^_app\\./, \"\") // Remove _app prefix\n\t\t\t\t\t\t.replace(/\\./g, \"/\"); // Convert dots to slashes\n\t\t\t\t\tconst fullRoutePattern = `${routePattern}/${routeInfo.path}`;\n\n\t\t\t\t\t// Group by layout prefix\n\t\t\t\t\tif (!nestedRoutesByLayout.has(layoutPrefix)) {\n\t\t\t\t\t\tnestedRoutesByLayout.set(layoutPrefix, []);\n\t\t\t\t\t}\n\n\t\t\t\t\tnestedRoutesByLayout.get(layoutPrefix).push({\n\t\t\t\t\t\tpath: fullRoutePattern,\n\t\t\t\t\t\tfile: `./routes/${item}/${routeInfo.file}`,\n\t\t\t\t\t\toriginalPath: routeInfo.path,\n\t\t\t\t\t\tdirectoryPrefix: item,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} catch (error) {\n\t\tconsole.warn(\"Could not scan routes directory:\", error);\n\t}\n\n\treturn { nestedRoutesByLayout, layoutsByPath };\n};\n"],"mappings":"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","names":["join","route","join"]}