import { ComponentType } from "react" ; import { match } from "path-to-regexp" ; import { StateBase } from "./FrouteHistoryState" ; import { parse as parseUrl } from "url" ; import { parse as qsParse } from "querystring" ; import { FrouteMatchResult } from "./routing" ; import { DeepReadonly } from "./utils" ; export interface RouteDefinition < Params extends string , S extends StateBase > { match ( pathname ) : FrouteMatchResult < Params > | null ; toPath ( ) : string ; createState ( ) : S | null ; getActor ( ) : Actor < any > | null ; } export interface ActorDef < R extends RouteDefinition < any , StateBase > > { component : ( ) => Promise < { default : ComponentType < any > ; } | ComponentType < any > > | ComponentType < any > ; preload ? : ( context : any , params : ParamsOfRoute < R > , extra : { query : DeepReadonly < { [ K ] : string | string [ ] | undefined ; } > ; search : string ; } ) => Promise < any > ; [ key ] : any ; } type ParamsObject < Params extends string | OptionalParam < string > > = { [ K in Extract < Params , Extract < Params , OptionalParam < any > > > ] : string ; } & { [ K in OptionalParamStringToConst < Extract < Params , OptionalParam < string > > > ] ? : string ; } ; export type ParamsOfRoute < T extends RouteDefinition < any , any > > = T extends RouteDefiner < infer P > ? ParamsObject < P > : T extends Readonly < RouteDefinition < infer P , any > > ? ParamsObject < P > : T extends RouteDefinition < infer P , any > ? ParamsObject < P > : never ; export type StateOfRoute < R extends RouteDefinition < any , StateBase > > = R extends RouteDefinition < any , infer S > ? S : never ; type OptionalParam < S extends string > = S & { __OPTIONAL : true ; } ; type OptionalParamStringToConst < P extends OptionalParam < string > > = P extends OptionalParam < infer K > ? K : never ; type ParamFragment < T extends string > = T extends `:${ infer R }?` ? OptionalParam < R > : T extends `:${ infer R }` ? R : never ; type ParamsInPath < S extends string > = string extends S ? string : S extends `${ infer R }/${ infer Rest }` ? ParamFragment < R > | ParamsInPath < Rest > : ParamFragment < S > ; deprecated export const routeBy = ( path ) : RouteDefiner < Exclude < "" , "" > > => { return new RouteDefiner ( path ) ; } ; /**
 * Define route by pathname
 *
 * - `routeOf('/fragment')`
 * - `routeOf('/fragment/:paramName')`
 * - `routeOf('/fragment/:paramName?')`
 */ export const routeOf = < S extends string > ( path ) : RouteDefiner < ParamsInPath < S > > => { return new RouteDefiner ( path ) ; } ; class Actor < R extends RouteDefinition < any , any > > implements ActorDef < R > { private cache : ComponentType < any > | null = null ; constructor ( public component , public preload ? ) { } public async loadComponent ( ) { if ( this . cache ) return this . cache ; const module = await this . component ( ) ; this . cache = ( module as any ) . default ?? module ; return this . cache ; } public get cachedComponent ( ) { return this . cache ; } } export class RouteDefiner < Params extends string , State extends StateBase = never > implements RouteDefinition < Params , State > { private stack : string [ ] = [ ] ; private actor : Actor < this > | null = null ; private stateFactory : ( ( ) => State ) | null = null ; constructor ( path ) { this . stack . push ( path . replace ( /^\// , "" ) ) ; } public param < P extends string , Params extends string > ( this , paramName ) { this . stack . push ( `:${ paramName }` ) ; return this as any ; } public path ( path ) { this . stack . push ( path . replace ( /^\// , "" ) ) ; return this as any ; } public state < S extends StateBase > ( this , stateFactory ) { this . stateFactory = stateFactory ; return this as any ; } public action ( { component , preload , ... rest } ) { this . actor = new Actor ( component , preload ) ; Object . assign ( this . actor , rest ) ; return this as any ; } public match ( pathname ) { const parsed = parseUrl ( pathname ) ; const result = match < Record < Params , string > > ( this . toPath ( ) , { decode : decodeURIComponent , } ) ( parsed . pathname ! ) ; return result ? { ... result , query : qsParse ( parsed . query ?? "" , void 0 , void 0 , { decodeURIComponent , } ) , search : parsed . search ?? "" , } : null ; } public getActor < R extends RouteDefiner < any , any > > ( this ) { return this . actor ; } public createState ( ) { return this . stateFactory ?. ( ) ?? null ; } public toPath ( ) { return "/" + this . stack . join ( "/" ) ; } }