import type { Params, ExtractRouteParams } from './types.js'; import { nullOrigin, urlObjectAssign, urlRhs } from './util.js'; type Path = string; type SearchParamsObject = Record; interface BuildOptionsBase { searchParams?: Q; hash?: string; origin?: string; } interface BuildOptionsWithParams< TPath extends Path, Q extends SearchParamsObject, > extends BuildOptionsBase { params: ExtractRouteParams; } type NamedRouteWithParams = { path: TPath; build: (options: BuildOptionsWithParams) => string; searchParams: () => NamedRouteWithParams< TPath, QNew >; }; type NamedRouteWithoutParams< TPath extends Path, Q extends SearchParamsObject, > = { path: TPath; build: (options?: BuildOptionsBase) => string; searchParams: () => NamedRouteWithoutParams< TPath, QNew >; }; export type NamedRoute< TPath extends Path, Q extends SearchParamsObject = never, > = TPath extends `${string}:${string}` ? NamedRouteWithParams : NamedRouteWithoutParams; function buildUrl( path: TPath, options?: { params?: ExtractRouteParams | Params; searchParams?: SearchParamsObject; hash?: string; origin?: string; }, ): string { const search = new URLSearchParams(options?.searchParams); search.sort(); const pathname = options?.params ? interpolate(path, options.params) : path; const newUrl = urlObjectAssign(new URL(options?.origin || nullOrigin), { pathname, search: search.toString(), hash: options?.hash, origin: options?.origin, }); return newUrl.origin === nullOrigin.origin ? urlRhs(newUrl) : newUrl.toString(); } export function namedRoute( path: TPath, ): NamedRouteWithParams; export function namedRoute( path: TPath, ): NamedRouteWithoutParams; export function namedRoute(path: TPath) { const mkbuild = () => (options?: { params?: ExtractRouteParams | Params; searchParams?: T; hash?: string; origin?: string; }) => buildUrl(path, options); return { path, build: mkbuild(), searchParams() { return { ...this, build: mkbuild(), }; }, }; } export function interpolate( path: TPath, params: ExtractRouteParams | Params = {}, ): string { return path.replace(/\/:(\w+)[?+*]?/g, (_match, token: keyof typeof params) => params[token] ? `/${params[token]}` : '', ); }