Built and signed on GitHub ActionsBuilt and signed on GitHub Actions
Built and signed on GitHub Actions
latest
dldc-packages/cheminA type-safe pattern builder & route matching library written in TypeScript
This package works with Cloudflare Workers, Node.js, Deno, Bun, Browsers
JSR Score
82%
Published
6 months ago (12.0.0)
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172import { chemin } from "./chemin.ts"; import { isChemin } from "./core.ts"; import type { IS_CHEMIN } from "./internal.ts"; import type { IChemin, ICheminMatch, TCheminMatchMaybe, TCheminParamAny, TCreateChemin, TParams, } from "./types.ts"; import { splitPathname } from "./utils.ts"; export type TCheminsRecord = Record<string, IChemin>; export type TCheminsRecordMatches<Chemins extends TCheminsRecord> = { [K in keyof Chemins]: TCheminMatchMaybe<TParams<Chemins[K]>>; }; export type TNestedCheminsRecord = { [key: string]: IChemin | TNestedCheminsRecord; }; export type TNestedCheminsRecordMatches<Chemins extends TNestedCheminsRecord> = { [K in keyof Chemins]: Chemins[K] extends IChemin<infer P> ? TCheminMatchMaybe<P> : Chemins[K] extends TNestedCheminsRecord ? TNestedCheminsRecordMatches<Chemins[K]> : never; }; export function matchAll<Chemins extends TCheminsRecord>( chemins: Chemins, pathname: string | Array<string>, ): TCheminsRecordMatches<Chemins> { const pathParts = typeof pathname === "string" ? splitPathname(pathname) : pathname; return Object.keys(chemins).reduce<any>((acc, key) => { const chemin = chemins[key]; acc[key] = chemin.match(pathParts); return acc; }, {}); } export function matchAllNested<Chemins extends TNestedCheminsRecord>( chemins: Chemins, pathname: string | Array<string>, ): TNestedCheminsRecordMatches<Chemins> { const pathParts = typeof pathname === "string" ? splitPathname(pathname) : pathname; return Object.keys(chemins).reduce<any>((acc, key) => { const chemin = chemins[key]; if (isChemin(chemin)) { acc[key] = chemin.match(pathParts); return acc; } acc[key] = matchAllNested(chemin, pathParts); return acc; }, {}); } export function partialMatch<Params, PartialParams>( chemin: IChemin<Params>, match: TCheminMatchMaybe<Params>, part: IChemin<PartialParams>, ): null | PartialParams { if (!match) { return null; } const contains = chemin.extract().includes(part); if (contains === false) { return null; } return match.params as unknown as PartialParams; } export type TCheminsNamespaced< Base extends string | TCheminParamAny | IChemin, Chemins extends TCheminsRecord, > = { [K in keyof Chemins]: IChemin<Chemins[K][typeof IS_CHEMIN] & TParams<Base>>; }; /** * Add a base to a set of chemins */ export function namespace< Base extends string | TCheminParamAny | IChemin, Chemins extends TCheminsRecord, >( base: Base, chemins: Chemins, create: TCreateChemin = chemin, ): TCheminsNamespaced<Base, Chemins> { const result: Record<string, IChemin> = {}; Object.keys(chemins).forEach((key) => { const chemin = chemins[key]; result[key] = create(base, chemin); }); return result as TCheminsNamespaced<Base, Chemins>; } export type Prefixed< Prefix extends string, Children extends Record<string, any>, > = { [K in keyof Children as `${Prefix}.${K & string}`]: Children[K]; }; export function prefix< Prefix extends string, Children extends Record<string, any>, >( prefix: Prefix, children: Children, ): Prefixed<Prefix, Children> { return Object.fromEntries( Object.entries(children).map(([key, route]) => { return [`${prefix}.${key}`, route]; }), ) as any; } export interface IFirstMatchResult { chemin: IChemin; match: ICheminMatch<any>; } /** * Match a pathname against a list of chemins and return the first match */ export function matchFirst( chemins: Array<IChemin>, pathname: string | Array<string>, ): IFirstMatchResult | null { const pathParts = typeof pathname === "string" ? splitPathname(pathname) : pathname; for (let i = 0; i < chemins.length; i++) { const chemin = chemins[i]; const match = chemin.match(pathParts); if (match) { return { chemin: chemin, match }; } } return null; } export interface IFirstExactMatchResult { chemin: IChemin; params: any; } export function matchFirstExact( chemins: Array<IChemin>, pathname: string | Array<string>, ): IFirstExactMatchResult | null { const pathParts = typeof pathname === "string" ? splitPathname(pathname) : pathname; for (let i = 0; i < chemins.length; i++) { const chemin = chemins[i]; const params = chemin.matchExact(pathParts); if (params) { return { chemin: chemin, params }; } } return null; }