UNPKG

@eolme/vma-router

Version:
151 lines (117 loc) 3.03 kB
import * as qs from 'querystring'; import { parse as parseToTokens, tokensToRegexp, tokensToFunction } from 'path-to-regexp'; import type * as ptr from 'path-to-regexp'; import type { Page, RouteParams } from '../types'; const PATH_CONFIG = { delimiter: '/', end: false, strict: false, sensitive: false }; type CacheRecordPath = [ptr.PathFunction, Array<ptr.Token>]; type CacheRecordConvert = { regexp: RegExp, keys: Array<ptr.Token> }; type CachePath = Map<Page, CacheRecordPath>; type CacheConvert = Map<Page, CacheRecordConvert>; export declare type MatchedPath = { uri: string, exact: boolean, params: RouteParams }; export declare type ParsedPath = { url: string, params: RouteParams }; const cachedPaths: CachePath = new Map(); const cachedConverts: CacheConvert = new Map(); const cacheLimit = 512; let cacheCount = 0; export function parsePage(page: Page): CacheRecordPath { const cached = cachedPaths.get(page); if (cached) { return cached; } const tokens = parseToTokens(page, PATH_CONFIG); const generator = tokensToFunction(tokens, PATH_CONFIG); if (cacheCount < cacheLimit) { cachedPaths.set(page, [generator, tokens]); cacheCount++; } return [generator, tokens]; } export function buildPage(page: Page, params: RouteParams = {}) { if (page === '/') { return page; } const [generatePath, tokens] = parsePage(page); const path = generatePath(params); const query = { ...params }; tokens.forEach((token) => { if (typeof token === 'object') { delete query[token.name]; } }); return path + '?' + qs.stringify(query); } export function convertPage(page: Page): CacheRecordConvert { const cached = cachedConverts.get(page); if (cached) { return cached; } const tokens = parseToTokens(page, PATH_CONFIG); const keys: ptr.Key[] = []; const regexp = tokensToRegexp(tokens, keys, PATH_CONFIG); const result = { regexp, keys }; if (cacheCount < cacheLimit) { cachedConverts.set(page, result); cacheCount++; } return result; } export function matchPage(page: Page, path: string): MatchedPath { const { regexp, keys } = convertPage(page); const match = regexp.exec(path); if (!match) { return null; } const [url, ...values] = match; const params = keys.reduce((acc, key, index) => { if (typeof key === 'object') { acc[key.name] = values[index]; } return acc; }, {}); const uri = page === '/' && url === '' ? '/' : url; const exact = path === url; return { uri, exact, params }; } export function parsePath(path: string): ParsedPath { if (!path.includes('?')) { return { url: path, params: {} }; } const [url, query] = path.split('?', 2); if (!query) { return { url, params: {} }; } return { url, params: qs.parse(query) }; } export function preventScrollRestoration() { if (window.history.scrollRestoration) { window.history.scrollRestoration = 'manual'; } }