Skip to content

Commit db9fcf2

Browse files
committed
fix(compiler): avoid duplicate static checks
1 parent ed4a95d commit db9fcf2

File tree

2 files changed

+62
-70
lines changed

2 files changed

+62
-70
lines changed

src/compiler.ts

Lines changed: 55 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,5 @@
11
import type { MatchedRoute, MethodData, Node, RouterContext } from "./types.ts";
22

3-
// p: path
4-
// s: path parts
5-
// l: path parts length
6-
// m: method
7-
// e: Empty group
8-
93
/**
104
* Compiles the router instance into a faster route-matching function.
115
*
@@ -28,7 +22,7 @@ export function compileRouter<T>(
2822
router: RouterContext<T>,
2923
): (method: string, path: string) => MatchedRoute<T> | undefined {
3024
const deps: any[] = [];
31-
const compiled = _compileRouteMatch(router, deps);
25+
const compiled = compileRouteMatch(router, deps);
3226
return new Function(
3327
...deps.map((_, i) => "d" + (i + 1)),
3428
`let e={groups:{}};return(m,p)=>{${compiled}}`,
@@ -37,15 +31,39 @@ export function compileRouter<T>(
3731

3832
// ------- internal functions -------
3933

34+
// p: path
35+
// s: path parts
36+
// l: path parts length
37+
// m: method
38+
// e: Empty group
39+
4040
/**
41-
* Whether the current node has children nodes
42-
* @param n
41+
* Compile a router to pattern matching statements
42+
* @param router
43+
* @param deps - Dependencies of the function scope
4344
*/
44-
function _hasChild(n: Node<any>): boolean {
45-
return n.static != null || n.param != null || n.wildcard != null;
45+
function compileRouteMatch(router: RouterContext<any>, deps: any[]): string {
46+
// Ignore trailing slash
47+
let str = `if(p[p.length-1]==='/')p=p.slice(0,-1)||'/';`;
48+
49+
const staticNodes = new Set<Node>();
50+
51+
for (const key in router.static) {
52+
const node = router.static[key];
53+
if (node?.methods) {
54+
staticNodes.add(node);
55+
str += `if(p===${JSON.stringify(key.replace(/\/$/, "") || "/")}){${compileMethodMatch(node.methods, [], deps, -1)}}`;
56+
}
57+
}
58+
59+
return (
60+
str +
61+
"let s=p.split('/').filter(q=>q!==''),l=s.length;" +
62+
compileNode(router.root, [], 0, deps, false, staticNodes)
63+
);
4664
}
4765

48-
function _compileMethodMatch(
66+
function compileMethodMatch(
4967
methods: Record<string, MethodData<any>[] | undefined>,
5068
params: string[],
5169
deps: any[],
@@ -54,14 +72,14 @@ function _compileMethodMatch(
5472
let str = "";
5573
for (const key in methods) {
5674
const data = methods[key];
57-
if (data != null && data.length > 0) {
75+
if (data && data?.length > 0) {
5876
// Don't check for all method handler
59-
if (key !== "") str += `if(m==="${key}")`;
77+
if (key !== "") str += `if(m==='${key}')`;
6078
let returnData = `return{data:d${deps.push(data[0].data)}`;
6179

6280
// Add param properties
6381
const { paramsMap } = data[0];
64-
if (paramsMap != null && paramsMap.length > 0) {
82+
if (paramsMap && paramsMap.length > 0) {
6583
// Check for optional end parameters
6684
const required =
6785
!paramsMap[paramsMap.length - 1][2] && currentIdx !== -1;
@@ -89,44 +107,51 @@ function _compileMethodMatch(
89107
/**
90108
* Compile a node to matcher logic
91109
*/
92-
function _compileNode(
110+
function compileNode(
93111
node: Node<any>,
94112
params: string[],
95113
startIdx: number,
96114
deps: any[],
97115
isParamNode: boolean,
116+
staticNodes: Set<Node>,
98117
): string {
99118
let str = "";
100119

101-
if (node.methods != null)
102-
str += `if(l===${startIdx}${isParamNode ? `||l===${startIdx - 1}` : ""}){${_compileMethodMatch(node.methods, params, deps, isParamNode ? startIdx : -1)}}`;
120+
if (node.methods && !staticNodes.has(node)) {
121+
str += `if(l===${startIdx}${isParamNode ? `||l===${startIdx - 1}` : ""}){${compileMethodMatch(node.methods, params, deps, isParamNode ? startIdx : -1)}}`;
122+
}
103123

104-
if (node.static != null)
124+
if (node.static) {
105125
for (const key in node.static)
106-
str += `if(s[${startIdx}]===${JSON.stringify(key)}){${_compileNode(
126+
str += `if(s[${startIdx}]===${JSON.stringify(key)}){${compileNode(
107127
node.static[key],
108128
params,
109129
startIdx + 1,
110130
deps,
111131
false,
132+
staticNodes,
112133
)}}`;
134+
}
113135

114-
if (node.param != null)
115-
str += _compileNode(
136+
if (node.param) {
137+
str += compileNode(
116138
node.param,
117139
[...params, `s[${startIdx}]`],
118140
startIdx + 1,
119141
deps,
120142
true,
143+
staticNodes,
121144
);
145+
}
122146

123-
if (node.wildcard != null) {
147+
if (node.wildcard) {
124148
const { wildcard } = node;
125-
if (_hasChild(wildcard))
149+
if (hasChild(wildcard)) {
126150
throw new Error("Compiler mode does not support patterns after wildcard");
151+
}
127152

128-
if (wildcard.methods != null)
129-
str += _compileMethodMatch(
153+
if (wildcard.methods)
154+
str += compileMethodMatch(
130155
wildcard.methods,
131156
[...params, `s.slice(${startIdx}).join('/')`],
132157
deps,
@@ -138,23 +163,9 @@ function _compileNode(
138163
}
139164

140165
/**
141-
* Compile a router to pattern matching statements
142-
* @param router
143-
* @param deps - Dependencies of the function scope
166+
* Whether the current node has children nodes
167+
* @param n
144168
*/
145-
function _compileRouteMatch(router: RouterContext<any>, deps: any[]): string {
146-
// Support trailing slash
147-
let str = "if(p[p.length-1]==='/')p=p.slice(0,-1);";
148-
149-
for (const key in router.static) {
150-
const node = router.static[key];
151-
if (node != null && node.methods != null)
152-
str += `if(p===${JSON.stringify(key)}){${_compileMethodMatch(node.methods, [], deps, -1)}}`;
153-
}
154-
155-
return (
156-
str +
157-
"let s=p.split('/').filter(q=>q!==''),l=s.length;" +
158-
_compileNode(router.root, [], 0, deps, false)
159-
);
169+
function hasChild(n: Node<any>): boolean {
170+
return !!(n.static || n.param || n.wildcard);
160171
}

test/.snapshot/compiler.mjs

Lines changed: 7 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
(m, p) => {
2-
if (p[p.length - 1] === "/") p = p.slice(0, -1);
2+
if (p[p.length - 1] === "/") p = p.slice(0, -1) || "/";
33
if (p === "/test") {
44
if (m === "GET") return { data: d1 };
55
}
@@ -21,58 +21,39 @@
2121
let s = p.split("/").filter((q) => q !== ""),
2222
l = s.length;
2323
if (s[0] === "test") {
24-
if (l === 1) {
25-
if (m === "GET") return { data: d7 };
26-
}
2724
if (s[1] === "foo") {
28-
if (l === 2) {
29-
if (m === "GET") return { data: d8 };
30-
}
3125
if (s[2] === "bar") {
3226
if (s[3] === "qux") {
33-
if (l === 4) {
34-
if (m === "GET") return { data: d9 };
35-
}
3627
}
3728
}
3829
if (s[2] === "baz") {
39-
if (l === 3) {
40-
if (m === "GET") return { data: d10 };
41-
}
4230
}
4331
if (l === 3 || l === 2) {
44-
if (m === "GET") return { data: d11, params: { _0: s[2] } };
32+
if (m === "GET") return { data: d7, params: { _0: s[2] } };
4533
}
46-
if (m === "GET")
47-
return { data: d12, params: { _: s.slice(2).join("/") } };
34+
if (m === "GET") return { data: d8, params: { _: s.slice(2).join("/") } };
4835
}
4936
if (s[1] === "fooo") {
50-
if (l === 2) {
51-
if (m === "GET") return { data: d13 };
52-
}
5337
}
5438
if (l === 2 || l === 1) {
55-
if (m === "GET") if (l >= 2) return { data: d14, params: { id: s[1] } };
39+
if (m === "GET") if (l >= 2) return { data: d9, params: { id: s[1] } };
5640
}
5741
if (s[2] === "y") {
5842
if (l === 3) {
59-
if (m === "GET") return { data: d15, params: { idY: s[1] } };
43+
if (m === "GET") return { data: d10, params: { idY: s[1] } };
6044
}
6145
if (s[3] === "z") {
6246
if (l === 4) {
63-
if (m === "GET") return { data: d16, params: { idYZ: s[1] } };
47+
if (m === "GET") return { data: d11, params: { idYZ: s[1] } };
6448
}
6549
}
6650
}
6751
}
6852
if (s[0] === "another") {
6953
if (s[1] === "path") {
70-
if (l === 2) {
71-
if (m === "GET") return { data: d17 };
72-
}
7354
}
7455
}
7556
if (s[0] === "wildcard") {
76-
if (m === "GET") return { data: d18, params: { _: s.slice(1).join("/") } };
57+
if (m === "GET") return { data: d12, params: { _: s.slice(1).join("/") } };
7758
}
7859
};

0 commit comments

Comments
 (0)