@shuvi/router 2.0.0-dev.18 → 2.0.0-dev.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm/router.d.ts +0 -1
- package/esm/router.js +2 -5
- package/lib/router.d.ts +0 -1
- package/lib/router.js +2 -5
- package/package.json +2 -2
- package/esm/matchStaticRoutes.d.ts +0 -18
- package/esm/matchStaticRoutes.js +0 -210
- package/lib/matchStaticRoutes.d.ts +0 -18
- package/lib/matchStaticRoutes.js +0 -213
package/esm/router.d.ts
CHANGED
|
@@ -4,7 +4,6 @@ interface IRouterOptions<RouteRecord extends IPartialRouteRecord> {
|
|
|
4
4
|
history: History;
|
|
5
5
|
routes: RouteRecord[];
|
|
6
6
|
caseSensitive?: boolean;
|
|
7
|
-
staticMode?: boolean;
|
|
8
7
|
}
|
|
9
8
|
export declare const createRouter: <RouteRecord extends IRouteRecord>(options: IRouterOptions<RouteRecord>) => IRouter<RouteRecord>;
|
|
10
9
|
export {};
|
package/esm/router.js
CHANGED
|
@@ -5,7 +5,6 @@ import { createEvents, resolvePath } from './utils';
|
|
|
5
5
|
import { isError, isFunction } from './utils/error';
|
|
6
6
|
import { runQueue } from './utils/async';
|
|
7
7
|
import { getRedirectFromRoutes } from './getRedirectFromRoutes';
|
|
8
|
-
import { matchStaticRoutes } from './matchStaticRoutes';
|
|
9
8
|
const START = {
|
|
10
9
|
matches: [],
|
|
11
10
|
params: {},
|
|
@@ -18,12 +17,11 @@ const START = {
|
|
|
18
17
|
redirected: false
|
|
19
18
|
};
|
|
20
19
|
class Router {
|
|
21
|
-
constructor({ history, routes
|
|
20
|
+
constructor({ history, routes }) {
|
|
22
21
|
this._pending = null;
|
|
23
22
|
this._cancleHandler = null;
|
|
24
23
|
this._ready = false;
|
|
25
24
|
this._readyDefer = createDefer();
|
|
26
|
-
this._staticMode = false;
|
|
27
25
|
this._listeners = createEvents();
|
|
28
26
|
this._beforeEachs = createEvents();
|
|
29
27
|
this._beforeResolves = createEvents();
|
|
@@ -76,7 +74,7 @@ class Router {
|
|
|
76
74
|
};
|
|
77
75
|
this.match = (to) => {
|
|
78
76
|
const { _routes: routes } = this;
|
|
79
|
-
const matches =
|
|
77
|
+
const matches = matchRoutes(routes, to);
|
|
80
78
|
return matches || [];
|
|
81
79
|
};
|
|
82
80
|
this.replaceRoutes = (routes) => {
|
|
@@ -104,7 +102,6 @@ class Router {
|
|
|
104
102
|
this._history = history;
|
|
105
103
|
this._routes = createRoutesFromArray(routes);
|
|
106
104
|
this._current = START;
|
|
107
|
-
this._staticMode = !!staticMode;
|
|
108
105
|
this._history.doTransition = this._doTransition.bind(this);
|
|
109
106
|
}
|
|
110
107
|
get ready() {
|
package/lib/router.d.ts
CHANGED
|
@@ -4,7 +4,6 @@ interface IRouterOptions<RouteRecord extends IPartialRouteRecord> {
|
|
|
4
4
|
history: History;
|
|
5
5
|
routes: RouteRecord[];
|
|
6
6
|
caseSensitive?: boolean;
|
|
7
|
-
staticMode?: boolean;
|
|
8
7
|
}
|
|
9
8
|
export declare const createRouter: <RouteRecord extends IRouteRecord>(options: IRouterOptions<RouteRecord>) => IRouter<RouteRecord>;
|
|
10
9
|
export {};
|
package/lib/router.js
CHANGED
|
@@ -8,7 +8,6 @@ const utils_1 = require("./utils");
|
|
|
8
8
|
const error_1 = require("./utils/error");
|
|
9
9
|
const async_1 = require("./utils/async");
|
|
10
10
|
const getRedirectFromRoutes_1 = require("./getRedirectFromRoutes");
|
|
11
|
-
const matchStaticRoutes_1 = require("./matchStaticRoutes");
|
|
12
11
|
const START = {
|
|
13
12
|
matches: [],
|
|
14
13
|
params: {},
|
|
@@ -21,12 +20,11 @@ const START = {
|
|
|
21
20
|
redirected: false
|
|
22
21
|
};
|
|
23
22
|
class Router {
|
|
24
|
-
constructor({ history, routes
|
|
23
|
+
constructor({ history, routes }) {
|
|
25
24
|
this._pending = null;
|
|
26
25
|
this._cancleHandler = null;
|
|
27
26
|
this._ready = false;
|
|
28
27
|
this._readyDefer = (0, defer_1.createDefer)();
|
|
29
|
-
this._staticMode = false;
|
|
30
28
|
this._listeners = (0, utils_1.createEvents)();
|
|
31
29
|
this._beforeEachs = (0, utils_1.createEvents)();
|
|
32
30
|
this._beforeResolves = (0, utils_1.createEvents)();
|
|
@@ -79,7 +77,7 @@ class Router {
|
|
|
79
77
|
};
|
|
80
78
|
this.match = (to) => {
|
|
81
79
|
const { _routes: routes } = this;
|
|
82
|
-
const matches =
|
|
80
|
+
const matches = (0, matchRoutes_1.matchRoutes)(routes, to);
|
|
83
81
|
return matches || [];
|
|
84
82
|
};
|
|
85
83
|
this.replaceRoutes = (routes) => {
|
|
@@ -107,7 +105,6 @@ class Router {
|
|
|
107
105
|
this._history = history;
|
|
108
106
|
this._routes = (0, createRoutesFromArray_1.createRoutesFromArray)(routes);
|
|
109
107
|
this._current = START;
|
|
110
|
-
this._staticMode = !!staticMode;
|
|
111
108
|
this._history.doTransition = this._doTransition.bind(this);
|
|
112
109
|
}
|
|
113
110
|
get ready() {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shuvi/router",
|
|
3
|
-
"version": "2.0.0-dev.
|
|
3
|
+
"version": "2.0.0-dev.19",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/shuvijs/shuvi.git",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"node": ">= 16.0.0"
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@shuvi/utils": "2.0.0-dev.
|
|
31
|
+
"@shuvi/utils": "2.0.0-dev.19",
|
|
32
32
|
"query-string": "6.13.8"
|
|
33
33
|
}
|
|
34
34
|
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { IRouteMatch, PartialLocation } from './types';
|
|
2
|
-
import { IRouteBaseObject } from './matchRoutes';
|
|
3
|
-
/**
|
|
4
|
-
* Static Route Matching Function
|
|
5
|
-
*
|
|
6
|
-
* Optimization features:
|
|
7
|
-
* 1. O(1) exact match - direct HashMap lookup
|
|
8
|
-
* 2. Precompiled results - pre-calculate all match results at startup
|
|
9
|
-
* 3. Zero regex - pure string matching
|
|
10
|
-
* 4. Memory friendly - matcher instance reuse
|
|
11
|
-
* 5. Type safe - full TypeScript support
|
|
12
|
-
*
|
|
13
|
-
* @param routes Route configuration array
|
|
14
|
-
* @param location Location to match
|
|
15
|
-
* @param basename Base path
|
|
16
|
-
* @returns Match result or null
|
|
17
|
-
*/
|
|
18
|
-
export declare function matchStaticRoutes<T extends IRouteBaseObject>(routes: T[], location: string | PartialLocation, basename?: string): IRouteMatch<T>[] | null;
|
package/esm/matchStaticRoutes.js
DELETED
|
@@ -1,210 +0,0 @@
|
|
|
1
|
-
import { joinPaths, normalizeBase, resolvePath, stripBase } from './utils';
|
|
2
|
-
/**
|
|
3
|
-
* Static Route Matcher Class
|
|
4
|
-
*/
|
|
5
|
-
class StaticRouteMatcher {
|
|
6
|
-
constructor(routes) {
|
|
7
|
-
this.exactMatchMap = new Map();
|
|
8
|
-
this.allRoutes = routes;
|
|
9
|
-
this.prefixTree = this.buildPrefixTree(routes);
|
|
10
|
-
this.precompileMatches();
|
|
11
|
-
}
|
|
12
|
-
/**
|
|
13
|
-
* Build prefix tree
|
|
14
|
-
*/
|
|
15
|
-
buildPrefixTree(routes) {
|
|
16
|
-
const root = {
|
|
17
|
-
exactRoutes: new Map(),
|
|
18
|
-
children: new Map(),
|
|
19
|
-
routes: []
|
|
20
|
-
};
|
|
21
|
-
// Flatten all routes
|
|
22
|
-
const flatRoutes = this.flattenRoutes(routes);
|
|
23
|
-
for (const { path, routeChain } of flatRoutes) {
|
|
24
|
-
// Only process static routes
|
|
25
|
-
if (this.isStaticPath(path)) {
|
|
26
|
-
this.insertIntoTree(root, path, routeChain);
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
return root;
|
|
30
|
-
}
|
|
31
|
-
/**
|
|
32
|
-
* Flatten route structure
|
|
33
|
-
*/
|
|
34
|
-
flattenRoutes(routes, parentPath = '', parentRoutes = []) {
|
|
35
|
-
const result = [];
|
|
36
|
-
routes.forEach(route => {
|
|
37
|
-
let fullPath;
|
|
38
|
-
if (route.path === '') {
|
|
39
|
-
fullPath = parentPath;
|
|
40
|
-
}
|
|
41
|
-
else {
|
|
42
|
-
fullPath = joinPaths([parentPath, route.path]);
|
|
43
|
-
}
|
|
44
|
-
const routeChain = [...parentRoutes, route];
|
|
45
|
-
result.push({ path: fullPath, routeChain });
|
|
46
|
-
// Recursively process child routes
|
|
47
|
-
if (route.children) {
|
|
48
|
-
result.push(...this.flattenRoutes(route.children, fullPath, routeChain));
|
|
49
|
-
}
|
|
50
|
-
});
|
|
51
|
-
return result;
|
|
52
|
-
}
|
|
53
|
-
/**
|
|
54
|
-
* Check if path is static
|
|
55
|
-
*/
|
|
56
|
-
isStaticPath(path) {
|
|
57
|
-
return !path.includes(':') && !path.includes('*') && !path.includes('(');
|
|
58
|
-
}
|
|
59
|
-
/**
|
|
60
|
-
* Insert route into prefix tree
|
|
61
|
-
*/
|
|
62
|
-
insertIntoTree(node, path, routes) {
|
|
63
|
-
// Store exact match
|
|
64
|
-
node.exactRoutes.set(path, routes);
|
|
65
|
-
// Build prefix tree for prefix matching
|
|
66
|
-
const segments = path.split('/').filter(Boolean);
|
|
67
|
-
let currentNode = node;
|
|
68
|
-
for (const segment of segments) {
|
|
69
|
-
if (!currentNode.children.has(segment)) {
|
|
70
|
-
currentNode.children.set(segment, {
|
|
71
|
-
exactRoutes: new Map(),
|
|
72
|
-
children: new Map(),
|
|
73
|
-
routes: []
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
|
-
currentNode = currentNode.children.get(segment);
|
|
77
|
-
}
|
|
78
|
-
// Store routes at leaf node
|
|
79
|
-
currentNode.routes = routes;
|
|
80
|
-
}
|
|
81
|
-
/**
|
|
82
|
-
* Precompile all possible match results
|
|
83
|
-
*/
|
|
84
|
-
precompileMatches() {
|
|
85
|
-
for (const [path, routeChain] of this.prefixTree.exactRoutes) {
|
|
86
|
-
const matches = this.buildMatches(routeChain, path);
|
|
87
|
-
this.exactMatchMap.set(path, {
|
|
88
|
-
matches,
|
|
89
|
-
pathname: path,
|
|
90
|
-
params: {} // Static routes have no parameters
|
|
91
|
-
});
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
/**
|
|
95
|
-
* Build match results
|
|
96
|
-
*/
|
|
97
|
-
buildMatches(routeChain, matchedPathname) {
|
|
98
|
-
const matches = [];
|
|
99
|
-
let currentMatchedPath = '/';
|
|
100
|
-
for (let i = 0; i < routeChain.length; i++) {
|
|
101
|
-
const route = routeChain[i];
|
|
102
|
-
if (route.path === '') {
|
|
103
|
-
// Simulate the exact behavior of matchPathname + joinPaths
|
|
104
|
-
// When remainingPathname is '/' and path is '', matchPathname returns { pathname: '/' }
|
|
105
|
-
// Then joinPaths([currentPath, '/']) adds trailing slash
|
|
106
|
-
currentMatchedPath = joinPaths([currentMatchedPath, '/']);
|
|
107
|
-
}
|
|
108
|
-
else {
|
|
109
|
-
// For non-empty path, use the path directly
|
|
110
|
-
currentMatchedPath = joinPaths([currentMatchedPath, route.path]);
|
|
111
|
-
}
|
|
112
|
-
matches.push({
|
|
113
|
-
route,
|
|
114
|
-
pathname: currentMatchedPath,
|
|
115
|
-
params: Object.freeze({}) // Static routes have no parameters
|
|
116
|
-
});
|
|
117
|
-
}
|
|
118
|
-
return matches;
|
|
119
|
-
}
|
|
120
|
-
/**
|
|
121
|
-
* Match pathname
|
|
122
|
-
*/
|
|
123
|
-
match(pathname) {
|
|
124
|
-
// 1. Exact match (O(1))
|
|
125
|
-
const exactMatch = this.exactMatchMap.get(pathname);
|
|
126
|
-
if (exactMatch) {
|
|
127
|
-
return exactMatch;
|
|
128
|
-
}
|
|
129
|
-
// 2. Prefix match (for handling trailing slash etc.)
|
|
130
|
-
return this.findPrefixMatch(pathname);
|
|
131
|
-
}
|
|
132
|
-
/**
|
|
133
|
-
* Find prefix match
|
|
134
|
-
*/
|
|
135
|
-
findPrefixMatch(pathname) {
|
|
136
|
-
// Try adding/removing trailing slash
|
|
137
|
-
const alternatives = [
|
|
138
|
-
pathname,
|
|
139
|
-
pathname === '/' ? pathname : pathname.replace(/\/$/, ''),
|
|
140
|
-
pathname.endsWith('/') ? pathname : pathname + '/'
|
|
141
|
-
];
|
|
142
|
-
for (const alt of alternatives) {
|
|
143
|
-
const match = this.exactMatchMap.get(alt);
|
|
144
|
-
if (match) {
|
|
145
|
-
return match;
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
return null;
|
|
149
|
-
}
|
|
150
|
-
/**
|
|
151
|
-
* Get all static route paths (for debugging)
|
|
152
|
-
*/
|
|
153
|
-
getAllStaticPaths() {
|
|
154
|
-
return Array.from(this.exactMatchMap.keys()).sort();
|
|
155
|
-
}
|
|
156
|
-
/**
|
|
157
|
-
* Get matching statistics
|
|
158
|
-
*/
|
|
159
|
-
getStats() {
|
|
160
|
-
return {
|
|
161
|
-
totalStaticRoutes: this.exactMatchMap.size,
|
|
162
|
-
totalRoutes: this.allRoutes.length,
|
|
163
|
-
staticRatio: (this.exactMatchMap.size / this.allRoutes.length * 100).toFixed(1) + '%'
|
|
164
|
-
};
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
// Global matcher cache
|
|
168
|
-
const staticMatcherCache = new WeakMap();
|
|
169
|
-
/**
|
|
170
|
-
* Static Route Matching Function
|
|
171
|
-
*
|
|
172
|
-
* Optimization features:
|
|
173
|
-
* 1. O(1) exact match - direct HashMap lookup
|
|
174
|
-
* 2. Precompiled results - pre-calculate all match results at startup
|
|
175
|
-
* 3. Zero regex - pure string matching
|
|
176
|
-
* 4. Memory friendly - matcher instance reuse
|
|
177
|
-
* 5. Type safe - full TypeScript support
|
|
178
|
-
*
|
|
179
|
-
* @param routes Route configuration array
|
|
180
|
-
* @param location Location to match
|
|
181
|
-
* @param basename Base path
|
|
182
|
-
* @returns Match result or null
|
|
183
|
-
*/
|
|
184
|
-
export function matchStaticRoutes(routes, location, basename = '') {
|
|
185
|
-
// Normalize input
|
|
186
|
-
if (typeof location === 'string') {
|
|
187
|
-
location = resolvePath(location);
|
|
188
|
-
}
|
|
189
|
-
let pathname = location.pathname || '/';
|
|
190
|
-
// Handle basename
|
|
191
|
-
if (basename) {
|
|
192
|
-
const normalizedBasename = normalizeBase(basename);
|
|
193
|
-
const pathnameWithoutBase = stripBase(pathname, normalizedBasename);
|
|
194
|
-
if (pathnameWithoutBase) {
|
|
195
|
-
pathname = pathnameWithoutBase;
|
|
196
|
-
}
|
|
197
|
-
else {
|
|
198
|
-
return null;
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
// Get or create matcher
|
|
202
|
-
let matcher = staticMatcherCache.get(routes);
|
|
203
|
-
if (!matcher) {
|
|
204
|
-
matcher = new StaticRouteMatcher(routes);
|
|
205
|
-
staticMatcherCache.set(routes, matcher);
|
|
206
|
-
}
|
|
207
|
-
// Execute matching
|
|
208
|
-
const result = matcher.match(pathname);
|
|
209
|
-
return result ? result.matches : null;
|
|
210
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { IRouteMatch, PartialLocation } from './types';
|
|
2
|
-
import { IRouteBaseObject } from './matchRoutes';
|
|
3
|
-
/**
|
|
4
|
-
* Static Route Matching Function
|
|
5
|
-
*
|
|
6
|
-
* Optimization features:
|
|
7
|
-
* 1. O(1) exact match - direct HashMap lookup
|
|
8
|
-
* 2. Precompiled results - pre-calculate all match results at startup
|
|
9
|
-
* 3. Zero regex - pure string matching
|
|
10
|
-
* 4. Memory friendly - matcher instance reuse
|
|
11
|
-
* 5. Type safe - full TypeScript support
|
|
12
|
-
*
|
|
13
|
-
* @param routes Route configuration array
|
|
14
|
-
* @param location Location to match
|
|
15
|
-
* @param basename Base path
|
|
16
|
-
* @returns Match result or null
|
|
17
|
-
*/
|
|
18
|
-
export declare function matchStaticRoutes<T extends IRouteBaseObject>(routes: T[], location: string | PartialLocation, basename?: string): IRouteMatch<T>[] | null;
|
package/lib/matchStaticRoutes.js
DELETED
|
@@ -1,213 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.matchStaticRoutes = matchStaticRoutes;
|
|
4
|
-
const utils_1 = require("./utils");
|
|
5
|
-
/**
|
|
6
|
-
* Static Route Matcher Class
|
|
7
|
-
*/
|
|
8
|
-
class StaticRouteMatcher {
|
|
9
|
-
constructor(routes) {
|
|
10
|
-
this.exactMatchMap = new Map();
|
|
11
|
-
this.allRoutes = routes;
|
|
12
|
-
this.prefixTree = this.buildPrefixTree(routes);
|
|
13
|
-
this.precompileMatches();
|
|
14
|
-
}
|
|
15
|
-
/**
|
|
16
|
-
* Build prefix tree
|
|
17
|
-
*/
|
|
18
|
-
buildPrefixTree(routes) {
|
|
19
|
-
const root = {
|
|
20
|
-
exactRoutes: new Map(),
|
|
21
|
-
children: new Map(),
|
|
22
|
-
routes: []
|
|
23
|
-
};
|
|
24
|
-
// Flatten all routes
|
|
25
|
-
const flatRoutes = this.flattenRoutes(routes);
|
|
26
|
-
for (const { path, routeChain } of flatRoutes) {
|
|
27
|
-
// Only process static routes
|
|
28
|
-
if (this.isStaticPath(path)) {
|
|
29
|
-
this.insertIntoTree(root, path, routeChain);
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
return root;
|
|
33
|
-
}
|
|
34
|
-
/**
|
|
35
|
-
* Flatten route structure
|
|
36
|
-
*/
|
|
37
|
-
flattenRoutes(routes, parentPath = '', parentRoutes = []) {
|
|
38
|
-
const result = [];
|
|
39
|
-
routes.forEach(route => {
|
|
40
|
-
let fullPath;
|
|
41
|
-
if (route.path === '') {
|
|
42
|
-
fullPath = parentPath;
|
|
43
|
-
}
|
|
44
|
-
else {
|
|
45
|
-
fullPath = (0, utils_1.joinPaths)([parentPath, route.path]);
|
|
46
|
-
}
|
|
47
|
-
const routeChain = [...parentRoutes, route];
|
|
48
|
-
result.push({ path: fullPath, routeChain });
|
|
49
|
-
// Recursively process child routes
|
|
50
|
-
if (route.children) {
|
|
51
|
-
result.push(...this.flattenRoutes(route.children, fullPath, routeChain));
|
|
52
|
-
}
|
|
53
|
-
});
|
|
54
|
-
return result;
|
|
55
|
-
}
|
|
56
|
-
/**
|
|
57
|
-
* Check if path is static
|
|
58
|
-
*/
|
|
59
|
-
isStaticPath(path) {
|
|
60
|
-
return !path.includes(':') && !path.includes('*') && !path.includes('(');
|
|
61
|
-
}
|
|
62
|
-
/**
|
|
63
|
-
* Insert route into prefix tree
|
|
64
|
-
*/
|
|
65
|
-
insertIntoTree(node, path, routes) {
|
|
66
|
-
// Store exact match
|
|
67
|
-
node.exactRoutes.set(path, routes);
|
|
68
|
-
// Build prefix tree for prefix matching
|
|
69
|
-
const segments = path.split('/').filter(Boolean);
|
|
70
|
-
let currentNode = node;
|
|
71
|
-
for (const segment of segments) {
|
|
72
|
-
if (!currentNode.children.has(segment)) {
|
|
73
|
-
currentNode.children.set(segment, {
|
|
74
|
-
exactRoutes: new Map(),
|
|
75
|
-
children: new Map(),
|
|
76
|
-
routes: []
|
|
77
|
-
});
|
|
78
|
-
}
|
|
79
|
-
currentNode = currentNode.children.get(segment);
|
|
80
|
-
}
|
|
81
|
-
// Store routes at leaf node
|
|
82
|
-
currentNode.routes = routes;
|
|
83
|
-
}
|
|
84
|
-
/**
|
|
85
|
-
* Precompile all possible match results
|
|
86
|
-
*/
|
|
87
|
-
precompileMatches() {
|
|
88
|
-
for (const [path, routeChain] of this.prefixTree.exactRoutes) {
|
|
89
|
-
const matches = this.buildMatches(routeChain, path);
|
|
90
|
-
this.exactMatchMap.set(path, {
|
|
91
|
-
matches,
|
|
92
|
-
pathname: path,
|
|
93
|
-
params: {} // Static routes have no parameters
|
|
94
|
-
});
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
/**
|
|
98
|
-
* Build match results
|
|
99
|
-
*/
|
|
100
|
-
buildMatches(routeChain, matchedPathname) {
|
|
101
|
-
const matches = [];
|
|
102
|
-
let currentMatchedPath = '/';
|
|
103
|
-
for (let i = 0; i < routeChain.length; i++) {
|
|
104
|
-
const route = routeChain[i];
|
|
105
|
-
if (route.path === '') {
|
|
106
|
-
// Simulate the exact behavior of matchPathname + joinPaths
|
|
107
|
-
// When remainingPathname is '/' and path is '', matchPathname returns { pathname: '/' }
|
|
108
|
-
// Then joinPaths([currentPath, '/']) adds trailing slash
|
|
109
|
-
currentMatchedPath = (0, utils_1.joinPaths)([currentMatchedPath, '/']);
|
|
110
|
-
}
|
|
111
|
-
else {
|
|
112
|
-
// For non-empty path, use the path directly
|
|
113
|
-
currentMatchedPath = (0, utils_1.joinPaths)([currentMatchedPath, route.path]);
|
|
114
|
-
}
|
|
115
|
-
matches.push({
|
|
116
|
-
route,
|
|
117
|
-
pathname: currentMatchedPath,
|
|
118
|
-
params: Object.freeze({}) // Static routes have no parameters
|
|
119
|
-
});
|
|
120
|
-
}
|
|
121
|
-
return matches;
|
|
122
|
-
}
|
|
123
|
-
/**
|
|
124
|
-
* Match pathname
|
|
125
|
-
*/
|
|
126
|
-
match(pathname) {
|
|
127
|
-
// 1. Exact match (O(1))
|
|
128
|
-
const exactMatch = this.exactMatchMap.get(pathname);
|
|
129
|
-
if (exactMatch) {
|
|
130
|
-
return exactMatch;
|
|
131
|
-
}
|
|
132
|
-
// 2. Prefix match (for handling trailing slash etc.)
|
|
133
|
-
return this.findPrefixMatch(pathname);
|
|
134
|
-
}
|
|
135
|
-
/**
|
|
136
|
-
* Find prefix match
|
|
137
|
-
*/
|
|
138
|
-
findPrefixMatch(pathname) {
|
|
139
|
-
// Try adding/removing trailing slash
|
|
140
|
-
const alternatives = [
|
|
141
|
-
pathname,
|
|
142
|
-
pathname === '/' ? pathname : pathname.replace(/\/$/, ''),
|
|
143
|
-
pathname.endsWith('/') ? pathname : pathname + '/'
|
|
144
|
-
];
|
|
145
|
-
for (const alt of alternatives) {
|
|
146
|
-
const match = this.exactMatchMap.get(alt);
|
|
147
|
-
if (match) {
|
|
148
|
-
return match;
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
return null;
|
|
152
|
-
}
|
|
153
|
-
/**
|
|
154
|
-
* Get all static route paths (for debugging)
|
|
155
|
-
*/
|
|
156
|
-
getAllStaticPaths() {
|
|
157
|
-
return Array.from(this.exactMatchMap.keys()).sort();
|
|
158
|
-
}
|
|
159
|
-
/**
|
|
160
|
-
* Get matching statistics
|
|
161
|
-
*/
|
|
162
|
-
getStats() {
|
|
163
|
-
return {
|
|
164
|
-
totalStaticRoutes: this.exactMatchMap.size,
|
|
165
|
-
totalRoutes: this.allRoutes.length,
|
|
166
|
-
staticRatio: (this.exactMatchMap.size / this.allRoutes.length * 100).toFixed(1) + '%'
|
|
167
|
-
};
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
// Global matcher cache
|
|
171
|
-
const staticMatcherCache = new WeakMap();
|
|
172
|
-
/**
|
|
173
|
-
* Static Route Matching Function
|
|
174
|
-
*
|
|
175
|
-
* Optimization features:
|
|
176
|
-
* 1. O(1) exact match - direct HashMap lookup
|
|
177
|
-
* 2. Precompiled results - pre-calculate all match results at startup
|
|
178
|
-
* 3. Zero regex - pure string matching
|
|
179
|
-
* 4. Memory friendly - matcher instance reuse
|
|
180
|
-
* 5. Type safe - full TypeScript support
|
|
181
|
-
*
|
|
182
|
-
* @param routes Route configuration array
|
|
183
|
-
* @param location Location to match
|
|
184
|
-
* @param basename Base path
|
|
185
|
-
* @returns Match result or null
|
|
186
|
-
*/
|
|
187
|
-
function matchStaticRoutes(routes, location, basename = '') {
|
|
188
|
-
// Normalize input
|
|
189
|
-
if (typeof location === 'string') {
|
|
190
|
-
location = (0, utils_1.resolvePath)(location);
|
|
191
|
-
}
|
|
192
|
-
let pathname = location.pathname || '/';
|
|
193
|
-
// Handle basename
|
|
194
|
-
if (basename) {
|
|
195
|
-
const normalizedBasename = (0, utils_1.normalizeBase)(basename);
|
|
196
|
-
const pathnameWithoutBase = (0, utils_1.stripBase)(pathname, normalizedBasename);
|
|
197
|
-
if (pathnameWithoutBase) {
|
|
198
|
-
pathname = pathnameWithoutBase;
|
|
199
|
-
}
|
|
200
|
-
else {
|
|
201
|
-
return null;
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
// Get or create matcher
|
|
205
|
-
let matcher = staticMatcherCache.get(routes);
|
|
206
|
-
if (!matcher) {
|
|
207
|
-
matcher = new StaticRouteMatcher(routes);
|
|
208
|
-
staticMatcherCache.set(routes, matcher);
|
|
209
|
-
}
|
|
210
|
-
// Execute matching
|
|
211
|
-
const result = matcher.match(pathname);
|
|
212
|
-
return result ? result.matches : null;
|
|
213
|
-
}
|