refactor router to use array for route storage and improve route handling

This commit is contained in:
Flummi 2025-03-17 07:09:03 +01:00
parent ce9f313220
commit 566a1b671c
4 changed files with 74 additions and 32 deletions

5
dist/index.js vendored
View File

@ -50,9 +50,8 @@ export default class Flummpress {
yield this.processPipeline(this.middleware, req, res); yield this.processPipeline(this.middleware, req, res);
const route = this.router.getRoute(req.url.pathname, req.method); const route = this.router.getRoute(req.url.pathname, req.method);
if (route) { if (route) {
const [pathPattern, methods] = route; const handler = route.methods[(_a = req.method) === null || _a === void 0 ? void 0 : _a.toLowerCase()];
const handler = methods[(_a = req.method) === null || _a === void 0 ? void 0 : _a.toLowerCase()]; req.params = ((_b = req.url.pathname.match(new RegExp(route.path))) === null || _b === void 0 ? void 0 : _b.groups) || {};
req.params = ((_b = req.url.pathname.match(new RegExp(pathPattern))) === null || _b === void 0 ? void 0 : _b.groups) || {};
req.post = yield this.readBody(req); req.post = yield this.readBody(req);
yield this.processPipeline(handler, req, res); yield this.processPipeline(handler, req, res);
} }

46
dist/router.js vendored
View File

@ -12,7 +12,8 @@ import path from "node:path";
import Tpl from "./template.js"; import Tpl from "./template.js";
export default class Router { export default class Router {
constructor() { constructor() {
this.routes = new Map(); this.routes = [];
this.routes;
this.mimes = new Map(); this.mimes = new Map();
} }
importRoutesFromPath(p_1) { importRoutesFromPath(p_1) {
@ -70,7 +71,6 @@ export default class Router {
} }
use(obj) { use(obj) {
if (obj instanceof Router) { if (obj instanceof Router) {
this.routes = new Map([...this.routes, ...obj.routes]);
this.sortRoutes(); this.sortRoutes();
} }
if (obj instanceof Tpl) { if (obj instanceof Tpl) {
@ -102,27 +102,47 @@ export default class Router {
return this; return this;
} }
registerRoute(path, method, handler) { registerRoute(path, method, handler) {
var _a; const existingRoute = this.routes.find(route => typeof route.path === "string"
const route = (_a = this.routes.get(path)) !== null && _a !== void 0 ? _a : { ? route.path === path
[method]: handler : route.path instanceof RegExp && path instanceof RegExp && route.path.toString() === path.toString());
}; if (existingRoute) {
this.routes.set(path, route); existingRoute.methods[method] = [
...(existingRoute.methods[method] || []),
...handler
];
}
else {
this.routes.push({
path,
methods: { [method]: handler }
});
}
console.log("route set:", method.toUpperCase(), path); console.log("route set:", method.toUpperCase(), path);
this.sortRoutes(); this.sortRoutes();
return this; return this;
} }
getRoute(path, method) { getRoute(path, method) {
return [...this.routes.entries()] return this.routes
.find(r => { .find(r => {
var _a, _b; var _a, _b;
return typeof r[0] === "string" return typeof r.path === "string"
? r[0] === path ? r.path === path
: ((_b = (_a = r[0]).exec) === null || _b === void 0 ? void 0 : _b.call(_a, path)) : ((_b = (_a = r.path).exec) === null || _b === void 0 ? void 0 : _b.call(_a, path))
&& r[1][method.toLowerCase()]; && r.methods[method.toLowerCase()];
}); });
} }
sortRoutes() { sortRoutes() {
this.routes = new Map([...this.routes.entries()].sort().reverse()); this.routes.sort((a, b) => {
const aLength = typeof a.path === "string" ? a.path.length : a.path.source.length;
const bLength = typeof b.path === "string" ? b.path.length : b.path.source.length;
if (typeof a.path === "string" && typeof b.path === "string")
return bLength - aLength;
if (typeof a.path === "string")
return -1;
if (typeof b.path === "string")
return 1;
return bLength - aLength;
});
return this; return this;
} }
readMimes(file = "/etc/mime.types") { readMimes(file = "/etc/mime.types") {

View File

@ -89,10 +89,9 @@ export default class Flummpress {
const route = this.router.getRoute(req.url.pathname, req.method!); const route = this.router.getRoute(req.url.pathname, req.method!);
if(route) { if(route) {
const [pathPattern, methods] = route; const handler = route.methods[req.method?.toLowerCase()!];
const handler = methods[req.method?.toLowerCase()!];
req.params = req.url.pathname.match(new RegExp(pathPattern))?.groups || {}; req.params = req.url.pathname.match(new RegExp(route.path))?.groups || {};
req.post = await this.readBody(req); req.post = await this.readBody(req);
await this.processPipeline(handler, req, res); await this.processPipeline(handler, req, res);
} }

View File

@ -5,12 +5,11 @@ import Tpl from "./template.js";
import { Request, Response, Handler } from "./types.js"; import { Request, Response, Handler } from "./types.js";
export default class Router { export default class Router {
private routes: Map<RegExp | string, { [key: string]: Handler[] }>; private routes: Array<{ path: string | RegExp; methods: { [method: string]: Handler[] } }> = [];
private tpl?: Tpl; private tpl?: Tpl;
private mimes: Map<string, string>; private mimes: Map<string, string>;
constructor() { constructor() {
this.routes = new Map();
this.mimes = new Map(); this.mimes = new Map();
} }
@ -96,7 +95,7 @@ export default class Router {
*/ */
use(obj: Router | Tpl): void { use(obj: Router | Tpl): void {
if(obj instanceof Router) { if(obj instanceof Router) {
this.routes = new Map([...this.routes, ...obj.routes]); this.routes = { ...this.routes, ...obj.routes };
this.sortRoutes(); this.sortRoutes();
} }
if(obj instanceof Tpl) { if(obj instanceof Tpl) {
@ -181,10 +180,24 @@ export default class Router {
method: string, method: string,
handler: Handler[] handler: Handler[]
): this { ): this {
const route: { [key: string]: Handler[] } = this.routes.get(path) ?? { const route = this.routes.find(route =>
[ method ]: handler typeof route.path === "string"
}; ? route.path === path
this.routes.set(path, route); : route.path instanceof RegExp && path instanceof RegExp && route.path.toString() === path.toString()
);
if(route) {
route.methods[method] = [
...(route.methods[method] || []),
...handler
];
}
else {
this.routes.push({
path,
methods: { [method]: handler }
});
}
console.log("route set:", method.toUpperCase(), path); console.log("route set:", method.toUpperCase(), path);
this.sortRoutes(); this.sortRoutes();
@ -198,11 +211,11 @@ export default class Router {
* @returns The matching route or undefined. * @returns The matching route or undefined.
*/ */
getRoute(path: string, method: string): any { getRoute(path: string, method: string): any {
return [...this.routes.entries()] return this.routes
.find(r => typeof r[0] === "string" .find(r => typeof r.path === "string"
? r[0] === path ? r.path === path
: r[0].exec?.(path) : r.path.exec?.(path)
&& r[1][method.toLowerCase()] && r.methods[method.toLowerCase()]
); );
} }
@ -211,7 +224,18 @@ export default class Router {
* @returns The Router instance for chaining. * @returns The Router instance for chaining.
*/ */
private sortRoutes(): this { private sortRoutes(): this {
this.routes = new Map([...this.routes.entries()].sort().reverse()); this.routes.sort((a, b) => {
const aLength = typeof a.path === "string" ? a.path.length : a.path.source.length;
const bLength = typeof b.path === "string" ? b.path.length : b.path.source.length;
if(typeof a.path === "string" && typeof b.path === "string")
return bLength - aLength;
if(typeof a.path === "string")
return -1;
if(typeof b.path === "string")
return 1;
return bLength - aLength;
});
return this; return this;
} }