@rangojs/router 0.0.0-experimental.9 → 0.0.0-experimental.a5f27bd5
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/AGENTS.md +5 -0
- package/README.md +884 -4
- package/dist/bin/rango.js +1531 -155
- package/dist/vite/index.js +4440 -2170
- package/package.json +60 -54
- package/skills/breadcrumbs/SKILL.md +250 -0
- package/skills/cache-guide/SKILL.md +262 -0
- package/skills/caching/SKILL.md +50 -21
- package/skills/composability/SKILL.md +172 -0
- package/skills/debug-manifest/SKILL.md +12 -8
- package/skills/document-cache/SKILL.md +18 -16
- package/skills/fonts/SKILL.md +6 -4
- package/skills/hooks/SKILL.md +333 -71
- package/skills/host-router/SKILL.md +218 -0
- package/skills/intercept/SKILL.md +131 -8
- package/skills/layout/SKILL.md +100 -3
- package/skills/links/SKILL.md +74 -15
- package/skills/loader/SKILL.md +388 -38
- package/skills/middleware/SKILL.md +171 -34
- package/skills/mime-routes/SKILL.md +15 -11
- package/skills/parallel/SKILL.md +78 -1
- package/skills/prerender/SKILL.md +405 -45
- package/skills/rango/SKILL.md +85 -21
- package/skills/response-routes/SKILL.md +144 -91
- package/skills/route/SKILL.md +226 -14
- package/skills/router-setup/SKILL.md +123 -30
- package/skills/theme/SKILL.md +9 -8
- package/skills/typesafety/SKILL.md +316 -87
- package/skills/use-cache/SKILL.md +324 -0
- package/src/__internal.ts +102 -4
- package/src/bin/rango.ts +312 -15
- package/src/browser/action-coordinator.ts +97 -0
- package/src/browser/action-response-classifier.ts +99 -0
- package/src/browser/event-controller.ts +87 -64
- package/src/browser/history-state.ts +80 -0
- package/src/browser/intercept-utils.ts +52 -0
- package/src/browser/link-interceptor.ts +24 -4
- package/src/browser/logging.ts +55 -0
- package/src/browser/merge-segment-loaders.ts +20 -12
- package/src/browser/navigation-bridge.ts +285 -553
- package/src/browser/navigation-client.ts +123 -73
- package/src/browser/navigation-store.ts +33 -50
- package/src/browser/navigation-transaction.ts +295 -0
- package/src/browser/network-error-handler.ts +61 -0
- package/src/browser/partial-update.ts +261 -309
- package/src/browser/prefetch/cache.ts +154 -0
- package/src/browser/prefetch/fetch.ts +135 -0
- package/src/browser/prefetch/observer.ts +65 -0
- package/src/browser/prefetch/policy.ts +48 -0
- package/src/browser/prefetch/queue.ts +88 -0
- package/src/browser/rango-state.ts +112 -0
- package/src/browser/react/Link.tsx +182 -70
- package/src/browser/react/NavigationProvider.tsx +51 -11
- package/src/browser/react/context.ts +6 -0
- package/src/browser/react/filter-segment-order.ts +11 -0
- package/src/browser/react/index.ts +12 -12
- package/src/browser/react/location-state-shared.ts +95 -53
- package/src/browser/react/location-state.ts +60 -15
- package/src/browser/react/mount-context.ts +6 -1
- package/src/browser/react/nonce-context.ts +23 -0
- package/src/browser/react/shallow-equal.ts +27 -0
- package/src/browser/react/use-action.ts +29 -51
- package/src/browser/react/use-client-cache.ts +5 -3
- package/src/browser/react/use-handle.ts +29 -70
- package/src/browser/react/use-link-status.ts +6 -5
- package/src/browser/react/use-navigation.ts +22 -63
- package/src/browser/react/use-params.ts +65 -0
- package/src/browser/react/use-pathname.ts +47 -0
- package/src/browser/react/use-router.ts +63 -0
- package/src/browser/react/use-search-params.ts +56 -0
- package/src/browser/react/use-segments.ts +80 -97
- package/src/browser/response-adapter.ts +73 -0
- package/src/browser/rsc-router.tsx +106 -27
- package/src/browser/scroll-restoration.ts +92 -16
- package/src/browser/segment-reconciler.ts +216 -0
- package/src/browser/segment-structure-assert.ts +16 -0
- package/src/browser/server-action-bridge.ts +504 -599
- package/src/browser/shallow.ts +6 -1
- package/src/browser/types.ts +107 -47
- package/src/browser/validate-redirect-origin.ts +29 -0
- package/src/build/generate-manifest.ts +82 -21
- package/src/build/generate-route-types.ts +36 -752
- package/src/build/index.ts +6 -5
- package/src/build/route-trie.ts +39 -13
- package/src/build/route-types/ast-helpers.ts +25 -0
- package/src/build/route-types/ast-route-extraction.ts +98 -0
- package/src/build/route-types/codegen.ts +102 -0
- package/src/build/route-types/include-resolution.ts +411 -0
- package/src/build/route-types/param-extraction.ts +48 -0
- package/src/build/route-types/per-module-writer.ts +128 -0
- package/src/build/route-types/router-processing.ts +469 -0
- package/src/build/route-types/scan-filter.ts +78 -0
- package/src/build/runtime-discovery.ts +231 -0
- package/src/cache/background-task.ts +34 -0
- package/src/cache/cache-key-utils.ts +44 -0
- package/src/cache/cache-policy.ts +125 -0
- package/src/cache/cache-runtime.ts +338 -0
- package/src/cache/cache-scope.ts +120 -301
- package/src/cache/cf/cf-cache-store.ts +119 -7
- package/src/cache/cf/index.ts +8 -2
- package/src/cache/document-cache.ts +101 -72
- package/src/cache/handle-capture.ts +81 -0
- package/src/cache/handle-snapshot.ts +41 -0
- package/src/cache/index.ts +0 -15
- package/src/cache/memory-segment-store.ts +191 -13
- package/src/cache/profile-registry.ts +73 -0
- package/src/cache/read-through-swr.ts +134 -0
- package/src/cache/segment-codec.ts +256 -0
- package/src/cache/taint.ts +98 -0
- package/src/cache/types.ts +72 -122
- package/src/client.rsc.tsx +3 -1
- package/src/client.tsx +84 -126
- package/src/component-utils.ts +4 -4
- package/src/components/DefaultDocument.tsx +5 -1
- package/src/context-var.ts +86 -0
- package/src/debug.ts +17 -7
- package/src/errors.ts +77 -7
- package/src/handle.ts +15 -10
- package/src/handles/MetaTags.tsx +73 -20
- package/src/handles/breadcrumbs.ts +66 -0
- package/src/handles/index.ts +1 -0
- package/src/handles/meta.ts +30 -13
- package/src/host/cookie-handler.ts +21 -15
- package/src/host/errors.ts +8 -8
- package/src/host/index.ts +4 -7
- package/src/host/pattern-matcher.ts +27 -27
- package/src/host/router.ts +61 -39
- package/src/host/testing.ts +8 -8
- package/src/host/types.ts +15 -7
- package/src/host/utils.ts +1 -1
- package/src/href-client.ts +65 -45
- package/src/index.rsc.ts +133 -21
- package/src/index.ts +164 -52
- package/src/internal-debug.ts +11 -0
- package/src/loader.rsc.ts +25 -143
- package/src/loader.ts +27 -10
- package/src/network-error-thrower.tsx +3 -1
- package/src/outlet-provider.tsx +45 -0
- package/src/prerender/param-hash.ts +4 -2
- package/src/prerender/store.ts +158 -13
- package/src/prerender.ts +333 -26
- package/src/reverse.ts +184 -121
- package/src/root-error-boundary.tsx +41 -29
- package/src/route-content-wrapper.tsx +7 -4
- package/src/route-definition/dsl-helpers.ts +934 -0
- package/src/route-definition/helper-factories.ts +200 -0
- package/src/route-definition/helpers-types.ts +430 -0
- package/src/route-definition/index.ts +52 -0
- package/src/route-definition/redirect.ts +93 -0
- package/src/route-definition.ts +1 -1431
- package/src/route-map-builder.ts +156 -123
- package/src/route-name.ts +53 -0
- package/src/route-types.ts +48 -9
- package/src/router/content-negotiation.ts +116 -0
- package/src/router/debug-manifest.ts +72 -0
- package/src/router/error-handling.ts +9 -9
- package/src/router/find-match.ts +158 -0
- package/src/router/handler-context.ts +374 -81
- package/src/router/intercept-resolution.ts +24 -16
- package/src/router/lazy-includes.ts +234 -0
- package/src/router/loader-resolution.ts +215 -122
- package/src/router/logging.ts +248 -0
- package/src/router/manifest.ts +83 -32
- package/src/router/match-api.ts +118 -119
- package/src/router/match-context.ts +4 -2
- package/src/router/match-handlers.ts +440 -0
- package/src/router/match-middleware/background-revalidation.ts +80 -93
- package/src/router/match-middleware/cache-lookup.ts +336 -84
- package/src/router/match-middleware/cache-store.ts +43 -24
- package/src/router/match-middleware/intercept-resolution.ts +45 -20
- package/src/router/match-middleware/segment-resolution.ts +16 -8
- package/src/router/match-pipelines.ts +10 -45
- package/src/router/match-result.ts +34 -28
- package/src/router/metrics.ts +235 -15
- package/src/router/middleware-cookies.ts +55 -0
- package/src/router/middleware-types.ts +222 -0
- package/src/router/middleware.ts +324 -367
- package/src/router/pattern-matching.ts +197 -41
- package/src/router/prerender-match.ts +402 -0
- package/src/router/preview-match.ts +170 -0
- package/src/router/revalidation.ts +137 -38
- package/src/router/router-context.ts +36 -21
- package/src/router/router-interfaces.ts +452 -0
- package/src/router/router-options.ts +592 -0
- package/src/router/router-registry.ts +24 -0
- package/src/router/segment-resolution/fresh.ts +570 -0
- package/src/router/segment-resolution/helpers.ts +263 -0
- package/src/router/segment-resolution/loader-cache.ts +198 -0
- package/src/router/segment-resolution/revalidation.ts +1239 -0
- package/src/router/segment-resolution/static-store.ts +67 -0
- package/src/router/segment-resolution.ts +21 -1315
- package/src/router/segment-wrappers.ts +289 -0
- package/src/router/telemetry-otel.ts +299 -0
- package/src/router/telemetry.ts +300 -0
- package/src/router/timeout.ts +148 -0
- package/src/router/trie-matching.ts +96 -29
- package/src/router/types.ts +16 -9
- package/src/router.ts +590 -1983
- package/src/rsc/handler-context.ts +45 -0
- package/src/rsc/handler.ts +661 -1015
- package/src/rsc/helpers.ts +140 -6
- package/src/rsc/index.ts +0 -20
- package/src/rsc/loader-fetch.ts +209 -0
- package/src/rsc/manifest-init.ts +86 -0
- package/src/rsc/nonce.ts +14 -0
- package/src/rsc/origin-guard.ts +141 -0
- package/src/rsc/progressive-enhancement.ts +379 -0
- package/src/rsc/response-error.ts +37 -0
- package/src/rsc/response-route-handler.ts +347 -0
- package/src/rsc/rsc-rendering.ts +237 -0
- package/src/rsc/runtime-warnings.ts +42 -0
- package/src/rsc/server-action.ts +348 -0
- package/src/rsc/ssr-setup.ts +128 -0
- package/src/rsc/types.ts +38 -11
- package/src/search-params.ts +230 -0
- package/src/segment-system.tsx +25 -13
- package/src/server/context.ts +173 -48
- package/src/server/cookie-store.ts +190 -0
- package/src/server/fetchable-loader-store.ts +37 -0
- package/src/server/handle-store.ts +94 -15
- package/src/server/loader-registry.ts +15 -56
- package/src/server/request-context.ts +430 -70
- package/src/server.ts +35 -155
- package/src/ssr/index.tsx +100 -31
- package/src/static-handler.ts +114 -0
- package/src/theme/ThemeProvider.tsx +21 -15
- package/src/theme/ThemeScript.tsx +5 -5
- package/src/theme/constants.ts +5 -2
- package/src/theme/index.ts +4 -14
- package/src/theme/theme-context.ts +4 -30
- package/src/theme/theme-script.ts +21 -18
- package/src/types/boundaries.ts +158 -0
- package/src/types/cache-types.ts +198 -0
- package/src/types/error-types.ts +192 -0
- package/src/types/global-namespace.ts +100 -0
- package/src/types/handler-context.ts +687 -0
- package/src/types/index.ts +88 -0
- package/src/types/loader-types.ts +183 -0
- package/src/types/route-config.ts +170 -0
- package/src/types/route-entry.ts +102 -0
- package/src/types/segments.ts +148 -0
- package/src/types.ts +1 -1757
- package/src/urls/include-helper.ts +197 -0
- package/src/urls/index.ts +53 -0
- package/src/urls/path-helper-types.ts +339 -0
- package/src/urls/path-helper.ts +329 -0
- package/src/urls/pattern-types.ts +95 -0
- package/src/urls/response-types.ts +106 -0
- package/src/urls/type-extraction.ts +372 -0
- package/src/urls/urls-function.ts +98 -0
- package/src/urls.ts +1 -1282
- package/src/use-loader.tsx +85 -77
- package/src/vite/discovery/bundle-postprocess.ts +184 -0
- package/src/vite/discovery/discover-routers.ts +344 -0
- package/src/vite/discovery/prerender-collection.ts +385 -0
- package/src/vite/discovery/route-types-writer.ts +258 -0
- package/src/vite/discovery/self-gen-tracking.ts +47 -0
- package/src/vite/discovery/state.ts +110 -0
- package/src/vite/discovery/virtual-module-codegen.ts +203 -0
- package/src/vite/index.ts +11 -1963
- package/src/vite/plugin-types.ts +131 -0
- package/src/vite/plugins/cjs-to-esm.ts +93 -0
- package/src/vite/plugins/client-ref-dedup.ts +115 -0
- package/src/vite/plugins/client-ref-hashing.ts +105 -0
- package/src/vite/{expose-action-id.ts → plugins/expose-action-id.ts} +72 -51
- package/src/vite/plugins/expose-id-utils.ts +287 -0
- package/src/vite/plugins/expose-ids/export-analysis.ts +296 -0
- package/src/vite/plugins/expose-ids/handler-transform.ts +179 -0
- package/src/vite/plugins/expose-ids/loader-transform.ts +74 -0
- package/src/vite/plugins/expose-ids/router-transform.ts +110 -0
- package/src/vite/plugins/expose-ids/types.ts +45 -0
- package/src/vite/plugins/expose-internal-ids.ts +569 -0
- package/src/vite/plugins/refresh-cmd.ts +65 -0
- package/src/vite/plugins/use-cache-transform.ts +323 -0
- package/src/vite/plugins/version-injector.ts +83 -0
- package/src/vite/plugins/version-plugin.ts +254 -0
- package/src/vite/{virtual-entries.ts → plugins/virtual-entries.ts} +23 -14
- package/src/vite/plugins/virtual-stub-plugin.ts +29 -0
- package/src/vite/rango.ts +510 -0
- package/src/vite/router-discovery.ts +785 -0
- package/src/vite/utils/ast-handler-extract.ts +517 -0
- package/src/vite/utils/banner.ts +36 -0
- package/src/vite/utils/bundle-analysis.ts +137 -0
- package/src/vite/utils/manifest-utils.ts +70 -0
- package/src/vite/{package-resolution.ts → utils/package-resolution.ts} +25 -29
- package/src/vite/utils/prerender-utils.ts +189 -0
- package/src/vite/utils/shared-utils.ts +169 -0
- package/CLAUDE.md +0 -43
- package/src/browser/lru-cache.ts +0 -69
- package/src/browser/request-controller.ts +0 -164
- package/src/cache/memory-store.ts +0 -253
- package/src/href-context.ts +0 -33
- package/src/router.gen.ts +0 -6
- package/src/urls.gen.ts +0 -8
- package/src/vite/expose-handle-id.ts +0 -209
- package/src/vite/expose-loader-id.ts +0 -426
- package/src/vite/expose-location-state-id.ts +0 -177
- package/src/vite/expose-prerender-handler-id.ts +0 -429
- /package/src/vite/{version.d.ts → plugins/version.d.ts} +0 -0
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
import type { RouteEntry, TrailingSlashMode } from "../types";
|
|
8
8
|
import type { EntryData } from "../server/context";
|
|
9
|
+
import { debugLog, isRouterDebugEnabled } from "./logging.js";
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* Parsed segment info
|
|
@@ -15,6 +16,7 @@ export interface ParsedSegment {
|
|
|
15
16
|
value: string; // static text, param name, or "*"
|
|
16
17
|
optional: boolean;
|
|
17
18
|
constraint?: string[]; // enum values like ["en", "gb"]
|
|
19
|
+
suffix?: string; // literal text after param in same segment (e.g., ".html")
|
|
18
20
|
}
|
|
19
21
|
|
|
20
22
|
/**
|
|
@@ -37,11 +39,22 @@ export function parsePattern(pattern: string): ParsedSegment[] {
|
|
|
37
39
|
// - :param(a|b)
|
|
38
40
|
// - :param(a|b)?
|
|
39
41
|
// - *
|
|
40
|
-
const segmentRegex =
|
|
42
|
+
const segmentRegex =
|
|
43
|
+
/\/(:([a-zA-Z_][a-zA-Z0-9_]*)(\(([^)]+)\))?(\?)?([^/]*)|(\*)|([^/]+))/g;
|
|
41
44
|
|
|
42
45
|
let match;
|
|
43
46
|
while ((match = segmentRegex.exec(pattern)) !== null) {
|
|
44
|
-
const [
|
|
47
|
+
const [
|
|
48
|
+
,
|
|
49
|
+
,
|
|
50
|
+
paramName,
|
|
51
|
+
,
|
|
52
|
+
constraint,
|
|
53
|
+
optional,
|
|
54
|
+
suffix,
|
|
55
|
+
wildcard,
|
|
56
|
+
staticText,
|
|
57
|
+
] = match;
|
|
45
58
|
|
|
46
59
|
if (wildcard) {
|
|
47
60
|
segments.push({ type: "wildcard", value: "*", optional: false });
|
|
@@ -51,6 +64,7 @@ export function parsePattern(pattern: string): ParsedSegment[] {
|
|
|
51
64
|
value: paramName,
|
|
52
65
|
optional: optional === "?",
|
|
53
66
|
constraint: constraint ? constraint.split("|") : undefined,
|
|
67
|
+
suffix: suffix || undefined,
|
|
54
68
|
});
|
|
55
69
|
} else if (staticText) {
|
|
56
70
|
segments.push({ type: "static", value: staticText, optional: false });
|
|
@@ -60,6 +74,48 @@ export function parsePattern(pattern: string): ParsedSegment[] {
|
|
|
60
74
|
return segments;
|
|
61
75
|
}
|
|
62
76
|
|
|
77
|
+
/**
|
|
78
|
+
* Compiled pattern result containing regex, param metadata, and trailing slash info.
|
|
79
|
+
*/
|
|
80
|
+
export interface CompiledPattern {
|
|
81
|
+
regex: RegExp;
|
|
82
|
+
paramNames: string[];
|
|
83
|
+
optionalParams: Set<string>;
|
|
84
|
+
hasTrailingSlash: boolean;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Module-level cache for compiled patterns. Route patterns are a finite set
|
|
88
|
+
// defined at build time, so this map is bounded by the number of routes.
|
|
89
|
+
const compiledPatternCache = new Map<string, CompiledPattern>();
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Get a compiled pattern from cache or compile and cache it.
|
|
93
|
+
* Avoids O(routes) regex compilations per request in the fallback path.
|
|
94
|
+
*/
|
|
95
|
+
export function getCompiledPattern(pattern: string): CompiledPattern {
|
|
96
|
+
let compiled = compiledPatternCache.get(pattern);
|
|
97
|
+
if (compiled) return compiled;
|
|
98
|
+
compiled = compilePattern(pattern);
|
|
99
|
+
compiledPatternCache.set(pattern, compiled);
|
|
100
|
+
return compiled;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Return the current size of the compiled pattern cache.
|
|
105
|
+
* Exposed for testing.
|
|
106
|
+
*/
|
|
107
|
+
export function getPatternCacheSize(): number {
|
|
108
|
+
return compiledPatternCache.size;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Clear the compiled pattern cache.
|
|
113
|
+
* Exposed for testing.
|
|
114
|
+
*/
|
|
115
|
+
export function clearPatternCache(): void {
|
|
116
|
+
compiledPatternCache.clear();
|
|
117
|
+
}
|
|
118
|
+
|
|
63
119
|
/**
|
|
64
120
|
* Compile a route pattern to regex
|
|
65
121
|
*
|
|
@@ -77,12 +133,7 @@ export function parsePattern(pattern: string): ParsedSegment[] {
|
|
|
77
133
|
* compilePattern("/:locale(en|gb)/blog") // matches /en/blog or /gb/blog
|
|
78
134
|
* compilePattern("/:locale(en|gb)?/blog") // matches /blog, /en/blog, or /gb/blog
|
|
79
135
|
*/
|
|
80
|
-
export function compilePattern(pattern: string): {
|
|
81
|
-
regex: RegExp;
|
|
82
|
-
paramNames: string[];
|
|
83
|
-
optionalParams: Set<string>;
|
|
84
|
-
hasTrailingSlash: boolean;
|
|
85
|
-
} {
|
|
136
|
+
export function compilePattern(pattern: string): CompiledPattern {
|
|
86
137
|
// Detect if pattern has trailing slash (but not just "/")
|
|
87
138
|
const hasTrailingSlash = pattern.length > 1 && pattern.endsWith("/");
|
|
88
139
|
// Remove trailing slash for parsing (we'll add it back to regex if needed)
|
|
@@ -100,16 +151,19 @@ export function compilePattern(pattern: string): {
|
|
|
100
151
|
regexPattern += "/(.*)";
|
|
101
152
|
} else if (segment.type === "param") {
|
|
102
153
|
paramNames.push(segment.value);
|
|
154
|
+
const suffixPattern = segment.suffix ? escapeRegex(segment.suffix) : "";
|
|
103
155
|
const valuePattern = segment.constraint
|
|
104
|
-
? `(${segment.constraint.join("|")})`
|
|
105
|
-
:
|
|
156
|
+
? `(${segment.constraint.map(escapeRegex).join("|")})`
|
|
157
|
+
: segment.suffix
|
|
158
|
+
? "([^/]+?)"
|
|
159
|
+
: "([^/]+)";
|
|
106
160
|
|
|
107
161
|
if (segment.optional) {
|
|
108
162
|
optionalParams.add(segment.value);
|
|
109
163
|
// Optional: make the whole /segment optional
|
|
110
|
-
regexPattern += `(?:/${valuePattern})?`;
|
|
164
|
+
regexPattern += `(?:/${valuePattern}${suffixPattern})?`;
|
|
111
165
|
} else {
|
|
112
|
-
regexPattern += `/${valuePattern}`;
|
|
166
|
+
regexPattern += `/${valuePattern}${suffixPattern}`;
|
|
113
167
|
}
|
|
114
168
|
} else {
|
|
115
169
|
// Static segment
|
|
@@ -239,7 +293,7 @@ export interface LazyEvaluationNeeded<TEnv = any> {
|
|
|
239
293
|
* Type guard to check if result is a lazy evaluation needed response
|
|
240
294
|
*/
|
|
241
295
|
export function isLazyEvaluationNeeded<TEnv>(
|
|
242
|
-
result: RouteMatchResult<TEnv> | LazyEvaluationNeeded<TEnv> | null
|
|
296
|
+
result: RouteMatchResult<TEnv> | LazyEvaluationNeeded<TEnv> | null,
|
|
243
297
|
): result is LazyEvaluationNeeded<TEnv> {
|
|
244
298
|
return result !== null && "lazyEntry" in result;
|
|
245
299
|
}
|
|
@@ -260,22 +314,33 @@ export function enableMatchDebug(enabled: boolean): void {
|
|
|
260
314
|
}
|
|
261
315
|
|
|
262
316
|
export function getMatchDebugStats(): MatchDebugStats {
|
|
263
|
-
return {
|
|
317
|
+
return {
|
|
318
|
+
entriesChecked: debugStats.entriesChecked,
|
|
319
|
+
entriesSkipped: debugStats.entriesSkipped,
|
|
320
|
+
routesChecked: debugStats.routesChecked,
|
|
321
|
+
};
|
|
264
322
|
}
|
|
265
323
|
|
|
266
324
|
export function findMatch<TEnv>(
|
|
267
325
|
pathname: string,
|
|
268
|
-
routesEntries: RouteEntry<TEnv>[]
|
|
326
|
+
routesEntries: RouteEntry<TEnv>[],
|
|
269
327
|
): RouteMatchResult<TEnv> | LazyEvaluationNeeded<TEnv> | null {
|
|
270
|
-
|
|
328
|
+
const effectiveDebug = debugEnabled || isRouterDebugEnabled();
|
|
329
|
+
|
|
330
|
+
if (effectiveDebug) {
|
|
271
331
|
debugStats = { entriesChecked: 0, entriesSkipped: 0, routesChecked: 0 };
|
|
272
|
-
|
|
332
|
+
debugLog("findMatch", "start", { pathname, entries: routesEntries.length });
|
|
273
333
|
for (const e of routesEntries) {
|
|
274
|
-
|
|
334
|
+
debugLog("findMatch", "entry", {
|
|
335
|
+
prefix: e.prefix,
|
|
336
|
+
staticPrefix: e.staticPrefix,
|
|
337
|
+
routeCount: Object.keys(e.routes).length,
|
|
338
|
+
});
|
|
275
339
|
}
|
|
276
340
|
}
|
|
277
341
|
|
|
278
|
-
const pathnameHasTrailingSlash =
|
|
342
|
+
const pathnameHasTrailingSlash =
|
|
343
|
+
pathname.length > 1 && pathname.endsWith("/");
|
|
279
344
|
// Try alternate pathname for redirect matching
|
|
280
345
|
const alternatePathname = pathnameHasTrailingSlash
|
|
281
346
|
? pathname.slice(0, -1)
|
|
@@ -285,9 +350,12 @@ export function findMatch<TEnv>(
|
|
|
285
350
|
// Short-circuit: skip entry if pathname doesn't start with static prefix
|
|
286
351
|
// staticPrefix is pre-computed at registration time, so this is O(1)
|
|
287
352
|
if (entry.staticPrefix && !pathname.startsWith(entry.staticPrefix)) {
|
|
288
|
-
if (
|
|
353
|
+
if (effectiveDebug) {
|
|
289
354
|
debugStats.entriesSkipped++;
|
|
290
|
-
|
|
355
|
+
debugLog("findMatch", "skipped entry", {
|
|
356
|
+
prefix: entry.prefix,
|
|
357
|
+
staticPrefix: entry.staticPrefix,
|
|
358
|
+
});
|
|
291
359
|
}
|
|
292
360
|
continue;
|
|
293
361
|
}
|
|
@@ -295,20 +363,22 @@ export function findMatch<TEnv>(
|
|
|
295
363
|
// Check if this is a lazy entry that needs evaluation
|
|
296
364
|
// When staticPrefix matches but routes are not yet populated, signal caller to evaluate
|
|
297
365
|
if (entry.lazy && !entry.lazyEvaluated) {
|
|
298
|
-
if (
|
|
299
|
-
|
|
366
|
+
if (effectiveDebug) {
|
|
367
|
+
debugLog("findMatch", "lazy entry requires evaluation", {
|
|
368
|
+
staticPrefix: entry.staticPrefix,
|
|
369
|
+
});
|
|
300
370
|
}
|
|
301
371
|
return { lazyEntry: entry };
|
|
302
372
|
}
|
|
303
373
|
|
|
304
|
-
if (
|
|
374
|
+
if (effectiveDebug) {
|
|
305
375
|
debugStats.entriesChecked++;
|
|
306
376
|
}
|
|
307
377
|
|
|
308
378
|
const routeEntries = Object.entries(entry.routes);
|
|
309
379
|
|
|
310
380
|
for (const [routeKey, pattern] of routeEntries) {
|
|
311
|
-
if (
|
|
381
|
+
if (effectiveDebug) {
|
|
312
382
|
debugStats.routesChecked++;
|
|
313
383
|
}
|
|
314
384
|
|
|
@@ -322,11 +392,20 @@ export function findMatch<TEnv>(
|
|
|
322
392
|
fullPattern = entry.prefix + pattern;
|
|
323
393
|
}
|
|
324
394
|
|
|
325
|
-
const { regex, paramNames, optionalParams, hasTrailingSlash } =
|
|
395
|
+
const { regex, paramNames, optionalParams, hasTrailingSlash } =
|
|
396
|
+
getCompiledPattern(fullPattern);
|
|
326
397
|
|
|
327
398
|
// Get trailing slash mode for this route (per-route config or pattern-based)
|
|
328
|
-
const trailingSlashMode: TrailingSlashMode | undefined =
|
|
399
|
+
const trailingSlashMode: TrailingSlashMode | undefined =
|
|
400
|
+
entry.trailingSlash?.[routeKey];
|
|
329
401
|
|
|
402
|
+
// Prerender flag from entry metadata (set by urls() for prerender handlers)
|
|
403
|
+
const prFlag = entry.prerenderRouteKeys?.has(routeKey)
|
|
404
|
+
? { pr: true as const }
|
|
405
|
+
: {};
|
|
406
|
+
const ptFlag = entry.passthroughRouteKeys?.has(routeKey)
|
|
407
|
+
? { pt: true as const }
|
|
408
|
+
: {};
|
|
330
409
|
|
|
331
410
|
// Try exact match first
|
|
332
411
|
const match = regex.exec(pathname);
|
|
@@ -336,21 +415,51 @@ export function findMatch<TEnv>(
|
|
|
336
415
|
params[name] = match[index + 1] ?? "";
|
|
337
416
|
});
|
|
338
417
|
|
|
339
|
-
if (
|
|
340
|
-
|
|
341
|
-
|
|
418
|
+
if (effectiveDebug) {
|
|
419
|
+
debugLog("findMatch", "matched route", {
|
|
420
|
+
routeKey,
|
|
421
|
+
pattern: fullPattern,
|
|
422
|
+
stats: { ...debugStats },
|
|
423
|
+
});
|
|
342
424
|
}
|
|
343
425
|
|
|
344
426
|
// Check if trailing slash mode requires redirect even on exact match
|
|
345
|
-
if (
|
|
427
|
+
if (
|
|
428
|
+
trailingSlashMode === "always" &&
|
|
429
|
+
!pathnameHasTrailingSlash &&
|
|
430
|
+
pathname !== "/"
|
|
431
|
+
) {
|
|
346
432
|
// Mode says always have trailing slash, but pathname doesn't have it
|
|
347
|
-
return {
|
|
433
|
+
return {
|
|
434
|
+
entry,
|
|
435
|
+
routeKey,
|
|
436
|
+
params,
|
|
437
|
+
optionalParams,
|
|
438
|
+
redirectTo: pathname + "/",
|
|
439
|
+
...prFlag,
|
|
440
|
+
...ptFlag,
|
|
441
|
+
};
|
|
348
442
|
} else if (trailingSlashMode === "never" && pathnameHasTrailingSlash) {
|
|
349
443
|
// Mode says never have trailing slash, but pathname has it
|
|
350
|
-
return {
|
|
444
|
+
return {
|
|
445
|
+
entry,
|
|
446
|
+
routeKey,
|
|
447
|
+
params,
|
|
448
|
+
optionalParams,
|
|
449
|
+
redirectTo: pathname.slice(0, -1),
|
|
450
|
+
...prFlag,
|
|
451
|
+
...ptFlag,
|
|
452
|
+
};
|
|
351
453
|
}
|
|
352
454
|
|
|
353
|
-
return {
|
|
455
|
+
return {
|
|
456
|
+
entry,
|
|
457
|
+
routeKey,
|
|
458
|
+
params,
|
|
459
|
+
optionalParams,
|
|
460
|
+
...prFlag,
|
|
461
|
+
...ptFlag,
|
|
462
|
+
};
|
|
354
463
|
}
|
|
355
464
|
|
|
356
465
|
// Try alternate pathname (opposite trailing slash)
|
|
@@ -364,24 +473,71 @@ export function findMatch<TEnv>(
|
|
|
364
473
|
// Determine redirect behavior based on mode
|
|
365
474
|
if (trailingSlashMode === "ignore") {
|
|
366
475
|
// Match without redirect
|
|
367
|
-
return {
|
|
476
|
+
return {
|
|
477
|
+
entry,
|
|
478
|
+
routeKey,
|
|
479
|
+
params,
|
|
480
|
+
optionalParams,
|
|
481
|
+
...prFlag,
|
|
482
|
+
...ptFlag,
|
|
483
|
+
};
|
|
368
484
|
} else if (trailingSlashMode === "never") {
|
|
369
485
|
// Redirect to no trailing slash
|
|
370
486
|
if (pathnameHasTrailingSlash) {
|
|
371
|
-
return {
|
|
487
|
+
return {
|
|
488
|
+
entry,
|
|
489
|
+
routeKey,
|
|
490
|
+
params,
|
|
491
|
+
optionalParams,
|
|
492
|
+
redirectTo: alternatePathname,
|
|
493
|
+
...prFlag,
|
|
494
|
+
...ptFlag,
|
|
495
|
+
};
|
|
372
496
|
}
|
|
373
|
-
return {
|
|
497
|
+
return {
|
|
498
|
+
entry,
|
|
499
|
+
routeKey,
|
|
500
|
+
params,
|
|
501
|
+
optionalParams,
|
|
502
|
+
...prFlag,
|
|
503
|
+
...ptFlag,
|
|
504
|
+
};
|
|
374
505
|
} else if (trailingSlashMode === "always") {
|
|
375
506
|
// Redirect to with trailing slash
|
|
376
507
|
if (!pathnameHasTrailingSlash) {
|
|
377
|
-
return {
|
|
508
|
+
return {
|
|
509
|
+
entry,
|
|
510
|
+
routeKey,
|
|
511
|
+
params,
|
|
512
|
+
optionalParams,
|
|
513
|
+
redirectTo: alternatePathname,
|
|
514
|
+
...prFlag,
|
|
515
|
+
...ptFlag,
|
|
516
|
+
};
|
|
378
517
|
}
|
|
379
|
-
return {
|
|
518
|
+
return {
|
|
519
|
+
entry,
|
|
520
|
+
routeKey,
|
|
521
|
+
params,
|
|
522
|
+
optionalParams,
|
|
523
|
+
...prFlag,
|
|
524
|
+
...ptFlag,
|
|
525
|
+
};
|
|
380
526
|
} else {
|
|
381
527
|
// No explicit mode - use pattern-based detection
|
|
382
528
|
// Redirect to canonical form (what the pattern defines)
|
|
383
|
-
const canonicalPath = hasTrailingSlash
|
|
384
|
-
|
|
529
|
+
const canonicalPath = hasTrailingSlash
|
|
530
|
+
? alternatePathname
|
|
531
|
+
: pathname.slice(0, -1);
|
|
532
|
+
return {
|
|
533
|
+
entry,
|
|
534
|
+
routeKey,
|
|
535
|
+
params,
|
|
536
|
+
optionalParams,
|
|
537
|
+
redirectTo: canonicalPath,
|
|
538
|
+
...prFlag,
|
|
539
|
+
...ptFlag,
|
|
540
|
+
};
|
|
385
541
|
}
|
|
386
542
|
}
|
|
387
543
|
}
|