@exlinep/router
Version:
Router for VKUI
121 lines (103 loc) • 3.09 kB
text/typescript
/**
* @ignore
* @packageDocumentation
*/
import * as ptr from 'path-to-regexp';
const cache: Map<string, [ptr.PathFunction, ptr.Token[]]> = new Map<string, [ptr.PathFunction, ptr.Token[]]>();
const cacheLimit = 10000;
let cacheCount = 0;
function parsePath(pageId: string): [ptr.PathFunction, ptr.Token[]] {
const cached = cache.get(pageId);
if (cached) {
return cached;
}
const tokens = ptr.parse(pageId);
const generator = ptr.tokensToFunction(tokens);
if (cacheCount < cacheLimit) {
cache.set(pageId, [generator, tokens]);
cacheCount++;
}
return [generator, tokens];
}
interface CachedPath {
regexp: RegExp;
keys: ptr.Token[];
}
const convertCache = new Map<string, CachedPath>();
function convertPath(path: string, options?: ptr.TokensToRegexpOptions & ptr.ParseOptions): CachedPath {
const cacheKey = `${options?.end ? '1' : '0'}${options?.strict}${options?.sensitive}${path}`;
const pathCache = convertCache.get(cacheKey);
if (pathCache) {
return pathCache;
}
const keys: ptr.Key[] = [];
const regexp = ptr.pathToRegexp(path, keys);
const result = { regexp, keys };
if (cacheCount < cacheLimit) {
convertCache.set(cacheKey, result);
cacheCount++;
}
return result;
}
/**
* Создание пути из шаблона
* @param pageId /user/:id
* @param params {id:5,name:Ivan}
* @return {string} /user/5?name=Ivan
* @ignore
*/
export function generatePath(pageId: string, params?: {}): string {
if (!params) {
params = {};
}
params = { ...params };
const [generatePath, tokens] = parsePath(pageId);
const path = generatePath(params);
const restParams: { [key: string]: any } = { ...params };
tokens.forEach((t) => {
if (typeof t === 'object') {
delete restParams[t.name.toString()];
}
});
const result = `${path}?${new URLSearchParams(restParams).toString()}`;
return result.replace(/\?$/gmu, '');
}
/**
* @ignore
*/
export interface MatchInterface {
isExact: boolean;
path: string;
url: string;
params: { [key: string]: string };
}
/**
* Проверка что строка удовлетворяет шаблону
* @param location /user/5
* @param pageId /user/:id([0-9]+)
* @ignore
*/
export function matchPath(location: string, pageId: string): null | MatchInterface {
const { regexp, keys } = convertPath(pageId, {
end: false,
strict: false,
sensitive: false,
});
let match = regexp.exec(location);
if (!match) {
return null;
}
const [url, ...values] = match;
const isExact = location === url;
return {
path: pageId, // the path used to match
url: pageId === '/' && url === '' ? '/' : url, // the matched portion of the URL
isExact, // whether or not we matched exactly
params: keys.reduce<{ [key: string]: string }>((memo, key, index) => {
if (typeof key === 'object') {
memo[key.name] = values[index];
}
return memo;
}, {}),
};
}