@reckona/mreact-router 0.0.167 → 0.0.169
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/dist/build.d.ts +2 -1
- package/dist/build.d.ts.map +1 -1
- package/dist/build.js +2 -1
- package/dist/build.js.map +1 -1
- package/dist/routes.d.ts +24 -1
- package/dist/routes.d.ts.map +1 -1
- package/dist/routes.js +123 -1
- package/dist/routes.js.map +1 -1
- package/dist/serve.js +1 -1
- package/dist/serve.js.map +1 -1
- package/package.json +11 -11
- package/src/build.ts +4 -2
- package/src/routes.ts +183 -1
- package/src/serve.ts +1 -1
package/src/routes.ts
CHANGED
|
@@ -80,6 +80,25 @@ export interface RouteMatcher {
|
|
|
80
80
|
match(pathname: string): MatchedRoute | undefined;
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
+
export type CompiledRouteMatcherSegment =
|
|
84
|
+
| { kind: "static"; value: string }
|
|
85
|
+
| { kind: "dynamic"; name: string }
|
|
86
|
+
| { kind: "catch-all"; name: string };
|
|
87
|
+
|
|
88
|
+
export interface CompiledRouteMatcherEntry {
|
|
89
|
+
catchAllIndex: number;
|
|
90
|
+
exactLength?: number;
|
|
91
|
+
minimumLength: number;
|
|
92
|
+
routeIndex: number;
|
|
93
|
+
segments: readonly CompiledRouteMatcherSegment[];
|
|
94
|
+
suffixLength?: number;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export interface CompiledRouteMatcherArtifact {
|
|
98
|
+
routes: readonly CompiledRouteMatcherEntry[];
|
|
99
|
+
version: 1;
|
|
100
|
+
}
|
|
101
|
+
|
|
83
102
|
/**
|
|
84
103
|
* Scans an app directory and returns sorted app-router route definitions.
|
|
85
104
|
*/
|
|
@@ -121,7 +140,33 @@ export function matchRoute(
|
|
|
121
140
|
return createRouteMatcher(routes).match(pathname);
|
|
122
141
|
}
|
|
123
142
|
|
|
124
|
-
export function
|
|
143
|
+
export function compileRouteMatcherArtifact(
|
|
144
|
+
routes: readonly AppRoute[],
|
|
145
|
+
): CompiledRouteMatcherArtifact {
|
|
146
|
+
const sortedRoutes = routes
|
|
147
|
+
.map((route, routeIndex) => ({ route, routeIndex }))
|
|
148
|
+
.sort((left, right) => compareRoutes(left.route, right.route));
|
|
149
|
+
|
|
150
|
+
return {
|
|
151
|
+
version: 1,
|
|
152
|
+
routes: sortedRoutes.map(({ route, routeIndex }) =>
|
|
153
|
+
compileRouteMatcherEntry(route, routeIndex),
|
|
154
|
+
),
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export function createRouteMatcher(
|
|
159
|
+
routes: readonly AppRoute[],
|
|
160
|
+
artifact?: CompiledRouteMatcherArtifact | undefined,
|
|
161
|
+
): RouteMatcher {
|
|
162
|
+
if (artifact?.version === 1) {
|
|
163
|
+
return {
|
|
164
|
+
match(pathname) {
|
|
165
|
+
return matchCompiledRoutes(routes, artifact, pathname);
|
|
166
|
+
},
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
125
170
|
const sortedRoutes = [...routes].sort(compareRoutes);
|
|
126
171
|
const nativeMatcher = createNativeRouteMatcher(sortedRoutes);
|
|
127
172
|
|
|
@@ -136,6 +181,143 @@ export function createRouteMatcher(routes: readonly AppRoute[]): RouteMatcher {
|
|
|
136
181
|
};
|
|
137
182
|
}
|
|
138
183
|
|
|
184
|
+
function compileRouteMatcherEntry(
|
|
185
|
+
route: AppRoute,
|
|
186
|
+
routeIndex: number,
|
|
187
|
+
): CompiledRouteMatcherEntry {
|
|
188
|
+
const catchAllIndex = route.segments.findIndex((segment) => segment.kind === "catch-all");
|
|
189
|
+
const shared = {
|
|
190
|
+
catchAllIndex,
|
|
191
|
+
minimumLength: catchAllIndex === -1 ? route.segments.length : catchAllIndex + 1,
|
|
192
|
+
routeIndex,
|
|
193
|
+
segments: route.segments.map((segment) => ({ ...segment })),
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
return catchAllIndex === -1
|
|
197
|
+
? {
|
|
198
|
+
...shared,
|
|
199
|
+
exactLength: route.segments.length,
|
|
200
|
+
}
|
|
201
|
+
: {
|
|
202
|
+
...shared,
|
|
203
|
+
suffixLength: route.segments.length - catchAllIndex - 1,
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function matchCompiledRoutes(
|
|
208
|
+
routes: readonly AppRoute[],
|
|
209
|
+
artifact: CompiledRouteMatcherArtifact,
|
|
210
|
+
pathname: string,
|
|
211
|
+
): MatchedRoute | undefined {
|
|
212
|
+
const normalized = normalizePath(pathname);
|
|
213
|
+
const pathnameSegments = normalized === "/" ? [] : normalized.slice(1).split("/");
|
|
214
|
+
|
|
215
|
+
for (const compiledRoute of artifact.routes) {
|
|
216
|
+
const route = routes[compiledRoute.routeIndex];
|
|
217
|
+
if (route === undefined) {
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
if (
|
|
222
|
+
compiledRoute.exactLength !== undefined &&
|
|
223
|
+
compiledRoute.exactLength !== pathnameSegments.length
|
|
224
|
+
) {
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if (pathnameSegments.length < compiledRoute.minimumLength) {
|
|
229
|
+
continue;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const params: Record<string, readonly string[] | string> = {};
|
|
233
|
+
let matched = true;
|
|
234
|
+
|
|
235
|
+
for (const [index, segment] of compiledRoute.segments.entries()) {
|
|
236
|
+
const value = pathnameSegments[index];
|
|
237
|
+
|
|
238
|
+
if (value === undefined) {
|
|
239
|
+
matched = false;
|
|
240
|
+
break;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
if (segment.kind === "static" && segment.value !== value) {
|
|
244
|
+
matched = false;
|
|
245
|
+
break;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
if (segment.kind === "dynamic") {
|
|
249
|
+
const decoded = safeDecodeURIComponent(value);
|
|
250
|
+
if (decoded === undefined) {
|
|
251
|
+
matched = false;
|
|
252
|
+
break;
|
|
253
|
+
}
|
|
254
|
+
params[segment.name] = decoded;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
if (segment.kind === "catch-all") {
|
|
258
|
+
const suffixLength = compiledRoute.suffixLength ?? 0;
|
|
259
|
+
const catchAllEnd = pathnameSegments.length - suffixLength;
|
|
260
|
+
|
|
261
|
+
if (catchAllEnd <= index) {
|
|
262
|
+
matched = false;
|
|
263
|
+
break;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const decodedParts: string[] = [];
|
|
267
|
+
for (let partIndex = index; partIndex < catchAllEnd; partIndex += 1) {
|
|
268
|
+
const decoded = safeDecodeURIComponent(pathnameSegments[partIndex] ?? "");
|
|
269
|
+
if (decoded === undefined) {
|
|
270
|
+
matched = false;
|
|
271
|
+
break;
|
|
272
|
+
}
|
|
273
|
+
decodedParts.push(decoded);
|
|
274
|
+
}
|
|
275
|
+
if (!matched) {
|
|
276
|
+
break;
|
|
277
|
+
}
|
|
278
|
+
params[segment.name] = decodedParts;
|
|
279
|
+
|
|
280
|
+
for (let suffixIndex = 0; suffixIndex < suffixLength; suffixIndex += 1) {
|
|
281
|
+
const suffixSegment = compiledRoute.segments[index + 1 + suffixIndex];
|
|
282
|
+
const suffixValue = pathnameSegments[catchAllEnd + suffixIndex];
|
|
283
|
+
|
|
284
|
+
if (suffixSegment === undefined || suffixValue === undefined) {
|
|
285
|
+
matched = false;
|
|
286
|
+
break;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
if (suffixSegment.kind === "static" && suffixSegment.value !== suffixValue) {
|
|
290
|
+
matched = false;
|
|
291
|
+
break;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
if (suffixSegment.kind === "dynamic") {
|
|
295
|
+
const decoded = safeDecodeURIComponent(suffixValue);
|
|
296
|
+
if (decoded === undefined) {
|
|
297
|
+
matched = false;
|
|
298
|
+
break;
|
|
299
|
+
}
|
|
300
|
+
params[suffixSegment.name] = decoded;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
if (suffixSegment.kind === "catch-all") {
|
|
304
|
+
matched = false;
|
|
305
|
+
break;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
break;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
if (matched) {
|
|
314
|
+
return { route, params };
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
return undefined;
|
|
319
|
+
}
|
|
320
|
+
|
|
139
321
|
function matchSortedRoutes(
|
|
140
322
|
routes: readonly AppRoute[],
|
|
141
323
|
pathname: string,
|
package/src/serve.ts
CHANGED
|
@@ -975,7 +975,7 @@ async function materializeBuiltRuntime(options: {
|
|
|
975
975
|
references,
|
|
976
976
|
]),
|
|
977
977
|
);
|
|
978
|
-
const routeMatcher = createRouteMatcher(routes);
|
|
978
|
+
const routeMatcher = createRouteMatcher(routes, serverManifest.routeMatcher);
|
|
979
979
|
const clientScripts = new Map(
|
|
980
980
|
clientManifest.routes.flatMap((route) =>
|
|
981
981
|
route.client && route.script !== undefined ? [[route.path, route.script]] : [],
|