reworked dynamic parameters to be positional rather than fixed
This commit is contained in:
@ -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 {
|
export default {
|
||||||
async fetch(req, connInfo) {
|
async fetch(req, connInfo) {
|
||||||
return await router.handleRequest(req, connInfo);
|
return await router.handleRequest(req, connInfo);
|
||||||
|
|||||||
@ -41,7 +41,6 @@ class HttpRouter {
|
|||||||
method: string,
|
method: string,
|
||||||
handler: RequestHandler<string>,
|
handler: RequestHandler<string>,
|
||||||
): HttpRouter;
|
): HttpRouter;
|
||||||
|
|
||||||
add(
|
add(
|
||||||
path: string | string[],
|
path: string | string[],
|
||||||
method: string,
|
method: string,
|
||||||
@ -103,7 +102,7 @@ class HttpRouter {
|
|||||||
? this.pathPreprocessor(c.path)
|
? this.pathPreprocessor(c.path)
|
||||||
: c.path;
|
: c.path;
|
||||||
|
|
||||||
let params: Params<string> = {};
|
let params: string[] = [];
|
||||||
|
|
||||||
const handler = this.routerTree
|
const handler = this.routerTree
|
||||||
.find(path)
|
.find(path)
|
||||||
@ -152,6 +151,10 @@ class HttpRouter {
|
|||||||
|
|
||||||
return c;
|
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
|
export type ExtractRouteParams<T extends string> = T extends string
|
||||||
|
|||||||
@ -6,6 +6,7 @@ const DEFAULT_PATH_SEPARATOR = "/";
|
|||||||
|
|
||||||
interface Node<T> {
|
interface Node<T> {
|
||||||
handler: Option<T>;
|
handler: Option<T>;
|
||||||
|
paramNames: string[];
|
||||||
addChild(
|
addChild(
|
||||||
segment: string,
|
segment: string,
|
||||||
wildcardSymbol: string,
|
wildcardSymbol: string,
|
||||||
@ -22,6 +23,7 @@ class StaticNode<T> implements Node<T> {
|
|||||||
protected dynamicChild: Option<DynamicNode<T>> = none;
|
protected dynamicChild: Option<DynamicNode<T>> = none;
|
||||||
protected wildcardChild: Option<WildcardNode<T>> = none;
|
protected wildcardChild: Option<WildcardNode<T>> = none;
|
||||||
public handler: Option<T> = none;
|
public handler: Option<T> = none;
|
||||||
|
public paramNames: string[] = [];
|
||||||
|
|
||||||
constructor(handler?: T) {
|
constructor(handler?: T) {
|
||||||
this.handler = fromNullableVal(handler);
|
this.handler = fromNullableVal(handler);
|
||||||
@ -33,8 +35,8 @@ class StaticNode<T> implements Node<T> {
|
|||||||
return child;
|
return child;
|
||||||
}
|
}
|
||||||
|
|
||||||
setDynamicChild(paramName: string, handler?: T): DynamicNode<T> {
|
setDynamicChild(handler?: T): DynamicNode<T> {
|
||||||
const child = new DynamicNode(paramName, handler);
|
const child = new DynamicNode(handler);
|
||||||
this.dynamicChild = some(child);
|
this.dynamicChild = some(child);
|
||||||
return child;
|
return child;
|
||||||
}
|
}
|
||||||
@ -55,8 +57,7 @@ class StaticNode<T> implements Node<T> {
|
|||||||
return this.setWildcardNode(handler);
|
return this.setWildcardNode(handler);
|
||||||
}
|
}
|
||||||
if (segment.startsWith(paramPrefixSymbol)) {
|
if (segment.startsWith(paramPrefixSymbol)) {
|
||||||
const paramName = segment.slice(paramPrefixSymbol.length);
|
return this.setDynamicChild(handler);
|
||||||
return this.setDynamicChild(paramName, handler);
|
|
||||||
}
|
}
|
||||||
return this.addStaticChild(segment, handler);
|
return this.addStaticChild(segment, handler);
|
||||||
}
|
}
|
||||||
@ -91,7 +92,6 @@ class StaticNode<T> implements Node<T> {
|
|||||||
// TODO: get rid of fixed param name
|
// TODO: get rid of fixed param name
|
||||||
class DynamicNode<T> extends StaticNode<T> implements Node<T> {
|
class DynamicNode<T> extends StaticNode<T> implements Node<T> {
|
||||||
constructor(
|
constructor(
|
||||||
public readonly paramName: string,
|
|
||||||
handler?: T,
|
handler?: T,
|
||||||
) {
|
) {
|
||||||
super(handler);
|
super(handler);
|
||||||
@ -104,6 +104,7 @@ class DynamicNode<T> extends StaticNode<T> implements Node<T> {
|
|||||||
|
|
||||||
class WildcardNode<T> implements Node<T> {
|
class WildcardNode<T> implements Node<T> {
|
||||||
public handler: Option<T>;
|
public handler: Option<T>;
|
||||||
|
public paramNames: string[] = [];
|
||||||
|
|
||||||
constructor(handler?: T) {
|
constructor(handler?: T) {
|
||||||
this.handler = fromNullableVal(handler);
|
this.handler = fromNullableVal(handler);
|
||||||
@ -144,6 +145,7 @@ export class RouterTree<T> {
|
|||||||
|
|
||||||
public add(path: string, handler: T): void {
|
public add(path: string, handler: T): void {
|
||||||
const segments = this.splitPath(path);
|
const segments = this.splitPath(path);
|
||||||
|
const paramNames: string[] = this.extractParams(segments);
|
||||||
let current: TreeNode<T> = this.root;
|
let current: TreeNode<T> = this.root;
|
||||||
|
|
||||||
for (const segment of segments) {
|
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);
|
current.handler = some(handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
public find(path: string): Option<RouteMatch<T>> {
|
public find(path: string): Option<RouteMatch<T>> {
|
||||||
const segments = this.splitPath(path);
|
const segments = this.splitPath(path);
|
||||||
const params: Record<string, string> = {};
|
const paramValues: string[] = [];
|
||||||
let current: TreeNode<T> = this.root;
|
let current: TreeNode<T> = this.root;
|
||||||
let i = 0;
|
let i = 0;
|
||||||
|
|
||||||
@ -175,7 +183,7 @@ export class RouterTree<T> {
|
|||||||
|
|
||||||
const nextNode = current.getChild(segment).ifSome((child) => {
|
const nextNode = current.getChild(segment).ifSome((child) => {
|
||||||
if (child.isDynamicNode()) {
|
if (child.isDynamicNode()) {
|
||||||
params[child.paramName] = segment;
|
paramValues.push(segment);
|
||||||
}
|
}
|
||||||
current = child;
|
current = child;
|
||||||
});
|
});
|
||||||
@ -186,10 +194,16 @@ export class RouterTree<T> {
|
|||||||
if (current.isWildcardNode()) {
|
if (current.isWildcardNode()) {
|
||||||
const rest = segments.slice(i - 1);
|
const rest = segments.slice(i - 1);
|
||||||
if (rest.length > 0) {
|
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 }));
|
return current.handler.map((value) => ({ value, params }));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -214,6 +228,16 @@ export class RouterTree<T> {
|
|||||||
const trimmed = path.trim().replace(/^\/+/, "").replace(/\/+$/, "");
|
const trimmed = path.trim().replace(/^\/+/, "").replace(/\/+$/, "");
|
||||||
return trimmed ? trimmed.split(this.pathSeparator) : [];
|
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>;
|
export type Params = Record<string, string>;
|
||||||
|
|||||||
Reference in New Issue
Block a user