102 lines
3.9 KiB
Plaintext
102 lines
3.9 KiB
Plaintext
import { checkOptionalParameter } from "../../utils/url.js";
|
|
import { MESSAGE_MATCHER_IS_ALREADY_BUILT, UnsupportedPathError } from "../../router.js";
|
|
import { createNullObject } from "../utils.js";
|
|
import { emptyParam, match } from "./matcher.js";
|
|
import { LABEL_REG_EXP_STR, PATH_ERROR, TAIL_WILDCARD_REG_EXP_STR } from "./node.js";
|
|
import { Trie } from "./trie.js";
|
|
//#region src/router/reg-exp-router/router.ts
|
|
let wildcardRegExpCache = createNullObject();
|
|
function buildWildcardRegExp(path) {
|
|
return wildcardRegExpCache[path] ??= new RegExp(`^${path.replace(/\/:[^/{}]+(?:\{\[\^\/]\+})?(?=[/{]|$)|\/?\*$|([.\\+*[^\]$()?{}|])/g, (match, metaChar) => metaChar ? `\\${metaChar}` : match === "/*" ? TAIL_WILDCARD_REG_EXP_STR : match === "*" ? ".*" : `/:${LABEL_REG_EXP_STR}`)}$`);
|
|
}
|
|
function findMiddleware(middleware, path) {
|
|
for (const k of Object.keys(middleware).sort((a, b) => b.length - a.length)) if (buildWildcardRegExp(k).test(path)) return [...middleware[k]];
|
|
}
|
|
var RegExpRouter = class {
|
|
name = "RegExpRouter";
|
|
#middleware;
|
|
#routes;
|
|
#tries;
|
|
constructor() {
|
|
this.#middleware = { ["ALL"]: createNullObject() };
|
|
this.#routes = { ["ALL"]: createNullObject() };
|
|
this.#tries = { ["ALL"]: new Trie() };
|
|
}
|
|
#insertPath(method, path) {
|
|
try {
|
|
this.#tries[method].insert(path, !/\*|\/:/.test(path));
|
|
} catch (e) {
|
|
throw e === PATH_ERROR ? new UnsupportedPathError(path) : e;
|
|
}
|
|
}
|
|
add(method, path, handler) {
|
|
const middleware = this.#middleware;
|
|
const routes = this.#routes;
|
|
if (!middleware) throw new Error(MESSAGE_MATCHER_IS_ALREADY_BUILT);
|
|
if (!middleware[method]) {
|
|
this.#tries[method] = new Trie();
|
|
for (const handlerMap of [middleware, routes]) {
|
|
handlerMap[method] = createNullObject();
|
|
for (const p in handlerMap["ALL"]) {
|
|
handlerMap[method][p] = [...handlerMap["ALL"][p]];
|
|
this.#insertPath(method, p);
|
|
}
|
|
}
|
|
}
|
|
if (path === "/*") path = "*";
|
|
const methods = method === "ALL" ? Object.keys(middleware) : [method];
|
|
if (/\*$/.test(path)) {
|
|
const re = buildWildcardRegExp(path);
|
|
for (const m of methods) if (!middleware[m][path]) {
|
|
this.#insertPath(m, path);
|
|
middleware[m][path] = findMiddleware(middleware[m], path) || findMiddleware(middleware["ALL"], path) || [];
|
|
}
|
|
for (const handlerMap of [middleware, routes]) for (const m of methods) for (const p in handlerMap[m]) re.test(p) && handlerMap[m][p].push([handler, path]);
|
|
return;
|
|
}
|
|
const paths = checkOptionalParameter(path) || [path];
|
|
for (const path of paths) for (const m of methods) {
|
|
if (!routes[m][path]) {
|
|
this.#insertPath(m, path);
|
|
routes[m][path] = findMiddleware(middleware[m], path) || findMiddleware(middleware["ALL"], path) || [];
|
|
}
|
|
routes[m][path].push([handler, path]);
|
|
}
|
|
}
|
|
match = match;
|
|
buildAllMatchers() {
|
|
const matchers = createNullObject();
|
|
for (const method of Object.keys(this.#routes)) matchers[method] = this.#buildMatcher(method);
|
|
this.#middleware = this.#routes = this.#tries = void 0;
|
|
wildcardRegExpCache = createNullObject();
|
|
return matchers;
|
|
}
|
|
#buildMatcher(method) {
|
|
const middleware = this.#middleware[method];
|
|
const routes = this.#routes[method];
|
|
const trie = this.#tries[method];
|
|
const staticMap = createNullObject();
|
|
const handlerData = [];
|
|
const [regexp, indexReplacementMap, paramReplacementMap] = trie.buildRegExp();
|
|
for (const r of [middleware, routes]) for (const path in r) {
|
|
const handlers = r[path];
|
|
const pathData = trie.paths[path];
|
|
if (!pathData) {
|
|
staticMap[path] = [handlers.map(([h]) => [h, createNullObject()]), emptyParam];
|
|
continue;
|
|
}
|
|
handlerData[pathData[0]] = handlers.map(([h, handlerPath]) => [h, trie.paths[handlerPath][1].reduceRight((map, [key], i) => {
|
|
map[key] = paramReplacementMap[pathData[1][i][1]];
|
|
return map;
|
|
}, createNullObject())]);
|
|
}
|
|
return [
|
|
regexp,
|
|
indexReplacementMap.map((i) => handlerData[i]),
|
|
staticMap
|
|
];
|
|
}
|
|
};
|
|
//#endregion
|
|
export { RegExpRouter };
|