Files
SnowLuma/.pnpm-store/v11/files/5d/03af3f6122da60219335275c7e581d15d069a7d5f0512ef0dcb348057ffb857b5c37e7d1a46baa920bdad264c0b0a4ef82b87c0db6f633c03aae9b6a45b9f7
T
TanTan 2a69b23b47
Dev Build / cleanup (push) Waiting to run
Dev Build / typecheck (push) Waiting to run
docs: 仓库链接指向本镜像仓库
2026-11-13 09:27:07 +08:00

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 };