@rangojs/router 0.0.0-experimental.b9cb8739 → 0.0.0-experimental.bd6e11bc
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/README.md +196 -43
- package/dist/bin/rango.js +277 -99
- package/dist/testing/vitest.js +48 -0
- package/dist/vite/index.js +2779 -1064
- package/dist/vite/index.js.bak +5448 -0
- package/dist/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
- package/package.json +57 -11
- package/skills/breadcrumbs/SKILL.md +3 -1
- package/skills/bundle-analysis/SKILL.md +159 -0
- package/skills/cache-guide/SKILL.md +243 -21
- package/skills/caching/SKILL.md +155 -6
- package/skills/composability/SKILL.md +27 -2
- package/skills/document-cache/SKILL.md +78 -55
- package/skills/handler-use/SKILL.md +364 -0
- package/skills/hooks/SKILL.md +229 -20
- package/skills/host-router/SKILL.md +45 -20
- package/skills/i18n/SKILL.md +276 -0
- package/skills/intercept/SKILL.md +46 -4
- package/skills/layout/SKILL.md +28 -7
- package/skills/links/SKILL.md +249 -17
- package/skills/loader/SKILL.md +273 -53
- package/skills/middleware/SKILL.md +49 -12
- package/skills/migrate-nextjs/SKILL.md +562 -0
- package/skills/migrate-react-router/SKILL.md +769 -0
- package/skills/mime-routes/SKILL.md +27 -0
- package/skills/observability/SKILL.md +137 -0
- package/skills/parallel/SKILL.md +197 -6
- package/skills/prerender/SKILL.md +123 -100
- package/skills/rango/SKILL.md +242 -22
- package/skills/react-compiler/SKILL.md +168 -0
- package/skills/response-routes/SKILL.md +66 -9
- package/skills/route/SKILL.md +88 -4
- package/skills/router-setup/SKILL.md +90 -5
- package/skills/server-actions/SKILL.md +751 -0
- package/skills/streams-and-websockets/SKILL.md +283 -0
- package/skills/testing/SKILL.md +716 -0
- package/skills/typesafety/SKILL.md +329 -27
- package/skills/use-cache/SKILL.md +34 -5
- package/skills/view-transitions/SKILL.md +294 -0
- package/src/__augment-tests__/augment.ts +81 -0
- package/src/__augment-tests__/augmented.check.ts +117 -0
- package/src/__internal.ts +1 -1
- package/src/browser/action-coordinator.ts +53 -36
- package/src/browser/app-shell.ts +52 -0
- package/src/browser/app-version.ts +14 -0
- package/src/browser/event-controller.ts +91 -70
- package/src/browser/history-state.ts +21 -0
- package/src/browser/index.ts +3 -3
- package/src/browser/navigation-bridge.ts +102 -16
- package/src/browser/navigation-client.ts +164 -59
- package/src/browser/navigation-store.ts +75 -17
- package/src/browser/navigation-transaction.ts +21 -37
- package/src/browser/partial-update.ts +139 -38
- package/src/browser/prefetch/cache.ts +175 -15
- package/src/browser/prefetch/fetch.ts +180 -33
- package/src/browser/prefetch/queue.ts +123 -20
- package/src/browser/prefetch/resource-ready.ts +77 -0
- package/src/browser/rango-state.ts +53 -13
- package/src/browser/react/Link.tsx +81 -9
- package/src/browser/react/NavigationProvider.tsx +110 -33
- package/src/browser/react/context.ts +7 -2
- package/src/browser/react/filter-segment-order.ts +51 -7
- package/src/browser/react/index.ts +3 -0
- package/src/browser/react/location-state-shared.ts +175 -4
- package/src/browser/react/location-state.ts +39 -13
- package/src/browser/react/use-handle.ts +23 -64
- package/src/browser/react/use-navigation.ts +22 -2
- package/src/browser/react/use-params.ts +20 -8
- package/src/browser/react/use-reverse.ts +106 -0
- package/src/browser/react/use-router.ts +43 -10
- package/src/browser/react/use-segments.ts +11 -8
- package/src/browser/response-adapter.ts +25 -0
- package/src/browser/rsc-router.tsx +191 -74
- package/src/browser/scroll-restoration.ts +41 -14
- package/src/browser/segment-reconciler.ts +36 -9
- package/src/browser/segment-structure-assert.ts +2 -2
- package/src/browser/server-action-bridge.ts +31 -36
- package/src/browser/types.ts +57 -5
- package/src/build/collect-fallback-refs.ts +107 -0
- package/src/build/generate-manifest.ts +65 -40
- package/src/build/generate-route-types.ts +5 -0
- package/src/build/index.ts +2 -0
- package/src/build/route-trie.ts +52 -25
- package/src/build/route-types/codegen.ts +4 -4
- package/src/build/route-types/include-resolution.ts +9 -2
- package/src/build/route-types/per-module-writer.ts +7 -4
- package/src/build/route-types/router-processing.ts +278 -88
- package/src/build/route-types/scan-filter.ts +9 -2
- package/src/build/route-types/source-scan.ts +118 -0
- package/src/build/runtime-discovery.ts +9 -20
- package/src/cache/cache-runtime.ts +15 -11
- package/src/cache/cache-scope.ts +76 -49
- package/src/cache/cf/cf-cache-store.ts +501 -18
- package/src/cache/cf/index.ts +5 -1
- package/src/cache/document-cache.ts +17 -7
- package/src/cache/index.ts +1 -0
- package/src/cache/taint.ts +55 -0
- package/src/client.rsc.tsx +3 -0
- package/src/client.tsx +94 -238
- package/src/context-var.ts +72 -2
- package/src/debug.ts +2 -2
- package/src/decode-loader-results.ts +36 -0
- package/src/errors.ts +30 -1
- package/src/handle.ts +65 -12
- package/src/host/index.ts +2 -2
- package/src/host/router.ts +129 -57
- package/src/host/types.ts +31 -2
- package/src/host/utils.ts +1 -1
- package/src/href-client.ts +140 -20
- package/src/index.rsc.ts +12 -5
- package/src/index.ts +61 -11
- package/src/loader-store.ts +500 -0
- package/src/loader.rsc.ts +2 -5
- package/src/loader.ts +3 -10
- package/src/missing-id-error.ts +68 -0
- package/src/outlet-context.ts +1 -1
- package/src/prerender/store.ts +5 -4
- package/src/prerender.ts +141 -80
- package/src/response-utils.ts +37 -0
- package/src/reverse.ts +65 -15
- package/src/route-content-wrapper.tsx +6 -28
- package/src/route-definition/dsl-helpers.ts +435 -260
- package/src/route-definition/helper-factories.ts +29 -139
- package/src/route-definition/helpers-types.ts +110 -34
- package/src/route-definition/index.ts +3 -0
- package/src/route-definition/redirect.ts +11 -3
- package/src/route-definition/resolve-handler-use.ts +155 -0
- package/src/route-definition/use-item-types.ts +32 -0
- package/src/route-map-builder.ts +7 -1
- package/src/route-types.ts +37 -41
- package/src/router/basename.ts +14 -0
- package/src/router/content-negotiation.ts +113 -1
- package/src/router/error-handling.ts +1 -1
- package/src/router/find-match.ts +4 -2
- package/src/router/handler-context.ts +77 -38
- package/src/router/intercept-resolution.ts +15 -22
- package/src/router/lazy-includes.ts +12 -9
- package/src/router/loader-resolution.ts +174 -22
- package/src/router/logging.ts +5 -2
- package/src/router/manifest.ts +31 -16
- package/src/router/match-api.ts +128 -192
- package/src/router/match-handlers.ts +63 -20
- package/src/router/match-middleware/background-revalidation.ts +30 -2
- package/src/router/match-middleware/cache-lookup.ts +136 -106
- package/src/router/match-middleware/cache-store.ts +54 -10
- package/src/router/match-middleware/intercept-resolution.ts +9 -7
- package/src/router/match-middleware/segment-resolution.ts +61 -5
- package/src/router/match-result.ts +125 -10
- package/src/router/metrics.ts +7 -2
- package/src/router/middleware-types.ts +21 -34
- package/src/router/middleware.ts +103 -90
- package/src/router/navigation-snapshot.ts +182 -0
- package/src/router/pattern-matching.ts +101 -17
- package/src/router/prerender-match.ts +110 -10
- package/src/router/preview-match.ts +32 -102
- package/src/router/request-classification.ts +286 -0
- package/src/router/revalidation.ts +58 -2
- package/src/router/route-snapshot.ts +245 -0
- package/src/router/router-context.ts +6 -1
- package/src/router/router-interfaces.ts +77 -28
- package/src/router/router-options.ts +76 -11
- package/src/router/router-registry.ts +2 -5
- package/src/router/segment-resolution/fresh.ts +223 -24
- package/src/router/segment-resolution/helpers.ts +29 -24
- package/src/router/segment-resolution/loader-cache.ts +1 -0
- package/src/router/segment-resolution/revalidation.ts +466 -285
- package/src/router/segment-resolution/view-transition-default.ts +36 -0
- package/src/router/segment-wrappers.ts +2 -0
- package/src/router/substitute-pattern-params.ts +56 -0
- package/src/router/telemetry.ts +99 -0
- package/src/router/trie-matching.ts +18 -13
- package/src/router/types.ts +9 -0
- package/src/router/url-params.ts +49 -0
- package/src/router.ts +91 -23
- package/src/rsc/handler-context.ts +2 -2
- package/src/rsc/handler.ts +440 -381
- package/src/rsc/helpers.ts +91 -43
- package/src/rsc/index.ts +1 -1
- package/src/rsc/loader-fetch.ts +23 -3
- package/src/rsc/manifest-init.ts +5 -1
- package/src/rsc/origin-guard.ts +28 -10
- package/src/rsc/progressive-enhancement.ts +18 -2
- package/src/rsc/response-route-handler.ts +46 -53
- package/src/rsc/rsc-rendering.ts +41 -48
- package/src/rsc/runtime-warnings.ts +9 -10
- package/src/rsc/server-action.ts +25 -37
- package/src/rsc/ssr-setup.ts +18 -2
- package/src/rsc/types.ts +17 -3
- package/src/search-params.ts +4 -4
- package/src/segment-content-promise.ts +67 -0
- package/src/segment-loader-promise.ts +122 -0
- package/src/segment-system.tsx +219 -67
- package/src/serialize.ts +243 -0
- package/src/server/context.ts +277 -61
- package/src/server/cookie-store.ts +28 -4
- package/src/server/handle-store.ts +19 -0
- package/src/server/loader-registry.ts +9 -8
- package/src/server/request-context.ts +204 -60
- package/src/ssr/index.tsx +9 -1
- package/src/static-handler.ts +19 -7
- package/src/testing/cache-status.ts +166 -0
- package/src/testing/collect-handle.ts +63 -0
- package/src/testing/dispatch.ts +440 -0
- package/src/testing/dom.entry.ts +22 -0
- package/src/testing/e2e/fixture.ts +154 -0
- package/src/testing/e2e/index.ts +149 -0
- package/src/testing/e2e/matchers.ts +51 -0
- package/src/testing/e2e/page-helpers.ts +272 -0
- package/src/testing/e2e/parity.ts +306 -0
- package/src/testing/e2e/server.ts +183 -0
- package/src/testing/flight-matchers.ts +104 -0
- package/src/testing/flight-runtime.d.ts +21 -0
- package/src/testing/flight.entry.ts +22 -0
- package/src/testing/flight.ts +182 -0
- package/src/testing/generated-routes.ts +223 -0
- package/src/testing/index.ts +106 -0
- package/src/testing/internal/context.ts +255 -0
- package/src/testing/render-route.tsx +565 -0
- package/src/testing/run-loader.ts +296 -0
- package/src/testing/run-middleware.ts +179 -0
- package/src/testing/vitest-stubs/cloudflare-email.ts +9 -0
- package/src/testing/vitest-stubs/cloudflare-workers.ts +21 -0
- package/src/testing/vitest-stubs/plugin-rsc.ts +16 -0
- package/src/testing/vitest-stubs/version.ts +5 -0
- package/src/testing/vitest.ts +183 -0
- package/src/types/cache-types.ts +4 -4
- package/src/types/global-namespace.ts +39 -26
- package/src/types/handler-context.ts +194 -72
- package/src/types/index.ts +1 -0
- package/src/types/loader-types.ts +41 -15
- package/src/types/request-scope.ts +126 -0
- package/src/types/route-entry.ts +19 -1
- package/src/types/segments.ts +37 -1
- package/src/urls/include-helper.ts +34 -67
- package/src/urls/index.ts +0 -3
- package/src/urls/path-helper-types.ts +50 -9
- package/src/urls/path-helper.ts +63 -63
- package/src/urls/pattern-types.ts +48 -19
- package/src/urls/response-types.ts +25 -22
- package/src/urls/type-extraction.ts +26 -116
- package/src/urls/urls-function.ts +1 -5
- package/src/use-loader.tsx +487 -44
- package/src/vite/debug.ts +185 -0
- package/src/vite/discovery/bundle-postprocess.ts +34 -37
- package/src/vite/discovery/discover-routers.ts +105 -51
- package/src/vite/discovery/discovery-errors.ts +194 -0
- package/src/vite/discovery/gate-state.ts +171 -0
- package/src/vite/discovery/prerender-collection.ts +188 -93
- package/src/vite/discovery/route-types-writer.ts +40 -84
- package/src/vite/discovery/self-gen-tracking.ts +27 -1
- package/src/vite/discovery/state.ts +46 -6
- package/src/vite/discovery/virtual-module-codegen.ts +13 -23
- package/src/vite/index.ts +6 -0
- package/src/vite/plugin-types.ts +111 -72
- package/src/vite/plugins/cjs-to-esm.ts +8 -7
- package/src/vite/plugins/client-ref-dedup.ts +16 -0
- package/src/vite/plugins/client-ref-hashing.ts +28 -5
- package/src/vite/plugins/cloudflare-protocol-loader-hook.d.mts +23 -0
- package/src/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
- package/src/vite/plugins/cloudflare-protocol-stub.ts +214 -0
- package/src/vite/plugins/expose-action-id.ts +55 -33
- package/src/vite/plugins/expose-id-utils.ts +24 -8
- package/src/vite/plugins/expose-ids/export-analysis.ts +100 -20
- package/src/vite/plugins/expose-ids/handler-transform.ts +12 -35
- package/src/vite/plugins/expose-ids/loader-transform.ts +3 -5
- package/src/vite/plugins/expose-ids/router-transform.ts +20 -3
- package/src/vite/plugins/expose-internal-ids.ts +544 -317
- package/src/vite/plugins/performance-tracks.ts +92 -0
- package/src/vite/plugins/refresh-cmd.ts +88 -26
- package/src/vite/plugins/use-cache-transform.ts +65 -50
- package/src/vite/plugins/version-injector.ts +39 -23
- package/src/vite/plugins/version-plugin.ts +72 -3
- package/src/vite/plugins/virtual-entries.ts +2 -2
- package/src/vite/rango.ts +265 -226
- package/src/vite/router-discovery.ts +920 -137
- package/src/vite/utils/ast-handler-extract.ts +15 -15
- package/src/vite/utils/banner.ts +4 -4
- package/src/vite/utils/bundle-analysis.ts +4 -2
- package/src/vite/utils/client-chunks.ts +190 -0
- package/src/vite/utils/forward-user-plugins.ts +193 -0
- package/src/vite/utils/manifest-utils.ts +21 -5
- package/src/vite/utils/package-resolution.ts +41 -1
- package/src/vite/utils/prerender-utils.ts +38 -5
- package/src/vite/utils/shared-utils.ts +109 -27
- package/src/browser/action-response-classifier.ts +0 -99
|
@@ -11,11 +11,12 @@
|
|
|
11
11
|
import type { UrlPatterns } from "../urls.js";
|
|
12
12
|
import type { AllUseItems } from "../route-types.js";
|
|
13
13
|
import { extractStaticPrefix } from "../router/pattern-matching.js";
|
|
14
|
-
import {
|
|
14
|
+
import { RangoContext, runWithPrefixes } from "../server/context.js";
|
|
15
15
|
import type { EntryData, TrackedInclude } from "../server/context.js";
|
|
16
16
|
import type { TrailingSlashMode } from "../types.js";
|
|
17
17
|
import { createRouteHelpers } from "../route-definition.js";
|
|
18
18
|
import MapRootLayout from "../server/root-layout.js";
|
|
19
|
+
import { collectFallbackClientRefs } from "./collect-fallback-refs.js";
|
|
19
20
|
|
|
20
21
|
/**
|
|
21
22
|
* Node in the prefix tree
|
|
@@ -45,7 +46,7 @@ export interface GeneratedManifest {
|
|
|
45
46
|
routeTrailingSlash?: Record<string, string>;
|
|
46
47
|
/** Route names using Prerender (for dev-mode Node.js delegation) */
|
|
47
48
|
prerenderRoutes?: string[];
|
|
48
|
-
/** Route names with
|
|
49
|
+
/** Route names wrapped with Passthrough() (live handler for runtime fallback) */
|
|
49
50
|
passthroughRoutes?: string[];
|
|
50
51
|
/** Route name → response type for non-RSC routes */
|
|
51
52
|
responseTypeRoutes?: Record<string, string>;
|
|
@@ -57,6 +58,26 @@ export interface GeneratedManifest {
|
|
|
57
58
|
* Build prefix tree node by running the patterns with proper context.
|
|
58
59
|
* Uses a visited set to detect circular includes and prevent infinite recursion.
|
|
59
60
|
*/
|
|
61
|
+
// Merge tracked nested includes into `target`. Multiple includes can share a
|
|
62
|
+
// fullPrefix (e.g. include("/", a), include("/", b)) — concat their routes and
|
|
63
|
+
// Object.assign children rather than overwrite.
|
|
64
|
+
function mergeIncludeNodes(
|
|
65
|
+
target: Record<string, PrefixTreeNode>,
|
|
66
|
+
includes: TrackedInclude[],
|
|
67
|
+
buildChild: (include: TrackedInclude) => PrefixTreeNode,
|
|
68
|
+
): void {
|
|
69
|
+
for (const include of includes) {
|
|
70
|
+
const node = buildChild(include);
|
|
71
|
+
const existing = target[include.fullPrefix];
|
|
72
|
+
if (existing) {
|
|
73
|
+
existing.routes.push(...node.routes);
|
|
74
|
+
Object.assign(existing.children, node.children);
|
|
75
|
+
} else {
|
|
76
|
+
target[include.fullPrefix] = node;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
60
81
|
function buildPrefixTreeNode(
|
|
61
82
|
urlPrefix: string,
|
|
62
83
|
namePrefix: string | undefined,
|
|
@@ -93,7 +114,7 @@ function buildPrefixTreeNode(
|
|
|
93
114
|
const searchSchemasMap = new Map<string, Record<string, string>>();
|
|
94
115
|
const trackedIncludes: TrackedInclude[] = [];
|
|
95
116
|
|
|
96
|
-
|
|
117
|
+
RangoContext.run(
|
|
97
118
|
{
|
|
98
119
|
manifest,
|
|
99
120
|
patterns: patternsMap,
|
|
@@ -150,10 +171,7 @@ function buildPrefixTreeNode(
|
|
|
150
171
|
if (prerenderDefs && entry.prerenderDef) {
|
|
151
172
|
prerenderDefs[name] = entry.prerenderDef;
|
|
152
173
|
}
|
|
153
|
-
if (
|
|
154
|
-
passthroughRoutes &&
|
|
155
|
-
entry.prerenderDef?.options?.passthrough === true
|
|
156
|
-
) {
|
|
174
|
+
if (passthroughRoutes && entry.isPassthrough === true) {
|
|
157
175
|
passthroughRoutes.push(name);
|
|
158
176
|
}
|
|
159
177
|
}
|
|
@@ -169,13 +187,9 @@ function buildPrefixTreeNode(
|
|
|
169
187
|
}
|
|
170
188
|
}
|
|
171
189
|
|
|
172
|
-
// Build children from tracked nested includes.
|
|
173
|
-
// Multiple includes can share the same fullPrefix (e.g., include("/", patternsA),
|
|
174
|
-
// include("/", patternsB)). Merge their routes instead of overwriting.
|
|
175
190
|
const children: Record<string, PrefixTreeNode> = {};
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
const childNode = buildPrefixTreeNode(
|
|
191
|
+
mergeIncludeNodes(children, trackedIncludes, (include) =>
|
|
192
|
+
buildPrefixTreeNode(
|
|
179
193
|
include.fullPrefix,
|
|
180
194
|
include.namePrefix,
|
|
181
195
|
include.patterns as UrlPatterns<any>,
|
|
@@ -189,16 +203,8 @@ function buildPrefixTreeNode(
|
|
|
189
203
|
passthroughRoutes,
|
|
190
204
|
responseTypeRoutes,
|
|
191
205
|
routeSearchSchemas,
|
|
192
|
-
)
|
|
193
|
-
|
|
194
|
-
const existing = children[include.fullPrefix];
|
|
195
|
-
if (existing) {
|
|
196
|
-
existing.routes.push(...childNode.routes);
|
|
197
|
-
Object.assign(existing.children, childNode.children);
|
|
198
|
-
} else {
|
|
199
|
-
children[include.fullPrefix] = childNode;
|
|
200
|
-
}
|
|
201
|
-
}
|
|
206
|
+
),
|
|
207
|
+
);
|
|
202
208
|
|
|
203
209
|
// Remove from visited so sibling branches can reuse the same patterns
|
|
204
210
|
// without false circular-include detection. Only ancestors in the current
|
|
@@ -285,6 +291,17 @@ export function generateManifest<TEnv>(
|
|
|
285
291
|
export function generateManifestFull<TEnv>(
|
|
286
292
|
urlpatterns: UrlPatterns<TEnv, any>,
|
|
287
293
|
mountIndex: number = 0,
|
|
294
|
+
options?: {
|
|
295
|
+
urlPrefix?: string;
|
|
296
|
+
/**
|
|
297
|
+
* Called once per `"use client"` component registered as an
|
|
298
|
+
* errorBoundary/notFoundBoundary fallback, with its client-reference key
|
|
299
|
+
* (`$$id`). Lets the build collect fallback module ids for dedicated
|
|
300
|
+
* chunking without exposing the otherwise-discarded EntryData tree. The
|
|
301
|
+
* EntryData map built below is local; this is the only seam that surfaces it.
|
|
302
|
+
*/
|
|
303
|
+
collectClientFallbackRef?: (refKey: string) => void;
|
|
304
|
+
},
|
|
288
305
|
): FullManifest {
|
|
289
306
|
const routeManifest: Record<string, string> = {};
|
|
290
307
|
const routeAncestry: Record<string, string[]> = {};
|
|
@@ -298,7 +315,7 @@ export function generateManifestFull<TEnv>(
|
|
|
298
315
|
const searchSchemasMap = new Map<string, Record<string, string>>();
|
|
299
316
|
const trackedIncludes: TrackedInclude[] = [];
|
|
300
317
|
|
|
301
|
-
|
|
318
|
+
RangoContext.run(
|
|
302
319
|
{
|
|
303
320
|
manifest,
|
|
304
321
|
patterns: patternsMap,
|
|
@@ -310,6 +327,8 @@ export function generateManifestFull<TEnv>(
|
|
|
310
327
|
counters: {},
|
|
311
328
|
mountIndex,
|
|
312
329
|
trackedIncludes, // Enable include tracking
|
|
330
|
+
// basename sets the initial URL prefix for all path() registrations
|
|
331
|
+
...(options?.urlPrefix ? { urlPrefix: options.urlPrefix } : {}),
|
|
313
332
|
},
|
|
314
333
|
() => {
|
|
315
334
|
const helpers = createRouteHelpers();
|
|
@@ -320,6 +339,22 @@ export function generateManifestFull<TEnv>(
|
|
|
320
339
|
},
|
|
321
340
|
);
|
|
322
341
|
|
|
342
|
+
// Surface the "use client" components registered as error/notFound fallbacks
|
|
343
|
+
// (route-tree errorBoundary()/notFoundBoundary() helpers, stored on EntryData).
|
|
344
|
+
// The boundary may be a handler function and/or wrap the client boundary in
|
|
345
|
+
// server providers, so walk the whole tree (see collectFallbackClientRefs).
|
|
346
|
+
if (options?.collectClientFallbackRef) {
|
|
347
|
+
const report = options.collectClientFallbackRef;
|
|
348
|
+
const collect = (boundary: unknown[] | undefined) => {
|
|
349
|
+
for (const item of boundary ?? [])
|
|
350
|
+
collectFallbackClientRefs(item, report);
|
|
351
|
+
};
|
|
352
|
+
for (const entry of manifest.values()) {
|
|
353
|
+
collect(entry.errorBoundary);
|
|
354
|
+
collect(entry.notFoundBoundary);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
323
358
|
// Collect root-level routes and trailing slash config
|
|
324
359
|
const routeTrailingSlash: Record<string, string> = {};
|
|
325
360
|
for (const [name, pattern] of patternsMap.entries()) {
|
|
@@ -347,7 +382,7 @@ export function generateManifestFull<TEnv>(
|
|
|
347
382
|
if (entry.prerenderDef) {
|
|
348
383
|
prerenderDefs[name] = entry.prerenderDef;
|
|
349
384
|
}
|
|
350
|
-
if (entry.
|
|
385
|
+
if (entry.isPassthrough === true) {
|
|
351
386
|
passthroughRoutes.push(name);
|
|
352
387
|
}
|
|
353
388
|
}
|
|
@@ -356,12 +391,10 @@ export function generateManifestFull<TEnv>(
|
|
|
356
391
|
}
|
|
357
392
|
}
|
|
358
393
|
|
|
359
|
-
//
|
|
360
|
-
// Multiple includes can share the same fullPrefix (e.g., include("/", patternsA),
|
|
361
|
-
// include("/", patternsB)). Merge their routes instead of overwriting.
|
|
394
|
+
// Shared visited set for cycle detection across all root-level includes.
|
|
362
395
|
const visited = new Set<unknown>();
|
|
363
|
-
|
|
364
|
-
|
|
396
|
+
mergeIncludeNodes(prefixTree, trackedIncludes, (include) =>
|
|
397
|
+
buildPrefixTreeNode(
|
|
365
398
|
include.fullPrefix,
|
|
366
399
|
include.namePrefix,
|
|
367
400
|
include.patterns as UrlPatterns<any>,
|
|
@@ -375,16 +408,8 @@ export function generateManifestFull<TEnv>(
|
|
|
375
408
|
passthroughRoutes,
|
|
376
409
|
responseTypeRoutes,
|
|
377
410
|
routeSearchSchemas,
|
|
378
|
-
)
|
|
379
|
-
|
|
380
|
-
const existing = prefixTree[include.fullPrefix];
|
|
381
|
-
if (existing) {
|
|
382
|
-
existing.routes.push(...node.routes);
|
|
383
|
-
Object.assign(existing.children, node.children);
|
|
384
|
-
} else {
|
|
385
|
-
prefixTree[include.fullPrefix] = node;
|
|
386
|
-
}
|
|
387
|
-
}
|
|
411
|
+
),
|
|
412
|
+
);
|
|
388
413
|
|
|
389
414
|
return {
|
|
390
415
|
prefixTree,
|
|
@@ -25,6 +25,9 @@ export {
|
|
|
25
25
|
} from "./route-types/include-resolution.js";
|
|
26
26
|
export {
|
|
27
27
|
extractUrlsVariableFromRouter,
|
|
28
|
+
extractUrlsFromRouter,
|
|
29
|
+
extractBasenameFromRouter,
|
|
30
|
+
type UrlsExtractionResult,
|
|
28
31
|
buildCombinedRouteMapForRouterFile,
|
|
29
32
|
detectUnresolvableIncludes,
|
|
30
33
|
detectUnresolvableIncludesForUrlsFile,
|
|
@@ -32,5 +35,7 @@ export {
|
|
|
32
35
|
formatNestedRouterConflictError,
|
|
33
36
|
findRouterFiles,
|
|
34
37
|
writeCombinedRouteTypes,
|
|
38
|
+
genFileTsPath,
|
|
39
|
+
resolveSearchSchemas,
|
|
35
40
|
} from "./route-types/router-processing.js";
|
|
36
41
|
export { findUrlsVariableNames } from "./route-types/per-module-writer.js";
|
package/src/build/index.ts
CHANGED
package/src/build/route-trie.ts
CHANGED
|
@@ -20,7 +20,8 @@ export interface TrieLeaf {
|
|
|
20
20
|
sp: string;
|
|
21
21
|
/** Ancestry shortCodes from root to route [M0L0, M0L0L0, M0L0L0R499] */
|
|
22
22
|
a: string[];
|
|
23
|
-
/** Optional param names
|
|
23
|
+
/** Optional param names declared on the route. Absent params are
|
|
24
|
+
* omitted from the matched params record (read as `undefined`). */
|
|
24
25
|
op?: string[];
|
|
25
26
|
/** Constraint validation: paramName -> allowed values */
|
|
26
27
|
cv?: Record<string, string[]>;
|
|
@@ -98,8 +99,14 @@ export function buildRouteTrie(
|
|
|
98
99
|
}
|
|
99
100
|
|
|
100
101
|
/**
|
|
101
|
-
* Insert a route into the trie
|
|
102
|
-
*
|
|
102
|
+
* Insert a route into the trie. Optional params expand into two branches at
|
|
103
|
+
* registration time (skip-first, then present), so each terminal lives at the
|
|
104
|
+
* correct depth for its number of bound params and carries a branch-local
|
|
105
|
+
* `pa` listing only those names. The trie's single-slot `node.p` is reused
|
|
106
|
+
* across branches because matching ignores `node.p.n` — the leaf's `pa` is
|
|
107
|
+
* the source of truth for naming. Skip-first ordering lets `mergeLeaf`'s
|
|
108
|
+
* last-wins rule produce greedy-leftmost semantics for free at any shared
|
|
109
|
+
* terminal depth.
|
|
103
110
|
*/
|
|
104
111
|
function insertRoute(
|
|
105
112
|
node: TrieNode,
|
|
@@ -107,14 +114,13 @@ function insertRoute(
|
|
|
107
114
|
index: number,
|
|
108
115
|
leaf: Omit<TrieLeaf, "op" | "cv" | "pa">,
|
|
109
116
|
): void {
|
|
110
|
-
//
|
|
111
|
-
|
|
117
|
+
// op (full optional list) and cv (full constraint map) are route-level and
|
|
118
|
+
// identical on every terminal, so compute them once on the shared base.
|
|
112
119
|
const optionalParams: string[] = [];
|
|
113
120
|
const constraints: Record<string, string[]> = {};
|
|
114
121
|
|
|
115
122
|
for (const seg of segments) {
|
|
116
123
|
if (seg.type === "param") {
|
|
117
|
-
paramNames.push(seg.value);
|
|
118
124
|
if (seg.optional) {
|
|
119
125
|
optionalParams.push(seg.value);
|
|
120
126
|
}
|
|
@@ -124,21 +130,15 @@ function insertRoute(
|
|
|
124
130
|
}
|
|
125
131
|
}
|
|
126
132
|
|
|
127
|
-
const
|
|
133
|
+
const leafBase: Omit<TrieLeaf, "pa"> = {
|
|
128
134
|
...leaf,
|
|
129
|
-
...(paramNames.length > 0 ? { pa: paramNames } : {}),
|
|
130
135
|
...(optionalParams.length > 0 ? { op: optionalParams } : {}),
|
|
131
136
|
...(Object.keys(constraints).length > 0 ? { cv: constraints } : {}),
|
|
132
137
|
};
|
|
133
138
|
|
|
134
|
-
insertSegments(node, segments, index,
|
|
139
|
+
insertSegments(node, segments, index, leafBase, []);
|
|
135
140
|
}
|
|
136
141
|
|
|
137
|
-
/**
|
|
138
|
-
* Recursively insert segments into the trie.
|
|
139
|
-
* For optional params, we add a terminal at the current node (param absent)
|
|
140
|
-
* AND continue inserting into the param child (param present).
|
|
141
|
-
*/
|
|
142
142
|
/**
|
|
143
143
|
* Extract ancestry map from a built trie by visiting all leaf nodes.
|
|
144
144
|
* Returns { routeName: ancestryShortCodes[] } for every route in the trie.
|
|
@@ -218,15 +218,25 @@ function mergeLeaf(node: TrieNode, leaf: TrieLeaf): void {
|
|
|
218
218
|
node.r = mergeLeaves(node.r, leaf);
|
|
219
219
|
}
|
|
220
220
|
|
|
221
|
+
function buildLeaf(
|
|
222
|
+
leafBase: Omit<TrieLeaf, "pa">,
|
|
223
|
+
paramNames: string[],
|
|
224
|
+
): TrieLeaf {
|
|
225
|
+
return paramNames.length > 0
|
|
226
|
+
? { ...leafBase, pa: [...paramNames] }
|
|
227
|
+
: { ...leafBase };
|
|
228
|
+
}
|
|
229
|
+
|
|
221
230
|
function insertSegments(
|
|
222
231
|
node: TrieNode,
|
|
223
232
|
segments: ParsedSegment[],
|
|
224
233
|
index: number,
|
|
225
|
-
|
|
234
|
+
leafBase: Omit<TrieLeaf, "pa">,
|
|
235
|
+
paramNames: string[],
|
|
226
236
|
): void {
|
|
227
|
-
// Base case: all segments consumed, add terminal
|
|
237
|
+
// Base case: all segments consumed, add terminal with branch-local pa
|
|
228
238
|
if (index >= segments.length) {
|
|
229
|
-
mergeLeaf(node,
|
|
239
|
+
mergeLeaf(node, buildLeaf(leafBase, paramNames));
|
|
230
240
|
return;
|
|
231
241
|
}
|
|
232
242
|
|
|
@@ -235,12 +245,19 @@ function insertSegments(
|
|
|
235
245
|
if (segment.type === "static") {
|
|
236
246
|
if (!node.s) node.s = {};
|
|
237
247
|
if (!node.s[segment.value]) node.s[segment.value] = {};
|
|
238
|
-
insertSegments(
|
|
248
|
+
insertSegments(
|
|
249
|
+
node.s[segment.value],
|
|
250
|
+
segments,
|
|
251
|
+
index + 1,
|
|
252
|
+
leafBase,
|
|
253
|
+
paramNames,
|
|
254
|
+
);
|
|
239
255
|
} else if (segment.type === "param") {
|
|
240
256
|
if (segment.optional) {
|
|
241
|
-
//
|
|
242
|
-
|
|
243
|
-
//
|
|
257
|
+
// SKIP first: continue at the same node without binding this name.
|
|
258
|
+
// Skip-first ordering means the present-branch's TAKE overwrites any
|
|
259
|
+
// shared terminal later, giving greedy-leftmost semantics.
|
|
260
|
+
insertSegments(node, segments, index + 1, leafBase, paramNames);
|
|
244
261
|
}
|
|
245
262
|
if (segment.suffix) {
|
|
246
263
|
// Suffix param: keyed by suffix string (e.g., ".html")
|
|
@@ -248,16 +265,26 @@ function insertSegments(
|
|
|
248
265
|
if (!node.xp[segment.suffix]) {
|
|
249
266
|
node.xp[segment.suffix] = { n: segment.value, c: {} };
|
|
250
267
|
}
|
|
251
|
-
insertSegments(node.xp[segment.suffix].c, segments, index + 1,
|
|
268
|
+
insertSegments(node.xp[segment.suffix].c, segments, index + 1, leafBase, [
|
|
269
|
+
...paramNames,
|
|
270
|
+
segment.value,
|
|
271
|
+
]);
|
|
252
272
|
} else {
|
|
253
273
|
if (!node.p) {
|
|
254
274
|
node.p = { n: segment.value, c: {} };
|
|
255
275
|
}
|
|
256
|
-
insertSegments(node.p.c, segments, index + 1,
|
|
276
|
+
insertSegments(node.p.c, segments, index + 1, leafBase, [
|
|
277
|
+
...paramNames,
|
|
278
|
+
segment.value,
|
|
279
|
+
]);
|
|
257
280
|
}
|
|
258
281
|
} else if (segment.type === "wildcard") {
|
|
259
|
-
// Wildcard consumes all remaining segments
|
|
260
|
-
|
|
282
|
+
// Wildcard consumes all remaining segments. Carry any params bound before
|
|
283
|
+
// the wildcard in pa so they zip correctly against paramValues at match.
|
|
284
|
+
const wildLeaf: TrieLeaf & { pn: string } = {
|
|
285
|
+
...buildLeaf(leafBase, paramNames),
|
|
286
|
+
pn: "*",
|
|
287
|
+
};
|
|
261
288
|
const existing = node.w ? ({ ...node.w } as TrieLeaf) : undefined;
|
|
262
289
|
const merged = mergeLeaves(existing, wildLeaf);
|
|
263
290
|
node.w = merged as TrieLeaf & { pn: string };
|
|
@@ -23,7 +23,7 @@ export function generatePerModuleTypesSource(
|
|
|
23
23
|
const valid = routes.filter(({ name }) => {
|
|
24
24
|
if (!name || /["'\\`\n\r]/.test(name)) {
|
|
25
25
|
console.warn(
|
|
26
|
-
`[
|
|
26
|
+
`[rango] Skipping route with invalid name: ${JSON.stringify(name)}`,
|
|
27
27
|
);
|
|
28
28
|
return false;
|
|
29
29
|
}
|
|
@@ -42,7 +42,7 @@ export function generatePerModuleTypesSource(
|
|
|
42
42
|
for (const { name, pattern, params, search } of valid) {
|
|
43
43
|
if (deduped.has(name)) {
|
|
44
44
|
console.warn(
|
|
45
|
-
`[
|
|
45
|
+
`[rango] Duplicate route name "${name}" — keeping first definition`,
|
|
46
46
|
);
|
|
47
47
|
continue;
|
|
48
48
|
}
|
|
@@ -59,7 +59,7 @@ export function generatePerModuleTypesSource(
|
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
/**
|
|
62
|
-
* Generates a .ts file that augments
|
|
62
|
+
* Generates a .ts file that augments Rango.GeneratedRouteMap
|
|
63
63
|
* with route name -> pattern mappings. This enables Handler<"routeName">
|
|
64
64
|
* without circular references since the file has no imports from the app.
|
|
65
65
|
*/
|
|
@@ -94,7 +94,7 @@ ${objectBody}
|
|
|
94
94
|
} as const;
|
|
95
95
|
|
|
96
96
|
declare global {
|
|
97
|
-
namespace
|
|
97
|
+
namespace Rango {
|
|
98
98
|
interface GeneratedRouteMap extends Readonly<typeof NamedRoutes> {}
|
|
99
99
|
}
|
|
100
100
|
}
|
|
@@ -357,12 +357,17 @@ function buildRouteMapFromBlock(
|
|
|
357
357
|
/**
|
|
358
358
|
* Build route map and search schemas together.
|
|
359
359
|
* Internal helper used by the include resolution path.
|
|
360
|
+
*
|
|
361
|
+
* @param inlineBlock - Optional pre-extracted code block (e.g. from an inline
|
|
362
|
+
* builder function). When provided, variableName is ignored and the block
|
|
363
|
+
* is parsed directly for path()/include() calls.
|
|
360
364
|
*/
|
|
361
365
|
export function buildCombinedRouteMapWithSearch(
|
|
362
366
|
filePath: string,
|
|
363
367
|
variableName?: string,
|
|
364
368
|
visited?: Set<string>,
|
|
365
369
|
diagnosticsOut?: UnresolvableInclude[],
|
|
370
|
+
inlineBlock?: string,
|
|
366
371
|
): {
|
|
367
372
|
routes: Record<string, string>;
|
|
368
373
|
searchSchemas: Record<string, Record<string, string>>;
|
|
@@ -371,7 +376,7 @@ export function buildCombinedRouteMapWithSearch(
|
|
|
371
376
|
const realPath = resolve(filePath);
|
|
372
377
|
const key = variableName ? `${realPath}:${variableName}` : realPath;
|
|
373
378
|
if (visited.has(key)) {
|
|
374
|
-
console.warn(`[
|
|
379
|
+
console.warn(`[rango] Circular include detected, skipping: ${key}`);
|
|
375
380
|
return { routes: {}, searchSchemas: {} };
|
|
376
381
|
}
|
|
377
382
|
visited.add(key);
|
|
@@ -384,7 +389,9 @@ export function buildCombinedRouteMapWithSearch(
|
|
|
384
389
|
}
|
|
385
390
|
|
|
386
391
|
let block: string;
|
|
387
|
-
if (
|
|
392
|
+
if (inlineBlock) {
|
|
393
|
+
block = inlineBlock;
|
|
394
|
+
} else if (variableName) {
|
|
388
395
|
const extracted = extractUrlsBlockForVariable(source, variableName);
|
|
389
396
|
if (!extracted) return { routes: {}, searchSchemas: {} };
|
|
390
397
|
block = extracted;
|
|
@@ -97,7 +97,10 @@ export function writePerModuleRouteTypesForFile(filePath: string): void {
|
|
|
97
97
|
routes = extractRoutesFromSource(source);
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
-
|
|
100
|
+
// Match .ts/.tsx/.js/.jsx (same as router-processing.ts / router-transform.ts).
|
|
101
|
+
// Without the jsx? branch a .jsx/.js source produced genPath === filePath,
|
|
102
|
+
// overwriting the source file instead of writing a sibling .gen.ts.
|
|
103
|
+
const genPath = filePath.replace(/\.(tsx?|jsx?)$/, ".gen.ts");
|
|
101
104
|
|
|
102
105
|
// When a urls() variable was found but static resolution yields zero
|
|
103
106
|
// routes, write an empty placeholder so generated imports stay
|
|
@@ -106,7 +109,7 @@ export function writePerModuleRouteTypesForFile(filePath: string): void {
|
|
|
106
109
|
if (varNames.length > 0 && !existsSync(genPath)) {
|
|
107
110
|
writeFileSync(genPath, generatePerModuleTypesSource([]));
|
|
108
111
|
console.log(
|
|
109
|
-
`[
|
|
112
|
+
`[rango] Generated route types (placeholder) -> ${genPath}`,
|
|
110
113
|
);
|
|
111
114
|
}
|
|
112
115
|
return;
|
|
@@ -118,11 +121,11 @@ export function writePerModuleRouteTypesForFile(filePath: string): void {
|
|
|
118
121
|
: null;
|
|
119
122
|
if (existing !== genSource) {
|
|
120
123
|
writeFileSync(genPath, genSource);
|
|
121
|
-
console.log(`[
|
|
124
|
+
console.log(`[rango] Generated route types -> ${genPath}`);
|
|
122
125
|
}
|
|
123
126
|
} catch (err) {
|
|
124
127
|
console.warn(
|
|
125
|
-
`[
|
|
128
|
+
`[rango] Failed to generate route types for ${filePath}: ${(err as Error).message}`,
|
|
126
129
|
);
|
|
127
130
|
}
|
|
128
131
|
}
|