@rangojs/router 0.0.0-experimental.0f44aca1
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 +899 -0
- package/dist/bin/rango.js +1601 -0
- package/dist/vite/index.js +5214 -0
- package/package.json +176 -0
- package/skills/breadcrumbs/SKILL.md +250 -0
- package/skills/cache-guide/SKILL.md +262 -0
- package/skills/caching/SKILL.md +220 -0
- package/skills/composability/SKILL.md +172 -0
- package/skills/debug-manifest/SKILL.md +112 -0
- package/skills/document-cache/SKILL.md +182 -0
- package/skills/fonts/SKILL.md +167 -0
- package/skills/hooks/SKILL.md +704 -0
- package/skills/host-router/SKILL.md +218 -0
- package/skills/intercept/SKILL.md +313 -0
- package/skills/layout/SKILL.md +310 -0
- package/skills/links/SKILL.md +239 -0
- package/skills/loader/SKILL.md +596 -0
- package/skills/middleware/SKILL.md +339 -0
- package/skills/mime-routes/SKILL.md +128 -0
- package/skills/parallel/SKILL.md +305 -0
- package/skills/prerender/SKILL.md +643 -0
- package/skills/rango/SKILL.md +118 -0
- package/skills/response-routes/SKILL.md +411 -0
- package/skills/route/SKILL.md +385 -0
- package/skills/router-setup/SKILL.md +439 -0
- package/skills/tailwind/SKILL.md +129 -0
- package/skills/theme/SKILL.md +79 -0
- package/skills/typesafety/SKILL.md +623 -0
- package/skills/use-cache/SKILL.md +324 -0
- package/src/__internal.ts +273 -0
- package/src/bin/rango.ts +321 -0
- package/src/browser/action-coordinator.ts +97 -0
- package/src/browser/action-response-classifier.ts +99 -0
- package/src/browser/event-controller.ts +899 -0
- package/src/browser/history-state.ts +80 -0
- package/src/browser/index.ts +18 -0
- package/src/browser/intercept-utils.ts +52 -0
- package/src/browser/link-interceptor.ts +141 -0
- package/src/browser/logging.ts +55 -0
- package/src/browser/merge-segment-loaders.ts +134 -0
- package/src/browser/navigation-bridge.ts +645 -0
- package/src/browser/navigation-client.ts +215 -0
- package/src/browser/navigation-store.ts +806 -0
- package/src/browser/navigation-transaction.ts +295 -0
- package/src/browser/network-error-handler.ts +61 -0
- package/src/browser/partial-update.ts +550 -0
- package/src/browser/prefetch/cache.ts +146 -0
- package/src/browser/prefetch/fetch.ts +135 -0
- package/src/browser/prefetch/observer.ts +65 -0
- package/src/browser/prefetch/policy.ts +42 -0
- package/src/browser/prefetch/queue.ts +88 -0
- package/src/browser/rango-state.ts +112 -0
- package/src/browser/react/Link.tsx +360 -0
- package/src/browser/react/NavigationProvider.tsx +386 -0
- package/src/browser/react/ScrollRestoration.tsx +94 -0
- package/src/browser/react/context.ts +59 -0
- package/src/browser/react/filter-segment-order.ts +11 -0
- package/src/browser/react/index.ts +52 -0
- package/src/browser/react/location-state-shared.ts +162 -0
- package/src/browser/react/location-state.ts +107 -0
- package/src/browser/react/mount-context.ts +37 -0
- 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 +218 -0
- package/src/browser/react/use-client-cache.ts +58 -0
- package/src/browser/react/use-handle.ts +162 -0
- package/src/browser/react/use-href.tsx +40 -0
- package/src/browser/react/use-link-status.ts +135 -0
- package/src/browser/react/use-mount.ts +31 -0
- package/src/browser/react/use-navigation.ts +99 -0
- 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 +171 -0
- package/src/browser/response-adapter.ts +73 -0
- package/src/browser/rsc-router.tsx +431 -0
- package/src/browser/scroll-restoration.ts +400 -0
- package/src/browser/segment-reconciler.ts +216 -0
- package/src/browser/segment-structure-assert.ts +83 -0
- package/src/browser/server-action-bridge.ts +667 -0
- package/src/browser/shallow.ts +40 -0
- package/src/browser/types.ts +538 -0
- package/src/browser/validate-redirect-origin.ts +29 -0
- package/src/build/generate-manifest.ts +438 -0
- package/src/build/generate-route-types.ts +36 -0
- package/src/build/index.ts +35 -0
- package/src/build/route-trie.ts +265 -0
- 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 +382 -0
- package/src/cache/cf/cf-cache-store.ts +540 -0
- package/src/cache/cf/index.ts +25 -0
- package/src/cache/document-cache.ts +369 -0
- package/src/cache/handle-capture.ts +81 -0
- package/src/cache/handle-snapshot.ts +41 -0
- package/src/cache/index.ts +43 -0
- package/src/cache/memory-segment-store.ts +328 -0
- 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 +342 -0
- package/src/client.rsc.tsx +85 -0
- package/src/client.tsx +601 -0
- package/src/component-utils.ts +76 -0
- package/src/components/DefaultDocument.tsx +27 -0
- package/src/context-var.ts +86 -0
- package/src/debug.ts +243 -0
- package/src/default-error-boundary.tsx +88 -0
- package/src/deps/browser.ts +8 -0
- package/src/deps/html-stream-client.ts +2 -0
- package/src/deps/html-stream-server.ts +2 -0
- package/src/deps/rsc.ts +10 -0
- package/src/deps/ssr.ts +2 -0
- package/src/errors.ts +365 -0
- package/src/handle.ts +135 -0
- package/src/handles/MetaTags.tsx +246 -0
- package/src/handles/breadcrumbs.ts +66 -0
- package/src/handles/index.ts +7 -0
- package/src/handles/meta.ts +264 -0
- package/src/host/cookie-handler.ts +165 -0
- package/src/host/errors.ts +97 -0
- package/src/host/index.ts +53 -0
- package/src/host/pattern-matcher.ts +214 -0
- package/src/host/router.ts +352 -0
- package/src/host/testing.ts +79 -0
- package/src/host/types.ts +146 -0
- package/src/host/utils.ts +25 -0
- package/src/href-client.ts +222 -0
- package/src/index.rsc.ts +233 -0
- package/src/index.ts +277 -0
- package/src/internal-debug.ts +11 -0
- package/src/loader.rsc.ts +89 -0
- package/src/loader.ts +64 -0
- package/src/network-error-thrower.tsx +23 -0
- package/src/outlet-context.ts +15 -0
- package/src/outlet-provider.tsx +45 -0
- package/src/prerender/param-hash.ts +37 -0
- package/src/prerender/store.ts +185 -0
- package/src/prerender.ts +463 -0
- package/src/reverse.ts +330 -0
- package/src/root-error-boundary.tsx +289 -0
- package/src/route-content-wrapper.tsx +196 -0
- 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 -0
- package/src/route-map-builder.ts +275 -0
- package/src/route-name.ts +53 -0
- package/src/route-types.ts +259 -0
- package/src/router/content-negotiation.ts +116 -0
- package/src/router/debug-manifest.ts +72 -0
- package/src/router/error-handling.ts +287 -0
- package/src/router/find-match.ts +158 -0
- package/src/router/handler-context.ts +451 -0
- package/src/router/intercept-resolution.ts +395 -0
- package/src/router/lazy-includes.ts +234 -0
- package/src/router/loader-resolution.ts +420 -0
- package/src/router/logging.ts +248 -0
- package/src/router/manifest.ts +267 -0
- package/src/router/match-api.ts +620 -0
- package/src/router/match-context.ts +266 -0
- package/src/router/match-handlers.ts +440 -0
- package/src/router/match-middleware/background-revalidation.ts +223 -0
- package/src/router/match-middleware/cache-lookup.ts +634 -0
- package/src/router/match-middleware/cache-store.ts +295 -0
- package/src/router/match-middleware/index.ts +81 -0
- package/src/router/match-middleware/intercept-resolution.ts +306 -0
- package/src/router/match-middleware/segment-resolution.ts +192 -0
- package/src/router/match-pipelines.ts +179 -0
- package/src/router/match-result.ts +219 -0
- package/src/router/metrics.ts +282 -0
- package/src/router/middleware-cookies.ts +55 -0
- package/src/router/middleware-types.ts +222 -0
- package/src/router/middleware.ts +748 -0
- package/src/router/pattern-matching.ts +563 -0
- package/src/router/prerender-match.ts +402 -0
- package/src/router/preview-match.ts +170 -0
- package/src/router/revalidation.ts +289 -0
- package/src/router/router-context.ts +316 -0
- 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 -0
- 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 +239 -0
- package/src/router/types.ts +170 -0
- package/src/router.ts +1002 -0
- package/src/rsc/handler-context.ts +45 -0
- package/src/rsc/handler.ts +1089 -0
- package/src/rsc/helpers.ts +198 -0
- package/src/rsc/index.ts +36 -0
- package/src/rsc/loader-fetch.ts +209 -0
- package/src/rsc/manifest-init.ts +86 -0
- package/src/rsc/nonce.ts +32 -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 +235 -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 +263 -0
- package/src/search-params.ts +230 -0
- package/src/segment-system.tsx +454 -0
- package/src/server/context.ts +591 -0
- package/src/server/cookie-store.ts +190 -0
- package/src/server/fetchable-loader-store.ts +37 -0
- package/src/server/handle-store.ts +308 -0
- package/src/server/loader-registry.ts +133 -0
- package/src/server/request-context.ts +914 -0
- package/src/server/root-layout.tsx +10 -0
- package/src/server/tsconfig.json +14 -0
- package/src/server.ts +51 -0
- package/src/ssr/index.tsx +365 -0
- package/src/static-handler.ts +114 -0
- package/src/theme/ThemeProvider.tsx +297 -0
- package/src/theme/ThemeScript.tsx +61 -0
- package/src/theme/constants.ts +62 -0
- package/src/theme/index.ts +48 -0
- package/src/theme/theme-context.ts +44 -0
- package/src/theme/theme-script.ts +155 -0
- package/src/theme/types.ts +182 -0
- package/src/theme/use-theme.ts +44 -0
- 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 -0
- 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 -0
- package/src/use-loader.tsx +354 -0
- 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 +16 -0
- 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/plugins/expose-action-id.ts +365 -0
- 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/plugins/version.d.ts +12 -0
- package/src/vite/plugins/virtual-entries.ts +123 -0
- 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/utils/package-resolution.ts +121 -0
- package/src/vite/utils/prerender-utils.ts +189 -0
- package/src/vite/utils/shared-utils.ts +169 -0
|
@@ -0,0 +1,570 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fresh Path Segment Resolution
|
|
3
|
+
*
|
|
4
|
+
* Functions for resolving segments during a full (non-revalidation) request.
|
|
5
|
+
* Handles loaders, layouts, routes, parallels, orphan layouts, and error boundaries.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { ReactNode } from "react";
|
|
9
|
+
import { invariant } from "../../errors";
|
|
10
|
+
import type { EntryData } from "../../server/context";
|
|
11
|
+
import type {
|
|
12
|
+
HandlerContext,
|
|
13
|
+
InternalHandlerContext,
|
|
14
|
+
ResolvedSegment,
|
|
15
|
+
} from "../../types";
|
|
16
|
+
import type { SegmentResolutionDeps } from "../types.js";
|
|
17
|
+
import { resolveLoaderData } from "./loader-cache.js";
|
|
18
|
+
import {
|
|
19
|
+
handleHandlerResult,
|
|
20
|
+
tryStaticHandler,
|
|
21
|
+
tryStaticSlot,
|
|
22
|
+
resolveLayoutComponent,
|
|
23
|
+
resolveWithErrorBoundary,
|
|
24
|
+
} from "./helpers.js";
|
|
25
|
+
import { getRouterContext } from "../router-context.js";
|
|
26
|
+
import { resolveSink, safeEmit } from "../telemetry.js";
|
|
27
|
+
import { track } from "../../server/context.js";
|
|
28
|
+
|
|
29
|
+
// ---------------------------------------------------------------------------
|
|
30
|
+
// Streamed handler telemetry
|
|
31
|
+
// ---------------------------------------------------------------------------
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Attach a fire-and-forget rejection observer to a streamed handler promise.
|
|
35
|
+
* React catches the actual error via its error boundary; this only emits
|
|
36
|
+
* the handler.error telemetry event.
|
|
37
|
+
*/
|
|
38
|
+
function observeStreamedHandler(
|
|
39
|
+
promise: Promise<ReactNode>,
|
|
40
|
+
segmentId: string,
|
|
41
|
+
segmentType: string,
|
|
42
|
+
pathname?: string,
|
|
43
|
+
routeKey?: string,
|
|
44
|
+
params?: Record<string, string>,
|
|
45
|
+
): void {
|
|
46
|
+
let routerCtx;
|
|
47
|
+
try {
|
|
48
|
+
routerCtx = getRouterContext();
|
|
49
|
+
} catch {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
if (!routerCtx?.telemetry) return;
|
|
53
|
+
const sink = resolveSink(routerCtx.telemetry);
|
|
54
|
+
const reqId = routerCtx.requestId;
|
|
55
|
+
promise.catch((err: unknown) => {
|
|
56
|
+
const errorObj = err instanceof Error ? err : new Error(String(err));
|
|
57
|
+
safeEmit(sink, {
|
|
58
|
+
type: "handler.error",
|
|
59
|
+
timestamp: performance.now(),
|
|
60
|
+
requestId: reqId,
|
|
61
|
+
segmentId,
|
|
62
|
+
segmentType,
|
|
63
|
+
error: errorObj,
|
|
64
|
+
handledByBoundary: true,
|
|
65
|
+
pathname,
|
|
66
|
+
routeKey,
|
|
67
|
+
params,
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// ---------------------------------------------------------------------------
|
|
73
|
+
// Fresh path (full match, no revalidation)
|
|
74
|
+
// ---------------------------------------------------------------------------
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Resolve loaders for an entry and emit segments.
|
|
78
|
+
* Loaders are run lazily via ctx.use() and memoized for parallel execution.
|
|
79
|
+
*/
|
|
80
|
+
export async function resolveLoaders<TEnv>(
|
|
81
|
+
entry: EntryData,
|
|
82
|
+
ctx: HandlerContext<any, TEnv>,
|
|
83
|
+
belongsToRoute: boolean,
|
|
84
|
+
deps: SegmentResolutionDeps<TEnv>,
|
|
85
|
+
shortCodeOverride?: string,
|
|
86
|
+
): Promise<ResolvedSegment[]> {
|
|
87
|
+
const loaderEntries = entry.loader ?? [];
|
|
88
|
+
if (loaderEntries.length === 0) return [];
|
|
89
|
+
|
|
90
|
+
const shortCode = shortCodeOverride ?? entry.shortCode;
|
|
91
|
+
const hasLoading = "loading" in entry && entry.loading !== undefined;
|
|
92
|
+
const loadingDisabled = hasLoading && entry.loading === false;
|
|
93
|
+
|
|
94
|
+
if (!loadingDisabled) {
|
|
95
|
+
return loaderEntries.map((loaderEntry, i) => {
|
|
96
|
+
const { loader } = loaderEntry;
|
|
97
|
+
const segmentId = `${shortCode}D${i}.${loader.$$id}`;
|
|
98
|
+
return {
|
|
99
|
+
id: segmentId,
|
|
100
|
+
namespace: entry.id,
|
|
101
|
+
type: "loader" as const,
|
|
102
|
+
index: i,
|
|
103
|
+
component: null,
|
|
104
|
+
params: ctx.params,
|
|
105
|
+
loaderId: loader.$$id,
|
|
106
|
+
loaderData: deps.wrapLoaderPromise(
|
|
107
|
+
resolveLoaderData(loaderEntry, ctx, ctx.pathname),
|
|
108
|
+
entry,
|
|
109
|
+
segmentId,
|
|
110
|
+
ctx.pathname,
|
|
111
|
+
),
|
|
112
|
+
belongsToRoute,
|
|
113
|
+
};
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Loading disabled: still start all loaders in parallel, but only emit
|
|
118
|
+
// settled promises so handlers don't stream loading placeholders.
|
|
119
|
+
const pendingLoaderData = loaderEntries.map((loaderEntry) =>
|
|
120
|
+
resolveLoaderData(loaderEntry, ctx, ctx.pathname),
|
|
121
|
+
);
|
|
122
|
+
await Promise.all(pendingLoaderData);
|
|
123
|
+
|
|
124
|
+
return loaderEntries.map((loaderEntry, i) => {
|
|
125
|
+
const { loader } = loaderEntry;
|
|
126
|
+
const segmentId = `${shortCode}D${i}.${loader.$$id}`;
|
|
127
|
+
return {
|
|
128
|
+
id: segmentId,
|
|
129
|
+
namespace: entry.id,
|
|
130
|
+
type: "loader" as const,
|
|
131
|
+
index: i,
|
|
132
|
+
component: null,
|
|
133
|
+
params: ctx.params,
|
|
134
|
+
loaderId: loader.$$id,
|
|
135
|
+
loaderData: deps.wrapLoaderPromise(
|
|
136
|
+
pendingLoaderData[i]!,
|
|
137
|
+
entry,
|
|
138
|
+
segmentId,
|
|
139
|
+
ctx.pathname,
|
|
140
|
+
),
|
|
141
|
+
belongsToRoute,
|
|
142
|
+
};
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Options for segment resolution.
|
|
148
|
+
*/
|
|
149
|
+
export interface ResolveSegmentOptions {
|
|
150
|
+
/** When true, skip resolveLoaders() calls (used for pre-rendering) */
|
|
151
|
+
skipLoaders?: boolean;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Resolve segments from EntryData.
|
|
156
|
+
* Executes middlewares, loaders, parallels, and handlers in correct order.
|
|
157
|
+
* Returns array: [main segment, ...orphan layout segments]
|
|
158
|
+
*/
|
|
159
|
+
export async function resolveSegment<TEnv>(
|
|
160
|
+
entry: EntryData,
|
|
161
|
+
routeKey: string,
|
|
162
|
+
params: Record<string, string>,
|
|
163
|
+
context: HandlerContext<any, TEnv>,
|
|
164
|
+
loaderPromises: Map<string, Promise<any>>,
|
|
165
|
+
deps: SegmentResolutionDeps<TEnv>,
|
|
166
|
+
isRouteEntry: boolean = false,
|
|
167
|
+
options?: ResolveSegmentOptions,
|
|
168
|
+
): Promise<ResolvedSegment[]> {
|
|
169
|
+
const segments: ResolvedSegment[] = [];
|
|
170
|
+
|
|
171
|
+
if (entry.type === "layout" || entry.type === "cache") {
|
|
172
|
+
if (!options?.skipLoaders) {
|
|
173
|
+
const loaderSegments = await resolveLoaders(entry, context, false, deps);
|
|
174
|
+
segments.push(...loaderSegments);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Handler-first: layout handler executes before its parallels and orphan
|
|
178
|
+
// layouts so that ctx.set() values are visible to all children.
|
|
179
|
+
(context as InternalHandlerContext<any, TEnv>)._currentSegmentId =
|
|
180
|
+
entry.shortCode;
|
|
181
|
+
|
|
182
|
+
const doneLayoutHandler = track(`handler:${entry.id}`, 2);
|
|
183
|
+
const component = await resolveLayoutComponent(entry, context);
|
|
184
|
+
doneLayoutHandler();
|
|
185
|
+
|
|
186
|
+
segments.push({
|
|
187
|
+
id: entry.shortCode,
|
|
188
|
+
namespace: entry.id,
|
|
189
|
+
type: "layout",
|
|
190
|
+
index: 0,
|
|
191
|
+
component,
|
|
192
|
+
loading: entry.loading === false ? null : entry.loading,
|
|
193
|
+
transition: entry.transition,
|
|
194
|
+
params,
|
|
195
|
+
belongsToRoute: false,
|
|
196
|
+
layoutName: entry.id,
|
|
197
|
+
...(entry.mountPath ? { mountPath: entry.mountPath } : {}),
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
for (const parallelEntry of entry.parallel) {
|
|
201
|
+
const parallelSegments = await resolveParallelEntry(
|
|
202
|
+
parallelEntry,
|
|
203
|
+
params,
|
|
204
|
+
context,
|
|
205
|
+
false,
|
|
206
|
+
entry.shortCode,
|
|
207
|
+
deps,
|
|
208
|
+
options,
|
|
209
|
+
routeKey,
|
|
210
|
+
);
|
|
211
|
+
segments.push(...parallelSegments);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
for (const orphan of entry.layout) {
|
|
215
|
+
const orphanSegments = await resolveOrphanLayout(
|
|
216
|
+
orphan,
|
|
217
|
+
params,
|
|
218
|
+
context,
|
|
219
|
+
loaderPromises,
|
|
220
|
+
false,
|
|
221
|
+
deps,
|
|
222
|
+
options,
|
|
223
|
+
routeKey,
|
|
224
|
+
);
|
|
225
|
+
segments.push(...orphanSegments);
|
|
226
|
+
}
|
|
227
|
+
} else if (entry.type === "route") {
|
|
228
|
+
if (!options?.skipLoaders) {
|
|
229
|
+
const loaderSegments = await resolveLoaders(entry, context, true, deps);
|
|
230
|
+
segments.push(...loaderSegments);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Route handler EXECUTES before its children (orphan layouts, parallels).
|
|
234
|
+
// This lets the handler set() context variables that children can read
|
|
235
|
+
// via get(). Caching wraps all segments together (per-route, not
|
|
236
|
+
// per-segment), so either all run or none do -- no partial scenarios.
|
|
237
|
+
//
|
|
238
|
+
// The handler's segment is PUSHED after orphans/parallels to preserve
|
|
239
|
+
// the correct tree composition order (layouts wrap the route content).
|
|
240
|
+
(context as InternalHandlerContext<any, TEnv>)._currentSegmentId =
|
|
241
|
+
entry.shortCode;
|
|
242
|
+
let component: ReactNode | undefined = await tryStaticHandler(
|
|
243
|
+
entry,
|
|
244
|
+
entry.shortCode,
|
|
245
|
+
);
|
|
246
|
+
if (component === undefined) {
|
|
247
|
+
const doneRouteHandler = track(`handler:${entry.id}`, 2);
|
|
248
|
+
if (entry.loading) {
|
|
249
|
+
const result = handleHandlerResult(entry.handler(context));
|
|
250
|
+
if (result instanceof Promise) {
|
|
251
|
+
result.finally(doneRouteHandler).catch(() => {});
|
|
252
|
+
const tracked = deps.trackHandler(result, {
|
|
253
|
+
segmentId: entry.shortCode,
|
|
254
|
+
segmentType: entry.type,
|
|
255
|
+
});
|
|
256
|
+
observeStreamedHandler(
|
|
257
|
+
tracked,
|
|
258
|
+
entry.shortCode,
|
|
259
|
+
entry.type,
|
|
260
|
+
context.pathname,
|
|
261
|
+
routeKey,
|
|
262
|
+
params,
|
|
263
|
+
);
|
|
264
|
+
component = tracked;
|
|
265
|
+
} else {
|
|
266
|
+
doneRouteHandler();
|
|
267
|
+
component = result;
|
|
268
|
+
}
|
|
269
|
+
} else {
|
|
270
|
+
component = handleHandlerResult(await entry.handler(context));
|
|
271
|
+
doneRouteHandler();
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
for (const orphan of entry.layout) {
|
|
276
|
+
const orphanSegments = await resolveOrphanLayout(
|
|
277
|
+
orphan,
|
|
278
|
+
params,
|
|
279
|
+
context,
|
|
280
|
+
loaderPromises,
|
|
281
|
+
true,
|
|
282
|
+
deps,
|
|
283
|
+
options,
|
|
284
|
+
routeKey,
|
|
285
|
+
);
|
|
286
|
+
segments.push(...orphanSegments);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
for (const parallelEntry of entry.parallel) {
|
|
290
|
+
const parallelSegments = await resolveParallelEntry(
|
|
291
|
+
parallelEntry,
|
|
292
|
+
params,
|
|
293
|
+
context,
|
|
294
|
+
true,
|
|
295
|
+
entry.shortCode,
|
|
296
|
+
deps,
|
|
297
|
+
options,
|
|
298
|
+
routeKey,
|
|
299
|
+
);
|
|
300
|
+
segments.push(...parallelSegments);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
segments.push({
|
|
304
|
+
id: entry.shortCode,
|
|
305
|
+
namespace: entry.id,
|
|
306
|
+
type: "route",
|
|
307
|
+
index: 0,
|
|
308
|
+
component,
|
|
309
|
+
loading: entry.loading === false ? null : entry.loading,
|
|
310
|
+
transition: entry.transition,
|
|
311
|
+
params,
|
|
312
|
+
belongsToRoute: true,
|
|
313
|
+
...(entry.mountPath ? { mountPath: entry.mountPath } : {}),
|
|
314
|
+
});
|
|
315
|
+
} else {
|
|
316
|
+
throw new Error(`Unknown entry type: ${(entry as any).type}`);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
return segments;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Resolve orphan layout with its middlewares, loaders, and parallels.
|
|
324
|
+
*/
|
|
325
|
+
export async function resolveOrphanLayout<TEnv>(
|
|
326
|
+
orphan: EntryData,
|
|
327
|
+
params: Record<string, string>,
|
|
328
|
+
context: HandlerContext<any, TEnv>,
|
|
329
|
+
loaderPromises: Map<string, Promise<any>>,
|
|
330
|
+
belongsToRoute: boolean,
|
|
331
|
+
deps: SegmentResolutionDeps<TEnv>,
|
|
332
|
+
options?: ResolveSegmentOptions,
|
|
333
|
+
routeKey?: string,
|
|
334
|
+
): Promise<ResolvedSegment[]> {
|
|
335
|
+
invariant(
|
|
336
|
+
orphan.type === "layout" || orphan.type === "cache",
|
|
337
|
+
`Expected orphan to be a layout or cache, got: ${orphan.type}`,
|
|
338
|
+
);
|
|
339
|
+
|
|
340
|
+
const segments: ResolvedSegment[] = [];
|
|
341
|
+
if (!options?.skipLoaders) {
|
|
342
|
+
const loaderSegments = await resolveLoaders(
|
|
343
|
+
orphan,
|
|
344
|
+
context,
|
|
345
|
+
belongsToRoute,
|
|
346
|
+
deps,
|
|
347
|
+
);
|
|
348
|
+
segments.push(...loaderSegments);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// Handler-first: orphan layout handler executes before its parallels
|
|
352
|
+
// so that ctx.set() values are visible to parallel children.
|
|
353
|
+
const doneOrphanHandler = track(`handler:${orphan.id}`, 2);
|
|
354
|
+
const component = await resolveLayoutComponent(orphan, context);
|
|
355
|
+
doneOrphanHandler();
|
|
356
|
+
|
|
357
|
+
segments.push({
|
|
358
|
+
id: orphan.shortCode,
|
|
359
|
+
namespace: orphan.id,
|
|
360
|
+
type: "layout",
|
|
361
|
+
index: 0,
|
|
362
|
+
component,
|
|
363
|
+
params,
|
|
364
|
+
belongsToRoute,
|
|
365
|
+
layoutName: orphan.id,
|
|
366
|
+
loading: orphan.loading === false ? null : orphan.loading,
|
|
367
|
+
transition: orphan.transition,
|
|
368
|
+
...(orphan.mountPath ? { mountPath: orphan.mountPath } : {}),
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
for (const parallelEntry of orphan.parallel) {
|
|
372
|
+
const parallelSegments = await resolveParallelEntry(
|
|
373
|
+
parallelEntry,
|
|
374
|
+
params,
|
|
375
|
+
context,
|
|
376
|
+
belongsToRoute,
|
|
377
|
+
orphan.shortCode,
|
|
378
|
+
deps,
|
|
379
|
+
options,
|
|
380
|
+
routeKey,
|
|
381
|
+
);
|
|
382
|
+
segments.push(...parallelSegments);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
return segments;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* Resolve parallel EntryData with its loaders and slot handlers.
|
|
390
|
+
*/
|
|
391
|
+
export async function resolveParallelEntry<TEnv>(
|
|
392
|
+
parallelEntry: EntryData,
|
|
393
|
+
params: Record<string, string>,
|
|
394
|
+
context: HandlerContext<any, TEnv>,
|
|
395
|
+
belongsToRoute: boolean,
|
|
396
|
+
parentShortCode: string,
|
|
397
|
+
deps: SegmentResolutionDeps<TEnv>,
|
|
398
|
+
options?: ResolveSegmentOptions,
|
|
399
|
+
routeKey?: string,
|
|
400
|
+
): Promise<ResolvedSegment[]> {
|
|
401
|
+
invariant(
|
|
402
|
+
parallelEntry.type === "parallel",
|
|
403
|
+
`Expected parallel entry, got: ${parallelEntry.type}`,
|
|
404
|
+
);
|
|
405
|
+
|
|
406
|
+
const segments: ResolvedSegment[] = [];
|
|
407
|
+
|
|
408
|
+
const slots = parallelEntry.handler as Record<
|
|
409
|
+
`@${string}`,
|
|
410
|
+
| ((ctx: HandlerContext<any, TEnv>) => ReactNode | Promise<ReactNode>)
|
|
411
|
+
| ReactNode
|
|
412
|
+
>;
|
|
413
|
+
|
|
414
|
+
for (const [slot, handler] of Object.entries(slots)) {
|
|
415
|
+
let component: ReactNode | undefined = await tryStaticSlot(
|
|
416
|
+
parallelEntry,
|
|
417
|
+
slot,
|
|
418
|
+
`${parentShortCode}.${slot}`,
|
|
419
|
+
);
|
|
420
|
+
|
|
421
|
+
if (component === undefined) {
|
|
422
|
+
const doneParallelHandler = track(
|
|
423
|
+
`handler:${parallelEntry.id}.${slot}`,
|
|
424
|
+
2,
|
|
425
|
+
);
|
|
426
|
+
const hasLoadingFallback =
|
|
427
|
+
parallelEntry.loading !== undefined && parallelEntry.loading !== false;
|
|
428
|
+
if (hasLoadingFallback) {
|
|
429
|
+
const result =
|
|
430
|
+
typeof handler === "function" ? handler(context) : handler;
|
|
431
|
+
if (result instanceof Promise) {
|
|
432
|
+
result.finally(doneParallelHandler).catch(() => {});
|
|
433
|
+
const tracked = deps.trackHandler(result, {
|
|
434
|
+
segmentId: `${parentShortCode}.${slot}`,
|
|
435
|
+
segmentType: "parallel",
|
|
436
|
+
});
|
|
437
|
+
observeStreamedHandler(
|
|
438
|
+
tracked,
|
|
439
|
+
`${parentShortCode}.${slot}`,
|
|
440
|
+
"parallel",
|
|
441
|
+
context.pathname,
|
|
442
|
+
routeKey,
|
|
443
|
+
params,
|
|
444
|
+
);
|
|
445
|
+
component = tracked as ReactNode;
|
|
446
|
+
} else {
|
|
447
|
+
doneParallelHandler();
|
|
448
|
+
component = result as ReactNode;
|
|
449
|
+
}
|
|
450
|
+
} else {
|
|
451
|
+
component =
|
|
452
|
+
typeof handler === "function" ? await handler(context) : handler;
|
|
453
|
+
doneParallelHandler();
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
segments.push({
|
|
458
|
+
id: `${parentShortCode}.${slot}`,
|
|
459
|
+
namespace: parallelEntry.id,
|
|
460
|
+
type: "parallel",
|
|
461
|
+
index: 0,
|
|
462
|
+
component,
|
|
463
|
+
loading: parallelEntry.loading === false ? null : parallelEntry.loading,
|
|
464
|
+
transition: parallelEntry.transition,
|
|
465
|
+
params,
|
|
466
|
+
slot,
|
|
467
|
+
belongsToRoute,
|
|
468
|
+
parallelName: `${parallelEntry.id}.${slot}`,
|
|
469
|
+
...(parallelEntry.mountPath
|
|
470
|
+
? { mountPath: parallelEntry.mountPath }
|
|
471
|
+
: {}),
|
|
472
|
+
});
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
if (!parallelEntry.loading && !options?.skipLoaders) {
|
|
476
|
+
const loaderSegments = await resolveLoaders(
|
|
477
|
+
parallelEntry,
|
|
478
|
+
context,
|
|
479
|
+
belongsToRoute,
|
|
480
|
+
deps,
|
|
481
|
+
parentShortCode,
|
|
482
|
+
);
|
|
483
|
+
segments.push(...loaderSegments);
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
return segments;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
/**
|
|
490
|
+
* Resolve all segments for a route (used for single-cache-per-request pattern).
|
|
491
|
+
*/
|
|
492
|
+
export async function resolveAllSegments<TEnv>(
|
|
493
|
+
entries: EntryData[],
|
|
494
|
+
routeKey: string,
|
|
495
|
+
params: Record<string, string>,
|
|
496
|
+
context: HandlerContext<any, TEnv>,
|
|
497
|
+
loaderPromises: Map<string, Promise<any>>,
|
|
498
|
+
deps: SegmentResolutionDeps<TEnv>,
|
|
499
|
+
options?: ResolveSegmentOptions,
|
|
500
|
+
): Promise<ResolvedSegment[]> {
|
|
501
|
+
const allSegments: ResolvedSegment[] = [];
|
|
502
|
+
const seenIds = new Set<string>();
|
|
503
|
+
|
|
504
|
+
// Safe request access: during build-time prerendering, context.request
|
|
505
|
+
// is a throwing getter. Use undefined when unavailable.
|
|
506
|
+
let safeRequest: Request | undefined;
|
|
507
|
+
try {
|
|
508
|
+
safeRequest = context.request;
|
|
509
|
+
} catch {}
|
|
510
|
+
|
|
511
|
+
// Get telemetry sink from RouterContext (may not exist during prerendering)
|
|
512
|
+
let telemetry;
|
|
513
|
+
try {
|
|
514
|
+
telemetry = getRouterContext()?.telemetry;
|
|
515
|
+
} catch {}
|
|
516
|
+
|
|
517
|
+
for (const entry of entries) {
|
|
518
|
+
const doneEntry = track(`segment:${entry.id}`, 1);
|
|
519
|
+
const resolvedSegments = await resolveWithErrorBoundary(
|
|
520
|
+
entry,
|
|
521
|
+
params,
|
|
522
|
+
() =>
|
|
523
|
+
resolveSegment(
|
|
524
|
+
entry,
|
|
525
|
+
routeKey,
|
|
526
|
+
params,
|
|
527
|
+
context,
|
|
528
|
+
loaderPromises,
|
|
529
|
+
deps,
|
|
530
|
+
false,
|
|
531
|
+
options,
|
|
532
|
+
),
|
|
533
|
+
(seg) => [seg],
|
|
534
|
+
deps,
|
|
535
|
+
{ request: safeRequest, url: context.url, routeKey, telemetry },
|
|
536
|
+
context.pathname,
|
|
537
|
+
);
|
|
538
|
+
doneEntry();
|
|
539
|
+
// Deduplicate by segment ID. include() scopes can produce entries that
|
|
540
|
+
// resolve the same shared layout/loader segment. Duplicates in the segment
|
|
541
|
+
// array propagate to the client's matched[] and change the React tree depth.
|
|
542
|
+
for (const seg of resolvedSegments) {
|
|
543
|
+
if (!seenIds.has(seg.id)) {
|
|
544
|
+
seenIds.add(seg.id);
|
|
545
|
+
allSegments.push(seg);
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
return allSegments;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
/**
|
|
554
|
+
* Resolve only loader segments for all entries (used when serving cached non-loader segments).
|
|
555
|
+
*/
|
|
556
|
+
export async function resolveLoadersOnly<TEnv>(
|
|
557
|
+
entries: EntryData[],
|
|
558
|
+
context: HandlerContext<any, TEnv>,
|
|
559
|
+
deps: SegmentResolutionDeps<TEnv>,
|
|
560
|
+
): Promise<ResolvedSegment[]> {
|
|
561
|
+
const loaderSegments: ResolvedSegment[] = [];
|
|
562
|
+
|
|
563
|
+
for (const entry of entries) {
|
|
564
|
+
const belongsToRoute = entry.type === "route";
|
|
565
|
+
const segments = await resolveLoaders(entry, context, belongsToRoute, deps);
|
|
566
|
+
loaderSegments.push(...segments);
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
return loaderSegments;
|
|
570
|
+
}
|