Skip to content

Commit

Permalink
refactor: Simplify router types
Browse files Browse the repository at this point in the history
  • Loading branch information
ardalanamini committed Nov 19, 2022
1 parent 4107bf7 commit f9a9bf5
Show file tree
Hide file tree
Showing 17 changed files with 134 additions and 230 deletions.
1 change: 1 addition & 0 deletions .eslintrc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ rules:
import/extensions: off
import/no-relative-parent-imports: off
"@typescript-eslint/no-magic-numbers": off
"@typescript-eslint/consistent-type-imports": off
4 changes: 1 addition & 3 deletions packages/router/src/Handlers.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import { Request as RequestT, Response as ResponseT } from "@foxify/http";
import { NodeHandlersT } from "./constants.js";

export default class Handlers<Request extends RequestT = RequestT,
Response extends ResponseT = ResponseT> implements NodeHandlersT<Request, Response> {
export default class Handlers implements NodeHandlersT {

public readonly ACL = [];

Expand Down
44 changes: 18 additions & 26 deletions packages/router/src/Node.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
import {
MethodT,
Request as RequestT,
Response as ResponseT,
StatusT,
} from "@foxify/http";
import { MethodT, StatusT } from "@foxify/http";
import fastJson from "fast-json-stringify";
import Handlers from "./Handlers.js";
import Options from "./Options.js";
Expand All @@ -21,36 +16,34 @@ import {
WILDCARD_LABEL,
} from "./constants.js";

interface Node<Request extends RequestT = RequestT,
Response extends ResponseT = ResponseT> {
interface Node {
constructor: typeof Node;
}

class Node<Request extends RequestT = RequestT,
Response extends ResponseT = ResponseT> {
class Node {

public allowHeader = "";

public readonly children: NodeChildrenT<Request, Response> = {};
public readonly children: NodeChildrenT = {};

public childrenCount = 0;

public readonly handlers: NodeHandlersT<Request, Response> = (new Handlers<Request, Response>);
public readonly handlers: NodeHandlersT = new Handlers;

public label!: string;

// eslint-disable-next-line no-undefined
public matchAllParamRegExp?: RegExp = undefined;

// eslint-disable-next-line no-undefined
public matchingWildcardNode?: Node<Request, Response> = undefined;
public matchingWildcardNode?: Node = undefined;

public readonly methods: MethodT[] = [];

// eslint-disable-next-line no-undefined
public neighborParamNode?: Node<Request, Response> = undefined;
public neighborParamNode?: Node = undefined;

public readonly options: NodeOptionsT = (new Options);
public readonly options: NodeOptionsT = new Options;

// eslint-disable-next-line no-undefined
public param?: string = undefined;
Expand All @@ -63,17 +56,16 @@ class Node<Request extends RequestT = RequestT,
this.init(prefix);
}

public static isNode<Request extends RequestT = RequestT,
Response extends ResponseT = ResponseT>(value: unknown): value is Node<Request, Response> {
public static isNode(value: unknown): value is Node {
return value instanceof this;
}

public addChild(node: Node<Request, Response>): Node<Request, Response>;
public addChild(prefix?: string): Node<Request, Response>;
public addChild(prefix: Node<Request, Response> | string = "/"): Node<Request, Response> {
let node: Node<Request, Response>;
public addChild(node: Node): Node;
public addChild(prefix?: string): Node;
public addChild(prefix: Node | string = "/"): Node {
let node: Node;

if (Node.isNode<Request, Response>(prefix)) node = prefix;
if (this.constructor.isNode(prefix)) node = prefix;
else node = new this.constructor(prefix);

const label = node.label;
Expand All @@ -88,7 +80,7 @@ class Node<Request extends RequestT = RequestT,
public addHandlers(
method: MethodT,
options: OptionsI,
handlers: Array<HandlerT<Request, Response>>,
handlers: HandlerT[],
): this {
if (handlers.length === 0) return this;

Expand Down Expand Up @@ -131,17 +123,17 @@ class Node<Request extends RequestT = RequestT,
return this;
}

public findChild(path: string, index = 0): Node<Request, Response> | undefined {
public findChild(path: string, index = 0): Node | undefined {
const children = this.children;

return children[path[index]] ?? children[PARAM_LABEL] ?? children[WILDCARD_LABEL];
}

public findChildByLabel(label: string): Node<Request, Response> | undefined {
public findChildByLabel(label: string): Node | undefined {
return this.children[label];
}

public findHandlers(method: MethodT, params: ParamsT = {}): HandlersResultT<Request, Response> {
public findHandlers(method: MethodT, params: ParamsT = {}): HandlersResultT {
const {
handlers: { [method]: handlers },
allowHeader,
Expand Down
134 changes: 65 additions & 69 deletions packages/router/src/Router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import {
METHODS,
MethodT,
NotFound,
Request as RequestT,
Response as ResponseT,
Request,
Response,
STATUS,
StatusT,
} from "@foxify/http";
Expand Down Expand Up @@ -42,82 +42,79 @@ import {

const { compact, deepFlatten } = array;

interface Router<Request extends RequestT = RequestT,
Response extends ResponseT = ResponseT> {
acl: ShortHandRouteT<Request, Response, this>;
bind: ShortHandRouteT<Request, Response, this>;
checkout: ShortHandRouteT<Request, Response, this>;
connect: ShortHandRouteT<Request, Response, this>;
copy: ShortHandRouteT<Request, Response, this>;
delete: ShortHandRouteT<Request, Response, this>;
get: ShortHandRouteT<Request, Response, this>;
head: ShortHandRouteT<Request, Response, this>;
link: ShortHandRouteT<Request, Response, this>;
lock: ShortHandRouteT<Request, Response, this>;
"m-search": ShortHandRouteT<Request, Response, this>;
merge: ShortHandRouteT<Request, Response, this>;
mkactivity: ShortHandRouteT<Request, Response, this>;
mkcalendar: ShortHandRouteT<Request, Response, this>;
mkcol: ShortHandRouteT<Request, Response, this>;
move: ShortHandRouteT<Request, Response, this>;
notify: ShortHandRouteT<Request, Response, this>;
options: ShortHandRouteT<Request, Response, this>;
patch: ShortHandRouteT<Request, Response, this>;
post: ShortHandRouteT<Request, Response, this>;
pri: ShortHandRouteT<Request, Response, this>;
propfind: ShortHandRouteT<Request, Response, this>;
proppatch: ShortHandRouteT<Request, Response, this>;
purge: ShortHandRouteT<Request, Response, this>;
put: ShortHandRouteT<Request, Response, this>;
rebind: ShortHandRouteT<Request, Response, this>;
report: ShortHandRouteT<Request, Response, this>;
search: ShortHandRouteT<Request, Response, this>;
source: ShortHandRouteT<Request, Response, this>;
subscribe: ShortHandRouteT<Request, Response, this>;
trace: ShortHandRouteT<Request, Response, this>;
unbind: ShortHandRouteT<Request, Response, this>;
unlink: ShortHandRouteT<Request, Response, this>;
unlock: ShortHandRouteT<Request, Response, this>;
unsubscribe: ShortHandRouteT<Request, Response, this>;
interface Router {
acl: ShortHandRouteT;
bind: ShortHandRouteT;
checkout: ShortHandRouteT;
connect: ShortHandRouteT;
copy: ShortHandRouteT;
delete: ShortHandRouteT;
get: ShortHandRouteT;
head: ShortHandRouteT;
link: ShortHandRouteT;
lock: ShortHandRouteT;
"m-search": ShortHandRouteT;
merge: ShortHandRouteT;
mkactivity: ShortHandRouteT;
mkcalendar: ShortHandRouteT;
mkcol: ShortHandRouteT;
move: ShortHandRouteT;
notify: ShortHandRouteT;
options: ShortHandRouteT;
patch: ShortHandRouteT;
post: ShortHandRouteT;
pri: ShortHandRouteT;
propfind: ShortHandRouteT;
proppatch: ShortHandRouteT;
purge: ShortHandRouteT;
put: ShortHandRouteT;
rebind: ShortHandRouteT;
report: ShortHandRouteT;
search: ShortHandRouteT;
source: ShortHandRouteT;
subscribe: ShortHandRouteT;
trace: ShortHandRouteT;
unbind: ShortHandRouteT;
unlink: ShortHandRouteT;
unlock: ShortHandRouteT;
unsubscribe: ShortHandRouteT;
}

class Router<Request extends RequestT = RequestT,
Response extends ResponseT = ResponseT> {
class Router {

protected catchers: Array<ErrorHandlerT<Request, Response>> = [];
protected catchers: ErrorHandlerT[] = [];

protected middlewares: Array<HandlerT<Request, Response>> = [];
protected middlewares: HandlerT[] = [];

protected readonly paramHandlers: ParamHandlerI<Request, Response> = {};
protected readonly paramHandlers: ParamHandlerI = {};

protected readonly tree = (new Node<Request, Response>);
protected readonly tree = new Node;

public constructor(protected readonly prefix = "/") {}

public all(
path: string,
options?:
HandlersT<Request, Response> | HandlerT<Request, Response> | OptionsI | false | null,
...handlers: HandlersT<Request, Response>
options?: HandlersT | HandlerT | OptionsI | false | null,
...handlers: HandlersT
): this {
return this.on(METHODS, path, options, handlers);
}

public catch(...handlers: ErrorHandlersT<Request, Response>): this {
public catch(...handlers: ErrorHandlersT): this {
handlers = compact(deepFlatten(handlers));

if (handlers.length === 0) return this;

this.catchers = this.catchers.concat(handlers as Array<ErrorHandlerT<Request, Response>>);
this.catchers = this.catchers.concat(handlers as ErrorHandlerT[]);

return this;
}

public find(
method: MethodT,
path: string,
): HandlersResultT<Request, Response> {
let node: Node<Request, Response> | undefined = this.tree;
): HandlersResultT {
let node: Node | undefined = this.tree;
let position = 0;

if (path.startsWith("/")) {
Expand Down Expand Up @@ -214,17 +211,16 @@ class Router<Request extends RequestT = RequestT,
public on(
method: MethodT | MethodT[],
path: string,
options:
HandlersT<Request, Response> | HandlerT<Request, Response> | OptionsI | false | null | undefined = {},
...handlers: HandlersT<Request, Response>
options: HandlersT | HandlerT | OptionsI | false | null | undefined = {},
...handlers: HandlersT
): this {
if (
options == null
|| options === false
|| typeof options === "function"
|| Array.isArray(options)
) {
handlers = [options as HandlerT<Request, Response>, ...handlers];
handlers = [options as HandlerT, ...handlers];
options = {};
}

Expand All @@ -248,10 +244,10 @@ class Router<Request extends RequestT = RequestT,

const params: string[] = [];

let node: Node<Request, Response> | undefined = this.tree;
let node: Node | undefined = this.tree;

let currentNode: Node<Request, Response> = node;
let matchAllNode: Node<Request, Response> | undefined;
let currentNode: Node = node;
let matchAllNode: Node | undefined;

// eslint-disable-next-line no-constant-condition,@typescript-eslint/no-unnecessary-condition
while (true) {
Expand Down Expand Up @@ -288,7 +284,7 @@ class Router<Request extends RequestT = RequestT,
node.addHandlers(
method,
options,
middlewares.concat(handlers as Array<HandlerT<Request, Response>>),
middlewares.concat(handlers as HandlerT[]),
);

return this;
Expand Down Expand Up @@ -381,7 +377,7 @@ class Router<Request extends RequestT = RequestT,
}
}

public param(name: string, handler: HandlerT<Request, Response>): this {
public param(name: string, handler: HandlerT): this {
const { paramHandlers } = this;

// eslint-disable-next-line no-undefined
Expand All @@ -399,8 +395,8 @@ class Router<Request extends RequestT = RequestT,
return prettyPrint(this.tree, "", true);
}

public route(path: string): RouteMethodsT<Request, Response> {
const ROUTER = METHODS.reduce<RouteMethodsT<Request, Response>>((router, method) => {
public route(path: string): RouteMethodsT {
const ROUTER = METHODS.reduce<RouteMethodsT>((router, method) => {
const name = method.toLowerCase() as Lowercase<MethodT>;

router[name] = (options, ...handlers): any => {
Expand All @@ -416,12 +412,12 @@ class Router<Request extends RequestT = RequestT,
return ROUTER;
}

public use(...handlers: MiddlewaresT<Request, Response>): this {
public use(...handlers: MiddlewaresT): this {
handlers = compact(deepFlatten(handlers));

if (handlers.length === 0) return this;

const routers: Array<Router<Request, Response>> = handlers.filter(handler => handler instanceof Router) as any;
const routers: Router[] = handlers.filter(handler => handler instanceof Router) as any;

for (const router of routers) {
// eslint-disable-next-line @typescript-eslint/no-shadow
Expand All @@ -431,7 +427,7 @@ class Router<Request extends RequestT = RequestT,
for (const [method, path, options, handlers] of routes) this.on(method, path, options, handlers);
}

const middlewares: Array<HandlerT<Request, Response>> = handlers
const middlewares: HandlerT[] = handlers
.filter(handler => !(handler instanceof Router)) as any;

if (middlewares.length > 0) this.middlewares = this.middlewares.concat(middlewares);
Expand Down Expand Up @@ -517,7 +513,7 @@ class Router<Request extends RequestT = RequestT,
error: Error,
req: Request,
res: Response,
catchers: Array<ErrorHandlerT<Request, Response>> = [],
catchers: ErrorHandlerT[] = [],
): NextT {
const length = catchers.length;
let index = 0;
Expand Down Expand Up @@ -555,7 +551,7 @@ class Router<Request extends RequestT = RequestT,
res: Response,
method: MethodT,
allowHeader: string,
handlers: Array<HandlerT<Request, Response>> = [],
handlers: HandlerT[] = [],
): NextT {
const length = handlers.length;
let index = 0;
Expand Down Expand Up @@ -593,7 +589,7 @@ class Router<Request extends RequestT = RequestT,
return next;
}

protected routes(): RoutesT<Request, Response> {
protected routes(): RoutesT {
return routes(this.tree);
}

Expand Down
Loading

0 comments on commit f9a9bf5

Please sign in to comment.