UNPKG

one

Version:

One is a new React Framework that makes Vite serve both native and web.

133 lines (114 loc) 3.82 kB
import { getRoutes } from '../router/getRoutes' import { isTypedRoute, removeSupportedExtensions } from '../router/matchers' import type { RouteNode } from '../router/Route' import type { One } from '../vite/types' // /[...param1]/ - Match [...param1] const CATCH_ALL = /\[\.\.\..+?\]/g // /[param1] - Match [param1] const SLUG = /\[.+?\]/g export function getTypedRoutesDeclarationFile(ctx: One.RouteContext) { const staticRoutes = new Set<string>() const dynamicRoutes = new Set<string>() const dynamicRouteContextKeys = new Set<string>() walkRouteNode( getRoutes(ctx, { platformRoutes: false, // We don't need to generate platform specific routes ignoreEntryPoints: true, ignoreRequireErrors: true, // importMode: 'async', }), '', staticRoutes, dynamicRoutes, dynamicRouteContextKeys ) return `// deno-lint-ignore-file /* eslint-disable */ // biome-ignore: needed import import type { OneRouter } from 'one' declare module 'one' { export namespace OneRouter { export interface __routes<T extends string = string> extends Record<string, unknown> { StaticRoutes: ${setToUnionType(staticRoutes)} DynamicRoutes: ${setToUnionType(dynamicRoutes)} DynamicRouteTemplate: ${setToUnionType(dynamicRouteContextKeys)} IsTyped: true } } } `.trim() } /** * Walks a RouteNode tree and adds the routes to the provided sets */ function walkRouteNode( routeNode: RouteNode | null, parentRoutePath: string, staticRoutes: Set<string>, dynamicRoutes: Set<string>, dynamicRouteContextKeys: Set<string> ) { if (!routeNode) return addRouteNode(routeNode, parentRoutePath, staticRoutes, dynamicRoutes, dynamicRouteContextKeys) parentRoutePath = `${removeSupportedExtensions(`${parentRoutePath}/${routeNode.route}`).replace(/\/?index$/, '')}` // replace /index with / for (const child of routeNode.children) { walkRouteNode(child, parentRoutePath, staticRoutes, dynamicRoutes, dynamicRouteContextKeys) } } /** * Given a RouteNode, adds the route to the correct sets * Modifies the RouteNode.route to be a typed-route string */ function addRouteNode( routeNode: RouteNode | null, parentRoutePath: string, staticRoutes: Set<string>, dynamicRoutes: Set<string>, dynamicRouteContextKeys: Set<string> ) { if (!routeNode?.route) return if (!isTypedRoute(routeNode.route)) return let routePath = `${parentRoutePath}/${removeSupportedExtensions(routeNode.route).replace(/\/?index$/, '')}` // replace /index with / if (!routePath.startsWith('/')) { routePath = `/${routePath}` } if (routeNode.dynamic) { for (const path of generateCombinations(routePath)) { dynamicRouteContextKeys.add(path) dynamicRoutes.add( `${path.replaceAll(CATCH_ALL, '${string}').replaceAll(SLUG, '${OneRouter.SingleRoutePart<T>}')}` ) } } else { for (const combination of generateCombinations(routePath)) { staticRoutes.add(combination) } } } /** * Converts a Set to a TypeScript union type */ const setToUnionType = <T>(set: Set<T>) => { return set.size > 0 ? [...set] .sort() .map((s) => `\`${s}\``) .join(' | ') : 'never' } function generateCombinations(pathname) { const groups = pathname.split('/').filter((part) => part.startsWith('(') && part.endsWith(')')) const combinations: string[] = [] function generate(currentIndex, currentPath) { if (currentIndex === groups.length) { combinations.push(currentPath.replace(/\/{2,}/g, '/')) return } const group = groups[currentIndex] const withoutGroup = currentPath.replace(`/${group}`, '') generate(currentIndex + 1, withoutGroup) generate(currentIndex + 1, currentPath) } generate(0, pathname) return combinations }