import type { ComponentType, Context, FunctionComponent, ReactNode } from "react"; import type { BaseSchema, InferOutput, ObjectSchema, TupleSchema } from "valibot"; import type { IsEmptyObject, OmitNever } from "../utils/types"; /** * Recursively extracts parameter types from a path pattern array. * * @remarks * This type iterates over a tuple of path segments. If a segment is a Valibot schema, * it infers the output type of that schema and adds it to the accumulator. * * @typeParam Arr - The tuple of path segments (strings or schemas). * @typeParam Acc - The accumulator for the extracted parameter types. */ type Params = []> = Arr extends readonly [ infer Head, ...infer Tail ] ? Head extends BaseSchema ? Params]> : Params : Acc; /** * A function that dynamically imports a component. * * @typeParam Props - The props of the component being imported. */ type ImportFn = () => Promise<{ default: FunctionComponent; }>; /** * Interface for a component that supports preloading. * * @typeParam Props - The props of the component. */ type Preloader = { preload: ImportFn; }; /** * Arguments passed to a route's preload function. * * @typeParam Args - The route parameters (e.g., from the URL path). * @typeParam SearchParam - The search parameters (query string). * @typeParam ParentParams - Parameters inherited from parent routes. * @typeParam Context - The router context type. */ type PreloadFunctionArgs = { params: Args; searchParams: SearchParam; parent: ParentParams; ctx: Context; }; /** * A function defined on a route to preload data or code before rendering. * * @remarks * This is useful for data fetching or code splitting. It runs before the component is rendered. * * @typeParam Args - The route parameters. * @typeParam SearchParam - The search parameters. * @typeParam ParentParams - The parent route parameters. * @typeParam Context - The router context. */ export type PreloadFunction = (arg: PreloadFunctionArgs) => Promise | void; /** * A React component that supports lazy loading and preloading. * * @typeParam Props - The component props. */ export type LazyComponent = Props extends undefined ? FunctionComponent & Preloader : FunctionComponent & Preloader; /** * Helper type to determine the children type for a route. * * @remarks * If the children object is empty, it returns `never`. Otherwise, it returns `ReactNode | null`. * * @typeParam C - The children configuration object. */ type RouteChildren = IsEmptyObject extends true ? never : ReactNode | null; /** * Props injected into a route component. * * @typeParam Args - The route parameters. * @typeParam SearchParam - The search parameter schema. * @typeParam Children - The children configuration. */ export type RouteComponentProps | undefined, Children> = OmitNever<{ params: Args; searchParams: SearchParam extends ObjectSchema ? InferOutput : never; children: RouteChildren; }>; /** * A route component, which can be a standard Functional Component or a Lazy Component. * * @typeParam Props - The route parameters. * @typeParam SearchParam - The search parameter schema. * @typeParam Children - The children configuration. */ export type Component | undefined, Children> = FunctionComponent> | LazyComponent>; /** * Defines the pattern of a URL path. * * @remarks * A path pattern is an array of strings (static segments) and Valibot schemas (dynamic parameters). * For example: `["users", string(), "posts", number()]` matches `/users/:userId/posts/:postId`. */ export type PathPattern = Readonly>>; /** * Configuration for a single route. * * @typeParam Path - The path pattern. * @typeParam Search - The search parameter schema. * @typeParam Children - The nested routes configuration. */ export type RouteConfig = any, Children = any> = Readonly<{ /** The path pattern for this route. */ path: Path; /** Optional schema for validating search parameters (query string). */ searchParams?: Search; /** Optional nested child routes. */ children?: Children; }>; /** * Processes the children configuration of a route to generate the corresponding router config. * * @typeParam Children - The children configuration object. * @typeParam ParentParams - Parameters inherited from the parent route. * @typeParam RouterContext - The context available to the router. */ export type ChildrenConfig, RouterContext> = IsEmptyObject extends true ? never : Children extends RoutesConfig ? CreateRouterConfig : never; /** * Represents a route definition, which can be either a simple path pattern or a full configuration object. */ export type RouteOption = Readonly>; /** * A map of route names to their definitions. */ export type RoutesConfig = Readonly<{ [route: string]: RouteOption; }>; /** * Internal representation of a processed route handler. * * @remarks * This type aggregates all necessary information for a route, including its component, * loading state, error boundary, and nested children. * * @typeParam Args - The route parameters. * @typeParam SearchParam - The search parameter schema. * @typeParam Children - The children configuration. * @typeParam RouterContext - The router context. * @typeParam ParentParams - The parent route parameters. */ export type RouteHandler | undefined, Children, RouterContext, ParentParams extends Array = []> = { preload?: PreloadFunction ? InferOutput : never, ParentParams, RouterContext>; component: Component; loading?: Component | null; error?: ComponentType | null; children: ChildrenConfig; ctx?: Context; }; /** * Generates the full router configuration from a user-provided routes config. * * @remarks * This mapped type iterates over the keys of the `Paths` config and produces a `RouteHandler` * for each route, handling both simple path patterns and detailed route configs. * * @typeParam Paths - The user-provided routes configuration. * @typeParam RouterContext - The router context. * @typeParam ParentParams - The parent route parameters. */ export type CreateRouterConfig> = { [Key in keyof Paths]: Paths[Key] extends Readonly ? OmitNever, never, never, RouterContext, ParentParams>> : Paths[Key] extends Readonly> ? OmitNever, Search, Children, RouterContext, ParentParams>> : never; }; /** * Helper type to define the parameters required to generate a link. * * @typeParam Path - The path pattern. * @typeParam SearchParams - The search parameter schema. */ type LinkParams = OmitNever<{ params: Params extends [] ? never : Params; searchParams: SearchParams extends ObjectSchema ? InferOutput : never; }>; /** * Generates type-safe link creation functions for the defined routes. * * @remarks * This type produces an object where each key corresponds to a route name. * The value is a function that takes the necessary parameters (path params and search params) * and returns the formatted URL string. If the route has children, the function also returns * the links for those children. * * @typeParam Paths - The routes configuration. */ export type Links = { [Key in keyof Paths]: Paths[Key] extends Readonly ? IsEmptyObject> extends true ? () => string : (params: LinkParams) => string : Paths[Key] extends Readonly> ? Children extends RoutesConfig ? IsEmptyObject> extends true ? () => string & Links : ((params: LinkParams) => string & Links) & Links : (params: LinkParams) => string : never; }; /** * Represents the inferred configuration for a single route after processing. * * @typeParam Params - The route parameters. * @typeParam Search - The search parameter schema. * @typeParam Children - The children configuration. * @typeParam RouterContext - The router context. */ export type InferredRouteConfig | undefined, Children, RouterContext> = { key: string; path: PathPattern; component: Component; loading?: Component | null; error?: ComponentType | null; preload?: PreloadFunction ? InferOutput : never, any, RouterContext>; children?: Children; searchSchema?: Search; paramsSchema?: TupleSchema; ctx?: Context; }; /** * Represents the full inferred router configuration. * * @remarks * This type mirrors the structure of `CreateRouterConfig` but uses `InferredRouteConfig` * to provide a more consumable type structure for the router implementation. * * @typeParam Paths - The routes configuration. * @typeParam RouterContext - The router context. */ export type InferredRouterConfig = { [Key in keyof Paths]: Paths[Key] extends Readonly ? InferredRouteConfig, undefined, undefined, RouterContext> : Paths[Key] extends Readonly> ? InferredRouteConfig, Search, Children extends RoutesConfig ? InferredRouterConfig : undefined, RouterContext> : never; }; export {};