UNPKG

@wizecorp/stratusjs

Version:
128 lines 4.45 kB
import { glob } from 'glob'; import * as path from 'path'; import { getConfig } from '../config'; // Cache for discovered routes let routesCache = null; /** * Converts a file path to a route path */ export function filePathToRoutePath(filePath) { const config = getConfig(); // Remove base directory and extension let routePath = filePath .replace(new RegExp(`^${config.routesDir.replace(/[/\\]/g, '[/\\\\]')}`), '') .replace(/\.(tsx?|jsx?)$/, ''); // Normalize slashes routePath = routePath.replace(/\\/g, '/'); // Handle dynamic parameters [param] -> :param routePath = routePath.replace(/\[([^\]]+)\]/g, ':$1'); // Handle catch-all routes [...param] -> *param routePath = routePath.replace(/\[\.\.\.([^\]]+)\]/g, '*$1'); // Index page becomes / routePath = routePath.replace(/\/page$/, '') || '/'; // Add initial slash if necessary if (!routePath.startsWith('/')) { routePath = '/' + routePath; } // Handle trailing slash if (config.trailingSlash && routePath !== '/' && !routePath.endsWith('/')) { routePath += '/'; } else if (!config.trailingSlash && routePath.endsWith('/') && routePath !== '/') { routePath = routePath.slice(0, -1); } return routePath; } /** * Automatically discovers routes in the project */ export async function discoverRoutes(useCache = true) { if (useCache && routesCache) { return routesCache; } const config = getConfig(); try { // Patterns to find pages const patterns = config.pageExtensions.map(ext => `${config.routesDir}/**/page.${ext}`); const routes = []; for (const pattern of patterns) { const files = await glob(pattern, { windowsPathsNoEscape: true }); for (const filePath of files) { const routePath = filePathToRoutePath(filePath); // Create a secure dynamic import const importPath = path.resolve(filePath).replace(/\\/g, '/'); routes.push({ path: routePath, component: () => import(/* @vite-ignore */ importPath), layout: await findLayoutForRoute(filePath), metadata: await loadRouteMetadata(filePath) }); } } // Sort routes by priority (static routes before dynamic) routes.sort((a, b) => { const aHasDynamic = a.path.includes(':') || a.path.includes('*'); const bHasDynamic = b.path.includes(':') || b.path.includes('*'); if (aHasDynamic && !bHasDynamic) return 1; if (!aHasDynamic && bHasDynamic) return -1; return 0; }); routesCache = routes; return routes; } catch (error) { console.error('Error discovering routes:', error); return []; } } /** * Finds the layout for a given route */ async function findLayoutForRoute(routePath) { const config = getConfig(); const routeDir = path.dirname(routePath); // Search for a layout in the same directory or parent directories let currentDir = routeDir; while (currentDir && currentDir !== config.routesDir) { for (const ext of config.pageExtensions) { const layoutPath = path.join(currentDir, `layout.${ext}`); try { // Check if file exists (simple verification) await import(/* @vite-ignore */ layoutPath); return path.relative(config.layoutsDir, layoutPath); } catch { // Layout not found, continue } } // Go up one level const parentDir = path.dirname(currentDir); if (parentDir === currentDir) break; currentDir = parentDir; } return config.defaultLayout; } /** * Loads metadata for a route */ async function loadRouteMetadata(routePath) { try { const routeDir = path.dirname(routePath); const metaPath = path.join(routeDir, 'metadata.ts'); const metaModule = await import(/* @vite-ignore */ metaPath); return metaModule.default || metaModule; } catch { return undefined; } } /** * Clears the routes cache (useful in development) */ export function clearRoutesCache() { routesCache = null; } //# sourceMappingURL=routeUtils.js.map