type RouteHandler = (params: Record) => void interface Route { pattern: RegExp keys: string[] handler: RouteHandler } const routes: Route[] = [] export function route(path: string, handler: RouteHandler): void { const keys: string[] = [] const pattern = new RegExp( '^' + path.replace(/:([^/]+)/g, (_: string, k: string) => { keys.push(k); return '([^/]+)' }) + '$' ) routes.push({ pattern, keys, handler }) } export function navigate(path: string): void { history.pushState(null, '', path) dispatch(path) } function dispatch(path: string): void { for (const r of routes) { const m = path.match(r.pattern) if (m) { const params: Record = {} r.keys.forEach((k, i) => { params[k] = m[i + 1] }) r.handler(params) return } } } export function startRouter(): void { window.addEventListener('popstate', () => dispatch(location.pathname)) dispatch(location.pathname) }