reworked dynamic parameters to be positional rather than fixed

This commit is contained in:
2025-02-03 17:13:04 +03:00
parent 19013984f3
commit 62d9f5a631
3 changed files with 52 additions and 11 deletions

View File

@ -49,6 +49,20 @@ router
);
});
router
.get("/user/:id/:name/*", (c) => {
return c.html(
`id = ${c.params.id}, name = ${c.params.name}, rest = ${c.params.restOfThePath}`,
);
});
router
.get("/user/:idButDifferent", (c) => {
return c.html(
`idButDifferent = ${c.params.idButDifferent}`,
);
});
export default {
async fetch(req, connInfo) {
return await router.handleRequest(req, connInfo);

View File

@ -41,7 +41,6 @@ class HttpRouter {
method: string,
handler: RequestHandler<string>,
): HttpRouter;
add(
path: string | string[],
method: string,
@ -103,7 +102,7 @@ class HttpRouter {
? this.pathPreprocessor(c.path)
: c.path;
let params: Params<string> = {};
let params: string[] = [];
const handler = this.routerTree
.find(path)
@ -152,6 +151,10 @@ class HttpRouter {
return c;
}
private setParams(path: string, params: string[]): Params<string> {
path.split("/").filter((segmet) => segmet.startsWith(":"));
}
}
export type ExtractRouteParams<T extends string> = T extends string

View File

@ -6,6 +6,7 @@ const DEFAULT_PATH_SEPARATOR = "/";
interface Node<T> {
handler: Option<T>;
paramNames: string[];
addChild(
segment: string,
wildcardSymbol: string,
@ -22,6 +23,7 @@ class StaticNode<T> implements Node<T> {
protected dynamicChild: Option<DynamicNode<T>> = none;
protected wildcardChild: Option<WildcardNode<T>> = none;
public handler: Option<T> = none;
public paramNames: string[] = [];
constructor(handler?: T) {
this.handler = fromNullableVal(handler);
@ -33,8 +35,8 @@ class StaticNode<T> implements Node<T> {
return child;
}
setDynamicChild(paramName: string, handler?: T): DynamicNode<T> {
const child = new DynamicNode(paramName, handler);
setDynamicChild(handler?: T): DynamicNode<T> {
const child = new DynamicNode(handler);
this.dynamicChild = some(child);
return child;
}
@ -55,8 +57,7 @@ class StaticNode<T> implements Node<T> {
return this.setWildcardNode(handler);
}
if (segment.startsWith(paramPrefixSymbol)) {
const paramName = segment.slice(paramPrefixSymbol.length);
return this.setDynamicChild(paramName, handler);
return this.setDynamicChild(handler);
}
return this.addStaticChild(segment, handler);
}
@ -91,7 +92,6 @@ class StaticNode<T> implements Node<T> {
// TODO: get rid of fixed param name
class DynamicNode<T> extends StaticNode<T> implements Node<T> {
constructor(
public readonly paramName: string,
handler?: T,
) {
super(handler);
@ -104,6 +104,7 @@ class DynamicNode<T> extends StaticNode<T> implements Node<T> {
class WildcardNode<T> implements Node<T> {
public handler: Option<T>;
public paramNames: string[] = [];
constructor(handler?: T) {
this.handler = fromNullableVal(handler);
@ -144,6 +145,7 @@ export class RouterTree<T> {
public add(path: string, handler: T): void {
const segments = this.splitPath(path);
const paramNames: string[] = this.extractParams(segments);
let current: TreeNode<T> = this.root;
for (const segment of segments) {
@ -157,15 +159,21 @@ export class RouterTree<T> {
)
);
if (current.isWildcardNode()) break;
if (current.isWildcardNode()) {
current.paramNames = paramNames;
current.paramNames.push("restOfThePath");
current.handler = some(handler);
return;
}
}
current.paramNames = paramNames;
current.handler = some(handler);
}
public find(path: string): Option<RouteMatch<T>> {
const segments = this.splitPath(path);
const params: Record<string, string> = {};
const paramValues: string[] = [];
let current: TreeNode<T> = this.root;
let i = 0;
@ -175,7 +183,7 @@ export class RouterTree<T> {
const nextNode = current.getChild(segment).ifSome((child) => {
if (child.isDynamicNode()) {
params[child.paramName] = segment;
paramValues.push(segment);
}
current = child;
});
@ -186,10 +194,16 @@ export class RouterTree<T> {
if (current.isWildcardNode()) {
const rest = segments.slice(i - 1);
if (rest.length > 0) {
params["restOfThePath"] = rest.join(this.pathSeparator);
paramValues.push(rest.join(this.pathSeparator));
}
}
const params: Params = {};
for (let i = 0; i < paramValues.length; i++) {
params[current.paramNames[i]] = paramValues[i];
}
return current.handler.map((value) => ({ value, params }));
}
@ -214,6 +228,16 @@ export class RouterTree<T> {
const trimmed = path.trim().replace(/^\/+/, "").replace(/\/+$/, "");
return trimmed ? trimmed.split(this.pathSeparator) : [];
}
public extractParams(segments: string[]): string[] {
return segments.filter((segment) =>
segment.startsWith(this.paramPrefixSymbol)
).map((segment) => this.stripParamPrefix(segment));
}
public stripParamPrefix(segment: string): string {
return segment.slice(this.paramPrefixSymbol.length);
}
}
export type Params = Record<string, string>;