Bluesky app fork with some witchin' additions 馃挮
at main 76 lines 2.1 kB view raw
1import {type Route, type RouteParams} from './types' 2 3export class Router<T extends Record<string, any>> { 4 routes: [string, Route][] = [] 5 constructor(description: Record<keyof T, string | string[]>) { 6 for (const [screen, pattern] of Object.entries(description)) { 7 if (typeof pattern === 'string') { 8 this.routes.push([screen, createRoute(pattern)]) 9 } else { 10 pattern.forEach(subPattern => { 11 this.routes.push([screen, createRoute(subPattern)]) 12 }) 13 } 14 } 15 } 16 17 matchName(name: keyof T | (string & {})): Route | undefined { 18 for (const [screenName, route] of this.routes) { 19 if (screenName === name) { 20 return route 21 } 22 } 23 } 24 25 matchPath(path: string): [string, RouteParams] { 26 let name = 'NotFound' 27 let params: RouteParams = {} 28 for (const [screenName, route] of this.routes) { 29 const res = route.match(path) 30 if (res) { 31 name = screenName 32 params = res.params 33 break 34 } 35 } 36 return [name, params] 37 } 38} 39 40function createRoute(pattern: string): Route { 41 const pathParamNames: Set<string> = new Set() 42 let matcherReInternal = pattern.replace(/:([\w]+)/g, (_m, name) => { 43 pathParamNames.add(name) 44 return `(?<${name}>[^/]+)` 45 }) 46 const matcherRe = new RegExp(`^${matcherReInternal}([?]|$)`, 'i') 47 return { 48 match(path) { 49 const {pathname, searchParams} = new URL(path, 'http://throwaway.com') 50 const addedParams = Object.fromEntries(searchParams.entries()) 51 52 const res = matcherRe.exec(pathname) 53 if (res) { 54 return {params: Object.assign(addedParams, res.groups || {})} 55 } 56 return undefined 57 }, 58 build(params = {}) { 59 const str = pattern.replace( 60 /:([\w]+)/g, 61 (_m, name) => params[encodeURIComponent(name)] || 'undefined', 62 ) 63 64 let hasQp = false 65 const qp = new URLSearchParams() 66 for (const paramName in params) { 67 if (!pathParamNames.has(paramName)) { 68 qp.set(paramName, params[paramName]) 69 hasQp = true 70 } 71 } 72 73 return str + (hasQp ? `?${qp.toString()}` : '') 74 }, 75 } 76}