@rangojs/router 0.0.0-experimental.a769fbe7 → 0.0.0-experimental.ac99d918
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 +211 -43
- package/dist/bin/rango.js +279 -102
- package/dist/testing/vitest.js +82 -0
- package/dist/vite/index.js +3313 -1160
- package/dist/vite/index.js.bak +5448 -0
- package/dist/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
- package/package.json +62 -11
- package/skills/api-client/SKILL.md +211 -0
- package/skills/breadcrumbs/SKILL.md +63 -1
- package/skills/bundle-analysis/SKILL.md +159 -0
- package/skills/cache-guide/SKILL.md +222 -30
- package/skills/caching/SKILL.md +263 -8
- package/skills/composability/SKILL.md +27 -2
- package/skills/css/SKILL.md +76 -0
- package/skills/document-cache/SKILL.md +78 -55
- package/skills/handler-use/SKILL.md +364 -0
- package/skills/hooks/SKILL.md +250 -30
- package/skills/host-router/SKILL.md +124 -22
- package/skills/i18n/SKILL.md +276 -0
- package/skills/intercept/SKILL.md +49 -5
- package/skills/layout/SKILL.md +35 -9
- package/skills/links/SKILL.md +249 -17
- package/skills/loader/SKILL.md +223 -9
- package/skills/middleware/SKILL.md +52 -13
- package/skills/migrate-nextjs/SKILL.md +584 -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 +77 -7
- package/skills/prerender/SKILL.md +123 -100
- package/skills/rango/SKILL.md +250 -22
- package/skills/react-compiler/SKILL.md +168 -0
- package/skills/response-routes/SKILL.md +122 -47
- package/skills/route/SKILL.md +66 -5
- package/skills/router-setup/SKILL.md +38 -3
- package/skills/server-actions/SKILL.md +775 -0
- package/skills/streams-and-websockets/SKILL.md +283 -0
- package/skills/tailwind/SKILL.md +27 -3
- package/skills/testing/SKILL.md +129 -0
- package/skills/testing/bindings.md +89 -0
- package/skills/testing/cache-prerender.md +124 -0
- package/skills/testing/client-components.md +122 -0
- package/skills/testing/e2e-parity.md +125 -0
- package/skills/testing/flight.md +92 -0
- package/skills/testing/handles.md +129 -0
- package/skills/testing/loader.md +128 -0
- package/skills/testing/middleware.md +99 -0
- package/skills/testing/render-handler.md +121 -0
- package/skills/testing/response-routes.md +95 -0
- package/skills/testing/reverse-and-types.md +84 -0
- package/skills/testing/server-actions.md +107 -0
- package/skills/testing/server-tree.md +128 -0
- package/skills/testing/setup.md +120 -0
- package/skills/typesafety/SKILL.md +319 -27
- package/skills/use-cache/SKILL.md +36 -5
- package/skills/vercel/SKILL.md +107 -0
- package/skills/view-transitions/SKILL.md +294 -0
- package/src/__augment-tests__/augment.ts +81 -0
- package/src/__augment-tests__/augmented.check.ts +116 -0
- package/src/__internal.ts +1 -66
- package/src/browser/action-coordinator.ts +53 -36
- package/src/browser/action-fence.ts +47 -0
- package/src/browser/app-shell.ts +39 -0
- package/src/browser/app-version.ts +14 -0
- package/src/browser/cookie-name.ts +140 -0
- package/src/browser/event-controller.ts +81 -147
- package/src/browser/history-state.ts +21 -0
- package/src/browser/index.ts +3 -3
- package/src/browser/invalidate-client-cache.ts +52 -0
- package/src/browser/navigation-bridge.ts +66 -14
- package/src/browser/navigation-client.ts +172 -109
- package/src/browser/navigation-store-handle.ts +38 -0
- package/src/browser/navigation-store.ts +76 -67
- package/src/browser/navigation-transaction.ts +9 -59
- package/src/browser/partial-update.ts +79 -93
- package/src/browser/prefetch/cache.ts +180 -62
- package/src/browser/prefetch/fetch.ts +252 -39
- package/src/browser/prefetch/queue.ts +42 -8
- package/src/browser/rango-state.ts +158 -76
- package/src/browser/react/Link.tsx +72 -10
- package/src/browser/react/NavigationProvider.tsx +83 -31
- package/src/browser/react/ScrollRestoration.tsx +10 -6
- package/src/browser/react/context.ts +7 -2
- package/src/browser/react/filter-segment-order.ts +49 -7
- package/src/browser/react/index.ts +0 -48
- package/src/browser/react/location-state-shared.ts +166 -8
- package/src/browser/react/location-state.ts +39 -14
- package/src/browser/react/use-action.ts +6 -15
- package/src/browser/react/use-handle.ts +23 -69
- package/src/browser/react/use-link-status.ts +0 -4
- package/src/browser/react/use-navigation.ts +22 -5
- package/src/browser/react/use-params.ts +20 -10
- package/src/browser/react/use-reverse.ts +106 -0
- package/src/browser/react/use-router.ts +46 -11
- package/src/browser/react/use-search-params.ts +0 -5
- package/src/browser/react/use-segments.ts +11 -21
- package/src/browser/response-adapter.ts +52 -1
- package/src/browser/rsc-router.tsx +111 -24
- package/src/browser/scroll-restoration.ts +29 -19
- package/src/browser/segment-reconciler.ts +36 -14
- package/src/browser/segment-structure-assert.ts +2 -2
- package/src/browser/server-action-bridge.ts +176 -62
- package/src/browser/types.ts +60 -11
- package/src/browser/validate-redirect-origin.ts +43 -16
- package/src/build/collect-fallback-refs.ts +107 -0
- package/src/build/generate-manifest.ts +65 -40
- package/src/build/generate-route-types.ts +6 -0
- package/src/build/index.ts +8 -2
- package/src/build/prefix-tree-utils.ts +123 -0
- package/src/build/route-trie.ts +137 -32
- package/src/build/route-types/codegen.ts +4 -4
- package/src/build/route-types/include-resolution.ts +9 -2
- package/src/build/route-types/param-extraction.ts +6 -3
- package/src/build/route-types/per-module-writer.ts +7 -4
- package/src/build/route-types/router-processing.ts +333 -94
- 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-error.ts +104 -0
- package/src/cache/cache-policy.ts +68 -28
- package/src/cache/cache-runtime.ts +134 -32
- package/src/cache/cache-scope.ts +100 -74
- package/src/cache/cache-tag.ts +98 -0
- package/src/cache/cf/cf-cache-store.ts +2256 -241
- package/src/cache/cf/index.ts +6 -16
- package/src/cache/document-cache.ts +61 -20
- package/src/cache/handle-snapshot.ts +63 -0
- package/src/cache/index.ts +22 -20
- package/src/cache/memory-segment-store.ts +136 -37
- package/src/cache/profile-registry.ts +6 -30
- package/src/cache/read-through-swr.ts +41 -11
- package/src/cache/segment-codec.ts +0 -16
- package/src/cache/tag-invalidation.ts +230 -0
- package/src/cache/types.ts +33 -100
- package/src/cache/vercel/index.ts +11 -0
- package/src/cache/vercel/vercel-cache-store.ts +799 -0
- package/src/client.rsc.tsx +6 -21
- package/src/client.tsx +108 -290
- package/src/component-utils.ts +19 -0
- package/src/context-var.ts +17 -5
- package/src/decode-loader-results.ts +36 -0
- package/src/defer.ts +196 -0
- package/src/deps/browser.ts +0 -1
- package/src/deps/ssr.ts +0 -1
- package/src/errors.ts +30 -4
- package/src/handle.ts +70 -22
- package/src/handles/MetaTags.tsx +0 -14
- package/src/handles/breadcrumbs.ts +16 -5
- package/src/handles/meta.ts +0 -39
- package/src/host/cookie-handler.ts +0 -36
- package/src/host/errors.ts +0 -24
- package/src/host/index.ts +8 -2
- package/src/host/pattern-matcher.ts +7 -50
- package/src/host/router.ts +107 -99
- package/src/host/testing.ts +40 -27
- package/src/host/types.ts +37 -4
- package/src/host/utils.ts +1 -1
- package/src/href-client.ts +137 -22
- package/src/index.rsc.ts +69 -10
- package/src/index.ts +112 -14
- package/src/internal-debug.ts +2 -4
- package/src/loader-store.ts +500 -0
- package/src/loader.rsc.ts +20 -13
- package/src/loader.ts +12 -11
- package/src/missing-id-error.ts +68 -0
- package/src/network-error-thrower.tsx +1 -6
- package/src/outlet-context.ts +1 -1
- package/src/outlet-provider.tsx +1 -5
- package/src/prerender/param-hash.ts +10 -11
- package/src/prerender/store.ts +37 -41
- package/src/prerender.ts +198 -82
- package/src/redirect-origin.ts +100 -0
- package/src/response-utils.ts +37 -0
- package/src/reverse.ts +65 -15
- package/src/root-error-boundary.tsx +1 -19
- package/src/route-content-wrapper.tsx +7 -72
- package/src/route-definition/dsl-helpers.ts +413 -275
- package/src/route-definition/helper-factories.ts +29 -139
- package/src/route-definition/helpers-types.ts +107 -32
- package/src/route-definition/index.ts +3 -0
- package/src/route-definition/redirect.ts +50 -8
- package/src/route-definition/resolve-handler-use.ts +161 -0
- package/src/route-definition/use-item-types.ts +32 -0
- package/src/route-map-builder.ts +0 -16
- package/src/route-types.ts +37 -41
- package/src/router/basename.ts +14 -0
- package/src/router/content-negotiation.ts +108 -9
- package/src/router/error-handling.ts +13 -17
- package/src/router/find-match.ts +44 -23
- package/src/router/handler-context.ts +46 -30
- package/src/router/intercept-resolution.ts +23 -23
- package/src/router/lazy-includes.ts +15 -52
- package/src/router/loader-resolution.ts +207 -30
- package/src/router/logging.ts +0 -6
- package/src/router/manifest.ts +40 -42
- package/src/router/match-api.ts +120 -204
- package/src/router/match-context.ts +0 -22
- package/src/router/match-handlers.ts +58 -58
- package/src/router/match-middleware/background-revalidation.ts +0 -7
- package/src/router/match-middleware/cache-lookup.ts +161 -262
- package/src/router/match-middleware/cache-store.ts +3 -33
- package/src/router/match-middleware/intercept-resolution.ts +0 -22
- package/src/router/match-middleware/segment-resolution.ts +45 -14
- package/src/router/match-pipelines.ts +1 -42
- package/src/router/match-result.ts +87 -39
- package/src/router/metrics.ts +0 -34
- package/src/router/middleware-types.ts +7 -140
- package/src/router/middleware.ts +169 -140
- package/src/router/navigation-snapshot.ts +131 -0
- package/src/router/params-util.ts +23 -0
- package/src/router/pattern-matching.ts +109 -63
- package/src/router/prerender-match.ts +190 -54
- package/src/router/preview-match.ts +32 -102
- package/src/router/request-classification.ts +276 -0
- package/src/router/revalidation.ts +63 -55
- package/src/router/route-snapshot.ts +244 -0
- package/src/router/router-context.ts +0 -27
- package/src/router/router-interfaces.ts +100 -35
- package/src/router/router-options.ts +91 -11
- package/src/router/router-registry.ts +2 -5
- package/src/router/segment-resolution/fresh.ts +119 -65
- package/src/router/segment-resolution/helpers.ts +34 -0
- package/src/router/segment-resolution/loader-cache.ts +40 -37
- package/src/router/segment-resolution/revalidation.ts +329 -305
- package/src/router/segment-resolution/static-store.ts +19 -5
- package/src/router/segment-resolution/streamed-handler-telemetry.ts +52 -0
- package/src/router/segment-resolution/view-transition-default.ts +36 -0
- package/src/router/segment-resolution.ts +4 -1
- package/src/router/segment-wrappers.ts +0 -3
- package/src/router/state-cookie-name.ts +33 -0
- package/src/router/substitute-pattern-params.ts +56 -0
- package/src/router/telemetry-otel.ts +0 -20
- package/src/router/telemetry.ts +96 -19
- package/src/router/timeout.ts +0 -20
- package/src/router/trie-matching.ts +91 -46
- package/src/router/types.ts +9 -63
- package/src/router/url-params.ts +44 -0
- package/src/router.ts +128 -42
- package/src/rsc/handler-context.ts +3 -2
- package/src/rsc/handler.ts +492 -409
- package/src/rsc/helpers.ts +162 -46
- package/src/rsc/index.ts +1 -1
- package/src/rsc/json-route-result.ts +38 -0
- package/src/rsc/loader-fetch.ts +18 -3
- package/src/rsc/manifest-init.ts +33 -42
- package/src/rsc/origin-guard.ts +39 -25
- package/src/rsc/progressive-enhancement.ts +28 -4
- package/src/rsc/redirect-guard.ts +99 -0
- package/src/rsc/response-error.ts +79 -12
- package/src/rsc/response-route-handler.ts +90 -63
- package/src/rsc/rsc-rendering.ts +53 -56
- package/src/rsc/runtime-warnings.ts +23 -10
- package/src/rsc/server-action.ts +74 -69
- package/src/rsc/ssr-setup.ts +18 -2
- package/src/rsc/types.ts +22 -9
- package/src/runtime-env.ts +18 -0
- package/src/search-params.ts +4 -20
- package/src/segment-content-promise.ts +67 -0
- package/src/segment-loader-promise.ts +134 -0
- package/src/segment-system.tsx +208 -201
- package/src/serialize.ts +243 -0
- package/src/server/context.ts +211 -52
- package/src/server/cookie-store.ts +80 -5
- package/src/server/handle-store.ts +26 -24
- package/src/server/loader-registry.ts +10 -28
- package/src/server/request-context.ts +289 -124
- package/src/ssr/index.tsx +22 -15
- package/src/static-handler.ts +27 -18
- package/src/testing/cache-status.ts +162 -0
- package/src/testing/collect-handle.ts +40 -0
- package/src/testing/dispatch.ts +618 -0
- package/src/testing/dom.entry.ts +22 -0
- package/src/testing/e2e/fixture.ts +188 -0
- package/src/testing/e2e/index.ts +128 -0
- package/src/testing/e2e/matchers.ts +35 -0
- package/src/testing/e2e/page-helpers.ts +272 -0
- package/src/testing/e2e/parity.ts +387 -0
- package/src/testing/e2e/server.ts +195 -0
- package/src/testing/flight-matchers.ts +97 -0
- package/src/testing/flight-normalize.ts +11 -0
- package/src/testing/flight-runtime.d.ts +57 -0
- package/src/testing/flight-tree.ts +682 -0
- package/src/testing/flight.entry.ts +52 -0
- package/src/testing/flight.ts +232 -0
- package/src/testing/generated-routes.ts +183 -0
- package/src/testing/index.ts +99 -0
- package/src/testing/internal/context.ts +348 -0
- package/src/testing/internal/flight-client-globals.ts +30 -0
- package/src/testing/internal/seed-vars.ts +54 -0
- package/src/testing/render-handler.ts +330 -0
- package/src/testing/render-route.tsx +566 -0
- package/src/testing/run-loader.ts +378 -0
- package/src/testing/run-middleware.ts +205 -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 +305 -0
- package/src/theme/ThemeProvider.tsx +0 -52
- package/src/theme/ThemeScript.tsx +0 -6
- package/src/theme/constants.ts +0 -12
- package/src/theme/index.ts +0 -7
- package/src/theme/theme-context.ts +1 -5
- package/src/theme/theme-script.ts +0 -14
- package/src/theme/use-theme.ts +0 -3
- package/src/types/boundaries.ts +0 -35
- package/src/types/cache-types.ts +17 -8
- package/src/types/error-types.ts +30 -90
- package/src/types/global-namespace.ts +54 -41
- package/src/types/handler-context.ts +124 -70
- package/src/types/index.ts +1 -10
- package/src/types/loader-types.ts +40 -11
- package/src/types/request-scope.ts +107 -0
- package/src/types/route-config.ts +6 -50
- package/src/types/route-entry.ts +12 -7
- package/src/types/segments.ts +36 -15
- package/src/urls/include-helper.ts +33 -70
- package/src/urls/index.ts +1 -11
- package/src/urls/path-helper-types.ts +58 -11
- package/src/urls/path-helper.ts +57 -111
- package/src/urls/pattern-types.ts +48 -19
- package/src/urls/response-types.ts +25 -22
- package/src/urls/type-extraction.ts +58 -139
- package/src/urls/urls-function.ts +1 -18
- package/src/use-loader.tsx +346 -89
- package/src/vite/debug.ts +185 -0
- package/src/vite/discovery/bundle-postprocess.ts +36 -38
- package/src/vite/discovery/discover-routers.ts +130 -85
- 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 +192 -99
- 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 +51 -4
- package/src/vite/discovery/virtual-module-codegen.ts +14 -34
- package/src/vite/index.ts +8 -0
- package/src/vite/plugin-types.ts +236 -6
- package/src/vite/plugins/cjs-to-esm.ts +8 -18
- package/src/vite/plugins/client-ref-dedup.ts +16 -11
- package/src/vite/plugins/client-ref-hashing.ts +28 -15
- 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 +194 -0
- package/src/vite/plugins/expose-action-id.ts +48 -95
- package/src/vite/plugins/expose-id-utils.ts +11 -50
- package/src/vite/plugins/expose-ids/export-analysis.ts +76 -34
- package/src/vite/plugins/expose-ids/handler-transform.ts +10 -48
- package/src/vite/plugins/expose-ids/loader-transform.ts +3 -20
- package/src/vite/plugins/expose-ids/router-transform.ts +20 -16
- package/src/vite/plugins/expose-internal-ids.ts +554 -317
- package/src/vite/plugins/performance-tracks.ts +65 -207
- package/src/vite/plugins/refresh-cmd.ts +89 -27
- package/src/vite/plugins/use-cache-transform.ts +73 -83
- package/src/vite/plugins/vercel-output.ts +258 -0
- package/src/vite/plugins/version-injector.ts +21 -25
- package/src/vite/plugins/version-plugin.ts +32 -23
- package/src/vite/plugins/virtual-entries.ts +46 -17
- package/src/vite/rango.ts +207 -125
- package/src/vite/router-discovery.ts +931 -133
- package/src/vite/utils/ast-handler-extract.ts +15 -31
- package/src/vite/utils/banner.ts +1 -1
- package/src/vite/utils/bundle-analysis.ts +10 -15
- package/src/vite/utils/client-chunks.ts +184 -0
- package/src/vite/utils/forward-user-plugins.ts +171 -0
- package/src/vite/utils/manifest-utils.ts +4 -59
- package/src/vite/utils/package-resolution.ts +20 -52
- package/src/vite/utils/prerender-utils.ts +27 -29
- package/src/vite/utils/shared-utils.ts +97 -44
- package/src/browser/action-response-classifier.ts +0 -99
- package/src/browser/debug-channel.ts +0 -93
- package/src/browser/react/use-client-cache.ts +0 -58
- package/src/browser/shallow.ts +0 -40
- package/src/handles/index.ts +0 -7
- package/src/router/middleware-cookies.ts +0 -55
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
getParallelEntries,
|
|
15
15
|
getParallelSlotEntries,
|
|
16
16
|
type EntryData,
|
|
17
|
+
type ParallelEntryData,
|
|
17
18
|
} from "../../server/context";
|
|
18
19
|
import type {
|
|
19
20
|
HandlerContext,
|
|
@@ -34,55 +35,40 @@ import {
|
|
|
34
35
|
import { resolveLoaderData } from "./loader-cache.js";
|
|
35
36
|
import {
|
|
36
37
|
handleHandlerResult,
|
|
38
|
+
warnOnStreamedResponse,
|
|
37
39
|
tryStaticHandler,
|
|
38
40
|
tryStaticSlot,
|
|
39
41
|
resolveLayoutComponent,
|
|
40
42
|
resolveWithErrorBoundary,
|
|
41
43
|
} from "./helpers.js";
|
|
44
|
+
import { applyViewTransitionDefault } from "./view-transition-default.js";
|
|
42
45
|
import { getRouterContext } from "../router-context.js";
|
|
43
46
|
import { resolveSink, safeEmit } from "../telemetry.js";
|
|
44
|
-
import {
|
|
45
|
-
import {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
47
|
+
import { observeStreamedHandler } from "./streamed-handler-telemetry.js";
|
|
48
|
+
import {
|
|
49
|
+
track,
|
|
50
|
+
RangoContext,
|
|
51
|
+
runInsideLoaderScope,
|
|
52
|
+
} from "../../server/context.js";
|
|
50
53
|
|
|
51
54
|
/**
|
|
52
|
-
*
|
|
53
|
-
*
|
|
55
|
+
* Trace a parallel slot that's being force-rendered on a full refetch (client
|
|
56
|
+
* has no cached state). User revalidate fns are bypassed in this case — see
|
|
57
|
+
* the call sites for the load-bearing rationale.
|
|
54
58
|
*/
|
|
55
|
-
function
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
segmentType: string,
|
|
59
|
-
pathname?: string,
|
|
60
|
-
routeKey?: string,
|
|
61
|
-
params?: Record<string, string>,
|
|
59
|
+
function traceFullRefetchedParallelSlot(
|
|
60
|
+
parallelId: string,
|
|
61
|
+
belongsToRoute: boolean,
|
|
62
62
|
): void {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
promise.catch((err: unknown) => {
|
|
73
|
-
const errorObj = err instanceof Error ? err : new Error(String(err));
|
|
74
|
-
safeEmit(sink, {
|
|
75
|
-
type: "handler.error",
|
|
76
|
-
timestamp: performance.now(),
|
|
77
|
-
requestId: reqId,
|
|
78
|
-
segmentId,
|
|
79
|
-
segmentType,
|
|
80
|
-
error: errorObj,
|
|
81
|
-
handledByBoundary: true,
|
|
82
|
-
pathname,
|
|
83
|
-
routeKey,
|
|
84
|
-
params,
|
|
85
|
-
});
|
|
63
|
+
if (!isTraceActive()) return;
|
|
64
|
+
pushRevalidationTraceEntry({
|
|
65
|
+
segmentId: parallelId,
|
|
66
|
+
segmentType: "parallel",
|
|
67
|
+
belongsToRoute,
|
|
68
|
+
source: "parallel",
|
|
69
|
+
defaultShouldRevalidate: true,
|
|
70
|
+
finalShouldRevalidate: true,
|
|
71
|
+
reason: "full-refetch",
|
|
86
72
|
});
|
|
87
73
|
}
|
|
88
74
|
|
|
@@ -233,7 +219,9 @@ export async function resolveLoadersWithRevalidation<TEnv>(
|
|
|
233
219
|
params: ctx.params,
|
|
234
220
|
loaderId: loader.$$id,
|
|
235
221
|
loaderData: deps.wrapLoaderPromise(
|
|
236
|
-
|
|
222
|
+
runInsideLoaderScope(() =>
|
|
223
|
+
resolveLoaderData(loaderEntry, ctx, ctx.pathname),
|
|
224
|
+
),
|
|
237
225
|
entry,
|
|
238
226
|
segmentId,
|
|
239
227
|
ctx.pathname,
|
|
@@ -314,6 +302,39 @@ export async function resolveLoadersOnlyWithRevalidation<TEnv>(
|
|
|
314
302
|
const childBelongsToRoute = belongsToRoute || entry.type === "route";
|
|
315
303
|
for (const layoutEntry of entry.layout) {
|
|
316
304
|
await collectEntryLoaders(layoutEntry, childBelongsToRoute);
|
|
305
|
+
// Inherit route loaders for orphan layouts with parallels.
|
|
306
|
+
// Resolve directly — do NOT re-enter collectEntryLoaders with the
|
|
307
|
+
// route entry, as that would re-iterate route.layout and loop.
|
|
308
|
+
if (
|
|
309
|
+
entry.type === "route" &&
|
|
310
|
+
entry.loader &&
|
|
311
|
+
entry.loader.length > 0 &&
|
|
312
|
+
Object.keys(layoutEntry.parallel).length > 0
|
|
313
|
+
) {
|
|
314
|
+
const inherited = await resolveLoadersWithRevalidation(
|
|
315
|
+
entry,
|
|
316
|
+
context,
|
|
317
|
+
childBelongsToRoute,
|
|
318
|
+
clientSegmentIds,
|
|
319
|
+
prevParams,
|
|
320
|
+
request,
|
|
321
|
+
prevUrl,
|
|
322
|
+
nextUrl,
|
|
323
|
+
routeKey,
|
|
324
|
+
deps,
|
|
325
|
+
actionContext,
|
|
326
|
+
layoutEntry.shortCode,
|
|
327
|
+
stale,
|
|
328
|
+
);
|
|
329
|
+
for (const seg of inherited.segments) {
|
|
330
|
+
if (!seenIds.has(seg.id)) {
|
|
331
|
+
seenIds.add(seg.id);
|
|
332
|
+
seg._inherited = true;
|
|
333
|
+
allLoaderSegments.push(seg);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
allMatchedIds.push(...inherited.matchedIds);
|
|
337
|
+
}
|
|
317
338
|
}
|
|
318
339
|
}
|
|
319
340
|
|
|
@@ -366,6 +387,97 @@ export function buildEntryRevalidateMap(
|
|
|
366
387
|
return map;
|
|
367
388
|
}
|
|
368
389
|
|
|
390
|
+
/**
|
|
391
|
+
* Resolve the component for a single parallel slot on the revalidation path.
|
|
392
|
+
* Pure component resolution shared verbatim by
|
|
393
|
+
* resolveParallelSegmentsWithRevalidation and the orphan-inlined loop in
|
|
394
|
+
* resolveOrphanLayoutWithRevalidation: try the static slot cache, else run the
|
|
395
|
+
* slot handler (pinning _currentSegmentId to the slot id so handle pushes land
|
|
396
|
+
* in the slot's own bucket, and wrapping a streamed handler). Returns the
|
|
397
|
+
* resolved component and whether the handler actually ran. Does NOT touch the
|
|
398
|
+
* revalidate-default policy (the caller decides shouldResolve, including the
|
|
399
|
+
* orphan-vs-main defaultOverride divergence) or loader-resolution ordering.
|
|
400
|
+
*/
|
|
401
|
+
async function resolveParallelSlotComponent<TEnv>(args: {
|
|
402
|
+
shouldResolve: boolean;
|
|
403
|
+
parallelEntry: ParallelEntryData;
|
|
404
|
+
slot: string;
|
|
405
|
+
parallelId: string;
|
|
406
|
+
handler:
|
|
407
|
+
| ((ctx: HandlerContext<any, TEnv>) => ReactNode | Promise<ReactNode>)
|
|
408
|
+
| ReactNode
|
|
409
|
+
| undefined;
|
|
410
|
+
context: HandlerContext<any, TEnv>;
|
|
411
|
+
deps: SegmentResolutionDeps<TEnv>;
|
|
412
|
+
routeKey: string;
|
|
413
|
+
params: Record<string, string>;
|
|
414
|
+
}): Promise<{ component: ReactNode | undefined; handlerRan: boolean }> {
|
|
415
|
+
const {
|
|
416
|
+
shouldResolve,
|
|
417
|
+
parallelEntry,
|
|
418
|
+
slot,
|
|
419
|
+
parallelId,
|
|
420
|
+
handler,
|
|
421
|
+
context,
|
|
422
|
+
deps,
|
|
423
|
+
routeKey,
|
|
424
|
+
params,
|
|
425
|
+
} = args;
|
|
426
|
+
|
|
427
|
+
let component: ReactNode | undefined;
|
|
428
|
+
let handlerRan = false;
|
|
429
|
+
if (shouldResolve) {
|
|
430
|
+
component = await tryStaticSlot(parallelEntry, slot, parallelId);
|
|
431
|
+
// tryStaticSlot returning a value means the static cache supplied the
|
|
432
|
+
// component — handler did NOT run. handlerRan stays false.
|
|
433
|
+
}
|
|
434
|
+
if (component === undefined) {
|
|
435
|
+
const hasLoadingFallback =
|
|
436
|
+
parallelEntry.loading !== undefined && parallelEntry.loading !== false;
|
|
437
|
+
if (!shouldResolve) {
|
|
438
|
+
component = null;
|
|
439
|
+
} else if (handler === undefined) {
|
|
440
|
+
// Handler evicted (production static slot) but static lookup missed.
|
|
441
|
+
// Nothing to render — use null so the client keeps its cached version.
|
|
442
|
+
component = null;
|
|
443
|
+
} else {
|
|
444
|
+
// Slot-keyed pushes — slot owns its own bucket, parent layout owns its
|
|
445
|
+
// own. On slot-only revalidations the partial merge updates only the
|
|
446
|
+
// slot's bucket; the parent's bucket stays intact.
|
|
447
|
+
(context as InternalHandlerContext<any, TEnv>)._currentSegmentId =
|
|
448
|
+
parallelId;
|
|
449
|
+
handlerRan = true;
|
|
450
|
+
if (hasLoadingFallback) {
|
|
451
|
+
const result =
|
|
452
|
+
typeof handler === "function" ? handler(context) : handler;
|
|
453
|
+
if (result instanceof Promise) {
|
|
454
|
+
warnOnStreamedResponse(result, parallelId);
|
|
455
|
+
const tracked = deps.trackHandler(result, {
|
|
456
|
+
segmentId: parallelId,
|
|
457
|
+
segmentType: "parallel",
|
|
458
|
+
});
|
|
459
|
+
observeStreamedHandler(
|
|
460
|
+
tracked,
|
|
461
|
+
parallelId,
|
|
462
|
+
"parallel",
|
|
463
|
+
context.pathname,
|
|
464
|
+
routeKey,
|
|
465
|
+
params,
|
|
466
|
+
);
|
|
467
|
+
component = tracked as ReactNode;
|
|
468
|
+
} else {
|
|
469
|
+
component = result as ReactNode;
|
|
470
|
+
}
|
|
471
|
+
} else {
|
|
472
|
+
component =
|
|
473
|
+
typeof handler === "function" ? await handler(context) : handler;
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
return { component, handlerRan };
|
|
479
|
+
}
|
|
480
|
+
|
|
369
481
|
/**
|
|
370
482
|
* Resolve parallel segments with revalidation.
|
|
371
483
|
*/
|
|
@@ -383,9 +495,30 @@ export async function resolveParallelSegmentsWithRevalidation<TEnv>(
|
|
|
383
495
|
deps: SegmentResolutionDeps<TEnv>,
|
|
384
496
|
actionContext?: ActionContext,
|
|
385
497
|
stale?: boolean,
|
|
498
|
+
options?: {
|
|
499
|
+
/**
|
|
500
|
+
* Seed for an unknown parent-chain slot (slot not in clientSegmentIds) when
|
|
501
|
+
* there are no deciding revalidate fns. "type-derived" (default, main path):
|
|
502
|
+
* `belongsToRoute || isNewParent`. "force-render" (orphan path): always
|
|
503
|
+
* `true` — orphan parallels always belong to the route and must render
|
|
504
|
+
* unless the user opts out via revalidate(); the #482 blank-parent-chain-
|
|
505
|
+
* slot guard.
|
|
506
|
+
*/
|
|
507
|
+
parentChainDefault?: "type-derived" | "force-render";
|
|
508
|
+
/**
|
|
509
|
+
* When a slot's loaders are resolved relative to the slot segment push.
|
|
510
|
+
* "after" (default, main path) pushes the slot segment first; "before"
|
|
511
|
+
* (orphan path) resolves loaders first. This only changes the
|
|
512
|
+
* segments/matchedIds emission ORDER (the client reconciler is insensitive
|
|
513
|
+
* to it: loader sub-ids are filtered out and slots are re-grouped by parent).
|
|
514
|
+
*/
|
|
515
|
+
loaderOrder?: "after" | "before";
|
|
516
|
+
},
|
|
386
517
|
): Promise<SegmentRevalidationResult> {
|
|
387
518
|
const segments: ResolvedSegment[] = [];
|
|
388
519
|
const matchedIds: string[] = [];
|
|
520
|
+
const parentChainDefault = options?.parentChainDefault ?? "type-derived";
|
|
521
|
+
const loaderOrder = options?.loaderOrder ?? "after";
|
|
389
522
|
|
|
390
523
|
const resolvedParallelEntries = new Set<string>();
|
|
391
524
|
for (const { slot, entry: parallelEntry } of getParallelSlotEntries(
|
|
@@ -410,44 +543,61 @@ export async function resolveParallelSegmentsWithRevalidation<TEnv>(
|
|
|
410
543
|
|
|
411
544
|
const isFullRefetch = clientSegmentIds.size === 0;
|
|
412
545
|
const isNewParent = !clientSegmentIds.has(entry.shortCode);
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
546
|
+
|
|
547
|
+
// A slot's loaders (never cached) are deduped per parallel entry and
|
|
548
|
+
// emitted either before or after the slot segment per loaderOrder.
|
|
549
|
+
const resolveSlotLoaders = async () => {
|
|
550
|
+
if (resolvedParallelEntries.has(parallelEntry.id)) return;
|
|
551
|
+
const loaderResult = await resolveLoadersWithRevalidation(
|
|
552
|
+
parallelEntry,
|
|
553
|
+
context,
|
|
554
|
+
belongsToRoute,
|
|
555
|
+
clientSegmentIds,
|
|
556
|
+
prevParams,
|
|
557
|
+
request,
|
|
558
|
+
prevUrl,
|
|
559
|
+
nextUrl,
|
|
560
|
+
routeKey,
|
|
561
|
+
deps,
|
|
562
|
+
actionContext,
|
|
563
|
+
entry.shortCode,
|
|
564
|
+
stale,
|
|
565
|
+
);
|
|
566
|
+
segments.push(...loaderResult.segments);
|
|
567
|
+
matchedIds.push(...loaderResult.matchedIds);
|
|
568
|
+
resolvedParallelEntries.add(parallelEntry.id);
|
|
569
|
+
};
|
|
570
|
+
|
|
571
|
+
if (loaderOrder === "before") {
|
|
572
|
+
await resolveSlotLoaders();
|
|
420
573
|
}
|
|
574
|
+
// Always announce the slot in matchedIds — it's unconditionally appended
|
|
575
|
+
// to `segments` below, and a segment present in segments but missing from
|
|
576
|
+
// matched lets the client prune it (then it's missing from clientSegmentIds
|
|
577
|
+
// on the next request, perpetuating the staleness).
|
|
578
|
+
matchedIds.push(parallelId);
|
|
421
579
|
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
}
|
|
435
|
-
return true;
|
|
436
|
-
}
|
|
580
|
+
let shouldResolve: boolean;
|
|
581
|
+
if (isFullRefetch) {
|
|
582
|
+
// Client has nothing cached — slot MUST render. User revalidate fns are
|
|
583
|
+
// bypassed here because returning false would leave the segment blank
|
|
584
|
+
// with no client-side fallback.
|
|
585
|
+
traceFullRefetchedParallelSlot(parallelId, belongsToRoute);
|
|
586
|
+
shouldResolve = true;
|
|
587
|
+
} else {
|
|
588
|
+
// For non-empty client sets, consult user revalidate fns. When the slot
|
|
589
|
+
// is unknown to the client, override the type-derived default so the
|
|
590
|
+
// soft chain seeds with the right "new segment" / "parent-chain" value.
|
|
591
|
+
let defaultOverride: { value: boolean; reason: string } | undefined;
|
|
437
592
|
if (!clientSegmentIds.has(parallelId)) {
|
|
438
|
-
const
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
finalShouldRevalidate: result,
|
|
447
|
-
reason: result ? "new-segment" : "skip-parent-chain",
|
|
448
|
-
});
|
|
449
|
-
}
|
|
450
|
-
return result;
|
|
593
|
+
const value =
|
|
594
|
+
parentChainDefault === "force-render"
|
|
595
|
+
? true
|
|
596
|
+
: belongsToRoute || isNewParent;
|
|
597
|
+
defaultOverride = {
|
|
598
|
+
value,
|
|
599
|
+
reason: value ? "new-segment" : "skip-parent-chain",
|
|
600
|
+
};
|
|
451
601
|
}
|
|
452
602
|
|
|
453
603
|
const dummySegment: ResolvedSegment = {
|
|
@@ -465,7 +615,7 @@ export async function resolveParallelSegmentsWithRevalidation<TEnv>(
|
|
|
465
615
|
: {}),
|
|
466
616
|
};
|
|
467
617
|
|
|
468
|
-
|
|
618
|
+
shouldResolve = await evaluateRevalidation({
|
|
469
619
|
segment: dummySegment,
|
|
470
620
|
prevParams,
|
|
471
621
|
getPrevSegment: null,
|
|
@@ -481,8 +631,9 @@ export async function resolveParallelSegmentsWithRevalidation<TEnv>(
|
|
|
481
631
|
actionContext,
|
|
482
632
|
stale,
|
|
483
633
|
traceSource: "parallel",
|
|
634
|
+
defaultOverride,
|
|
484
635
|
});
|
|
485
|
-
}
|
|
636
|
+
}
|
|
486
637
|
emitRevalidationDecision(
|
|
487
638
|
parallelId,
|
|
488
639
|
context.pathname,
|
|
@@ -490,44 +641,17 @@ export async function resolveParallelSegmentsWithRevalidation<TEnv>(
|
|
|
490
641
|
shouldResolve,
|
|
491
642
|
);
|
|
492
643
|
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
// Nothing to render — use null so the client keeps its cached version.
|
|
505
|
-
component = null;
|
|
506
|
-
} else if (hasLoadingFallback) {
|
|
507
|
-
const result =
|
|
508
|
-
typeof handler === "function" ? handler(context) : handler;
|
|
509
|
-
if (result instanceof Promise) {
|
|
510
|
-
const tracked = deps.trackHandler(result, {
|
|
511
|
-
segmentId: parallelId,
|
|
512
|
-
segmentType: "parallel",
|
|
513
|
-
});
|
|
514
|
-
observeStreamedHandler(
|
|
515
|
-
tracked,
|
|
516
|
-
parallelId,
|
|
517
|
-
"parallel",
|
|
518
|
-
context.pathname,
|
|
519
|
-
routeKey,
|
|
520
|
-
params,
|
|
521
|
-
);
|
|
522
|
-
component = tracked as ReactNode;
|
|
523
|
-
} else {
|
|
524
|
-
component = result as ReactNode;
|
|
525
|
-
}
|
|
526
|
-
} else {
|
|
527
|
-
component =
|
|
528
|
-
typeof handler === "function" ? await handler(context) : handler;
|
|
529
|
-
}
|
|
530
|
-
}
|
|
644
|
+
const { component, handlerRan } = await resolveParallelSlotComponent({
|
|
645
|
+
shouldResolve,
|
|
646
|
+
parallelEntry,
|
|
647
|
+
slot,
|
|
648
|
+
parallelId,
|
|
649
|
+
handler,
|
|
650
|
+
context,
|
|
651
|
+
deps,
|
|
652
|
+
routeKey,
|
|
653
|
+
params,
|
|
654
|
+
});
|
|
531
655
|
|
|
532
656
|
segments.push({
|
|
533
657
|
id: parallelId,
|
|
@@ -536,9 +660,13 @@ export async function resolveParallelSegmentsWithRevalidation<TEnv>(
|
|
|
536
660
|
index: 0,
|
|
537
661
|
component,
|
|
538
662
|
loading: parallelEntry.loading === false ? null : parallelEntry.loading,
|
|
539
|
-
transition:
|
|
663
|
+
transition: applyViewTransitionDefault(
|
|
664
|
+
parallelEntry.transition,
|
|
665
|
+
deps.viewTransitionDefault,
|
|
666
|
+
),
|
|
540
667
|
params,
|
|
541
668
|
slot,
|
|
669
|
+
_handlerRan: handlerRan,
|
|
542
670
|
belongsToRoute,
|
|
543
671
|
parallelName: `${parallelEntry.id}.${slot}`,
|
|
544
672
|
...(parallelEntry.mountPath
|
|
@@ -546,28 +674,9 @@ export async function resolveParallelSegmentsWithRevalidation<TEnv>(
|
|
|
546
674
|
: {}),
|
|
547
675
|
});
|
|
548
676
|
|
|
549
|
-
if (
|
|
550
|
-
|
|
677
|
+
if (loaderOrder === "after") {
|
|
678
|
+
await resolveSlotLoaders();
|
|
551
679
|
}
|
|
552
|
-
|
|
553
|
-
const loaderResult = await resolveLoadersWithRevalidation(
|
|
554
|
-
parallelEntry,
|
|
555
|
-
context,
|
|
556
|
-
belongsToRoute,
|
|
557
|
-
clientSegmentIds,
|
|
558
|
-
prevParams,
|
|
559
|
-
request,
|
|
560
|
-
prevUrl,
|
|
561
|
-
nextUrl,
|
|
562
|
-
routeKey,
|
|
563
|
-
deps,
|
|
564
|
-
actionContext,
|
|
565
|
-
entry.shortCode,
|
|
566
|
-
stale,
|
|
567
|
-
);
|
|
568
|
-
segments.push(...loaderResult.segments);
|
|
569
|
-
matchedIds.push(...loaderResult.matchedIds);
|
|
570
|
-
resolvedParallelEntries.add(parallelEntry.id);
|
|
571
680
|
}
|
|
572
681
|
|
|
573
682
|
return { segments, matchedIds };
|
|
@@ -593,6 +702,7 @@ export async function resolveEntryHandlerWithRevalidation<TEnv>(
|
|
|
593
702
|
): Promise<{ segment: ResolvedSegment; matchedId: string }> {
|
|
594
703
|
const matchedId = entry.shortCode;
|
|
595
704
|
|
|
705
|
+
let handlerRan = false;
|
|
596
706
|
const component = await revalidate(
|
|
597
707
|
async () => {
|
|
598
708
|
const hasSegment = clientSegmentIds.has(entry.shortCode);
|
|
@@ -669,6 +779,7 @@ export async function resolveEntryHandlerWithRevalidation<TEnv>(
|
|
|
669
779
|
return shouldRevalidate;
|
|
670
780
|
},
|
|
671
781
|
async () => {
|
|
782
|
+
handlerRan = true;
|
|
672
783
|
const doneHandler = track(`handler:${entry.id}`, 2);
|
|
673
784
|
(context as InternalHandlerContext<any, TEnv>)._currentSegmentId =
|
|
674
785
|
entry.shortCode;
|
|
@@ -683,14 +794,22 @@ export async function resolveEntryHandlerWithRevalidation<TEnv>(
|
|
|
683
794
|
return staticComponent;
|
|
684
795
|
}
|
|
685
796
|
const routeEntry = entry as Extract<EntryData, { type: "route" }>;
|
|
797
|
+
// For Passthrough routes at runtime, use the live handler instead of
|
|
798
|
+
// the build handler. At build time (context.build === true), always
|
|
799
|
+
// use the build handler from routeEntry.handler.
|
|
800
|
+
const handler =
|
|
801
|
+
!context.build && routeEntry.liveHandler
|
|
802
|
+
? routeEntry.liveHandler
|
|
803
|
+
: routeEntry.handler;
|
|
686
804
|
if (!routeEntry.loading) {
|
|
687
|
-
const result = handleHandlerResult(await
|
|
805
|
+
const result = handleHandlerResult(await handler(context));
|
|
688
806
|
doneHandler();
|
|
689
807
|
return result;
|
|
690
808
|
}
|
|
691
809
|
if (!actionContext) {
|
|
692
|
-
const result = handleHandlerResult(
|
|
810
|
+
const result = handleHandlerResult(handler(context));
|
|
693
811
|
if (result instanceof Promise) {
|
|
812
|
+
warnOnStreamedResponse(result, routeEntry.id);
|
|
694
813
|
result.finally(doneHandler).catch(() => {});
|
|
695
814
|
const tracked = deps.trackHandler(result, {
|
|
696
815
|
segmentId: entry.shortCode,
|
|
@@ -712,9 +831,7 @@ export async function resolveEntryHandlerWithRevalidation<TEnv>(
|
|
|
712
831
|
debugLog("segment.action", "resolving action route with awaited value", {
|
|
713
832
|
entryId: entry.id,
|
|
714
833
|
});
|
|
715
|
-
const actionResult = handleHandlerResult(
|
|
716
|
-
await routeEntry.handler(context),
|
|
717
|
-
);
|
|
834
|
+
const actionResult = handleHandlerResult(await handler(context));
|
|
718
835
|
doneHandler();
|
|
719
836
|
return {
|
|
720
837
|
content: Promise.resolve(actionResult),
|
|
@@ -738,13 +855,17 @@ export async function resolveEntryHandlerWithRevalidation<TEnv>(
|
|
|
738
855
|
index: 0,
|
|
739
856
|
component: resolvedComponent,
|
|
740
857
|
loading: entry.loading === false ? null : entry.loading,
|
|
741
|
-
transition:
|
|
858
|
+
transition: applyViewTransitionDefault(
|
|
859
|
+
entry.transition,
|
|
860
|
+
deps.viewTransitionDefault,
|
|
861
|
+
),
|
|
742
862
|
params,
|
|
743
863
|
belongsToRoute,
|
|
744
864
|
...(entry.type === "layout" || entry.type === "cache"
|
|
745
865
|
? { layoutName: entry.id }
|
|
746
866
|
: {}),
|
|
747
867
|
...(entry.mountPath ? { mountPath: entry.mountPath } : {}),
|
|
868
|
+
_handlerRan: handlerRan,
|
|
748
869
|
};
|
|
749
870
|
|
|
750
871
|
return { segment, matchedId };
|
|
@@ -763,7 +884,6 @@ export async function resolveSegmentWithRevalidation<TEnv>(
|
|
|
763
884
|
request: Request,
|
|
764
885
|
prevUrl: URL,
|
|
765
886
|
nextUrl: URL,
|
|
766
|
-
loaderPromises: Map<string, Promise<any>>,
|
|
767
887
|
deps: SegmentResolutionDeps<TEnv>,
|
|
768
888
|
actionContext?: ActionContext,
|
|
769
889
|
stale?: boolean,
|
|
@@ -825,11 +945,11 @@ export async function resolveSegmentWithRevalidation<TEnv>(
|
|
|
825
945
|
prevUrl,
|
|
826
946
|
nextUrl,
|
|
827
947
|
routeKey,
|
|
828
|
-
loaderPromises,
|
|
829
948
|
true,
|
|
830
949
|
deps,
|
|
831
950
|
actionContext,
|
|
832
951
|
stale,
|
|
952
|
+
entry,
|
|
833
953
|
);
|
|
834
954
|
segments.push(...orphanResult.segments);
|
|
835
955
|
matchedIds.push(...orphanResult.matchedIds);
|
|
@@ -909,7 +1029,6 @@ export async function resolveSegmentWithRevalidation<TEnv>(
|
|
|
909
1029
|
prevUrl,
|
|
910
1030
|
nextUrl,
|
|
911
1031
|
routeKey,
|
|
912
|
-
loaderPromises,
|
|
913
1032
|
false,
|
|
914
1033
|
deps,
|
|
915
1034
|
actionContext,
|
|
@@ -936,11 +1055,12 @@ export async function resolveOrphanLayoutWithRevalidation<TEnv>(
|
|
|
936
1055
|
prevUrl: URL,
|
|
937
1056
|
nextUrl: URL,
|
|
938
1057
|
routeKey: string,
|
|
939
|
-
loaderPromises: Map<string, Promise<any>>,
|
|
940
1058
|
belongsToRoute: boolean,
|
|
941
1059
|
deps: SegmentResolutionDeps<TEnv>,
|
|
942
1060
|
actionContext?: ActionContext,
|
|
943
1061
|
stale?: boolean,
|
|
1062
|
+
/** Parent route entry — its loaders are inherited so parallel slots can access them. */
|
|
1063
|
+
parentRouteEntry?: EntryData,
|
|
944
1064
|
): Promise<SegmentRevalidationResult> {
|
|
945
1065
|
invariant(
|
|
946
1066
|
orphan.type === "layout" || orphan.type === "cache",
|
|
@@ -968,6 +1088,37 @@ export async function resolveOrphanLayoutWithRevalidation<TEnv>(
|
|
|
968
1088
|
segments.push(...loaderResult.segments);
|
|
969
1089
|
matchedIds.push(...loaderResult.matchedIds);
|
|
970
1090
|
|
|
1091
|
+
// Inherit parent route's loaders so parallel slots inside this layout
|
|
1092
|
+
// can access them via useLoader(). See resolveOrphanLayout in fresh.ts.
|
|
1093
|
+
if (
|
|
1094
|
+
parentRouteEntry &&
|
|
1095
|
+
parentRouteEntry.loader &&
|
|
1096
|
+
parentRouteEntry.loader.length > 0 &&
|
|
1097
|
+
Object.keys(orphan.parallel).length > 0
|
|
1098
|
+
) {
|
|
1099
|
+
const inheritedResult = await resolveLoadersWithRevalidation(
|
|
1100
|
+
parentRouteEntry,
|
|
1101
|
+
context,
|
|
1102
|
+
belongsToRoute,
|
|
1103
|
+
clientSegmentIds,
|
|
1104
|
+
prevParams,
|
|
1105
|
+
request,
|
|
1106
|
+
prevUrl,
|
|
1107
|
+
nextUrl,
|
|
1108
|
+
routeKey,
|
|
1109
|
+
deps,
|
|
1110
|
+
actionContext,
|
|
1111
|
+
orphan.shortCode,
|
|
1112
|
+
stale,
|
|
1113
|
+
);
|
|
1114
|
+
// Tag as inherited so buildMatchResult can deduplicate when safe
|
|
1115
|
+
for (const s of inheritedResult.segments) {
|
|
1116
|
+
s._inherited = true;
|
|
1117
|
+
}
|
|
1118
|
+
segments.push(...inheritedResult.segments);
|
|
1119
|
+
matchedIds.push(...inheritedResult.matchedIds);
|
|
1120
|
+
}
|
|
1121
|
+
|
|
971
1122
|
// Handler-first: resolve orphan layout handler before its parallels
|
|
972
1123
|
// so ctx.set() values are visible to parallel children.
|
|
973
1124
|
matchedIds.push(orphan.shortCode);
|
|
@@ -1040,165 +1191,40 @@ export async function resolveOrphanLayoutWithRevalidation<TEnv>(
|
|
|
1040
1191
|
belongsToRoute,
|
|
1041
1192
|
layoutName: orphan.id,
|
|
1042
1193
|
loading: orphan.loading === false ? null : orphan.loading,
|
|
1043
|
-
transition:
|
|
1194
|
+
transition: applyViewTransitionDefault(
|
|
1195
|
+
orphan.transition,
|
|
1196
|
+
deps.viewTransitionDefault,
|
|
1197
|
+
),
|
|
1044
1198
|
...(orphan.mountPath ? { mountPath: orphan.mountPath } : {}),
|
|
1045
1199
|
});
|
|
1046
1200
|
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
)
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
resolvedParallelEntries.add(parallelEntry.id);
|
|
1075
|
-
}
|
|
1076
|
-
|
|
1077
|
-
const slots = parallelEntry.handler as Record<
|
|
1078
|
-
`@${string}`,
|
|
1079
|
-
| ((ctx: HandlerContext<any, TEnv>) => ReactNode | Promise<ReactNode>)
|
|
1080
|
-
| ReactNode
|
|
1081
|
-
>;
|
|
1082
|
-
// Handler may be undefined in production after static handler eviction.
|
|
1083
|
-
const handler = slots[slot];
|
|
1084
|
-
|
|
1085
|
-
// Use orphan.shortCode (the parent layout) to match the SSR path
|
|
1086
|
-
// (resolveParallelEntry receives parentShortCode = orphan.shortCode).
|
|
1087
|
-
// Using parallelEntry.shortCode would generate IDs the client doesn't know about.
|
|
1088
|
-
const parallelId = `${orphan.shortCode}.${slot}`;
|
|
1089
|
-
matchedIds.push(parallelId);
|
|
1090
|
-
|
|
1091
|
-
const shouldResolve = await (async () => {
|
|
1092
|
-
if (!clientSegmentIds.has(parallelId)) {
|
|
1093
|
-
if (isTraceActive()) {
|
|
1094
|
-
pushRevalidationTraceEntry({
|
|
1095
|
-
segmentId: parallelId,
|
|
1096
|
-
segmentType: "parallel",
|
|
1097
|
-
belongsToRoute,
|
|
1098
|
-
source: "parallel",
|
|
1099
|
-
defaultShouldRevalidate: true,
|
|
1100
|
-
finalShouldRevalidate: true,
|
|
1101
|
-
reason: "new-segment",
|
|
1102
|
-
});
|
|
1103
|
-
}
|
|
1104
|
-
return true;
|
|
1105
|
-
}
|
|
1106
|
-
|
|
1107
|
-
const dummySegment: ResolvedSegment = {
|
|
1108
|
-
id: parallelId,
|
|
1109
|
-
namespace: parallelEntry.id,
|
|
1110
|
-
type: "parallel",
|
|
1111
|
-
index: 0,
|
|
1112
|
-
component: null as any,
|
|
1113
|
-
params,
|
|
1114
|
-
slot,
|
|
1115
|
-
belongsToRoute,
|
|
1116
|
-
parallelName: `${parallelEntry.id}.${slot}`,
|
|
1117
|
-
...(parallelEntry.mountPath
|
|
1118
|
-
? { mountPath: parallelEntry.mountPath }
|
|
1119
|
-
: {}),
|
|
1120
|
-
};
|
|
1121
|
-
|
|
1122
|
-
return await evaluateRevalidation({
|
|
1123
|
-
segment: dummySegment,
|
|
1124
|
-
prevParams,
|
|
1125
|
-
getPrevSegment: null,
|
|
1126
|
-
request,
|
|
1127
|
-
prevUrl,
|
|
1128
|
-
nextUrl,
|
|
1129
|
-
revalidations: parallelEntry.revalidate.map((fn, i) => ({
|
|
1130
|
-
name: `revalidate${i}`,
|
|
1131
|
-
fn,
|
|
1132
|
-
})),
|
|
1133
|
-
routeKey,
|
|
1134
|
-
context,
|
|
1135
|
-
actionContext,
|
|
1136
|
-
stale,
|
|
1137
|
-
traceSource: "parallel",
|
|
1138
|
-
});
|
|
1139
|
-
})();
|
|
1140
|
-
emitRevalidationDecision(
|
|
1141
|
-
parallelId,
|
|
1142
|
-
context.pathname,
|
|
1143
|
-
routeKey,
|
|
1144
|
-
shouldResolve,
|
|
1145
|
-
);
|
|
1146
|
-
|
|
1147
|
-
let component: ReactNode | undefined;
|
|
1148
|
-
if (shouldResolve) {
|
|
1149
|
-
component = await tryStaticSlot(parallelEntry, slot, parallelId);
|
|
1150
|
-
}
|
|
1151
|
-
if (component === undefined) {
|
|
1152
|
-
const hasLoadingFallback =
|
|
1153
|
-
parallelEntry.loading !== undefined && parallelEntry.loading !== false;
|
|
1154
|
-
if (!shouldResolve) {
|
|
1155
|
-
component = null;
|
|
1156
|
-
} else if (handler === undefined) {
|
|
1157
|
-
// Handler evicted (production static slot) but static lookup missed.
|
|
1158
|
-
component = null;
|
|
1159
|
-
} else if (hasLoadingFallback) {
|
|
1160
|
-
const result =
|
|
1161
|
-
typeof handler === "function" ? handler(context) : handler;
|
|
1162
|
-
if (result instanceof Promise) {
|
|
1163
|
-
const tracked = deps.trackHandler(result, {
|
|
1164
|
-
segmentId: parallelId,
|
|
1165
|
-
segmentType: "parallel",
|
|
1166
|
-
});
|
|
1167
|
-
observeStreamedHandler(
|
|
1168
|
-
tracked,
|
|
1169
|
-
parallelId,
|
|
1170
|
-
"parallel",
|
|
1171
|
-
context.pathname,
|
|
1172
|
-
routeKey,
|
|
1173
|
-
params,
|
|
1174
|
-
);
|
|
1175
|
-
component = tracked as ReactNode;
|
|
1176
|
-
} else {
|
|
1177
|
-
component = result as ReactNode;
|
|
1178
|
-
}
|
|
1179
|
-
} else {
|
|
1180
|
-
component =
|
|
1181
|
-
typeof handler === "function" ? await handler(context) : handler;
|
|
1182
|
-
}
|
|
1183
|
-
}
|
|
1184
|
-
|
|
1185
|
-
segments.push({
|
|
1186
|
-
id: parallelId,
|
|
1187
|
-
namespace: parallelEntry.id,
|
|
1188
|
-
type: "parallel",
|
|
1189
|
-
index: 0,
|
|
1190
|
-
component,
|
|
1191
|
-
loading: parallelEntry.loading === false ? null : parallelEntry.loading,
|
|
1192
|
-
transition: parallelEntry.transition,
|
|
1193
|
-
params,
|
|
1194
|
-
slot,
|
|
1195
|
-
belongsToRoute,
|
|
1196
|
-
parallelName: `${parallelEntry.id}.${slot}`,
|
|
1197
|
-
...(parallelEntry.mountPath
|
|
1198
|
-
? { mountPath: parallelEntry.mountPath }
|
|
1199
|
-
: {}),
|
|
1200
|
-
});
|
|
1201
|
-
}
|
|
1201
|
+
// Resolve the orphan layout's parallel slots through the shared main-path
|
|
1202
|
+
// helper. The orphan policy is carried by explicit args, byte-for-byte:
|
|
1203
|
+
// - parentChainDefault "force-render": an unknown parent-chain slot seeds
|
|
1204
|
+
// `true` (orphan parallels always belong to the route — the #482 guard),
|
|
1205
|
+
// where the main path would seed `belongsToRoute || isNewParent`.
|
|
1206
|
+
// - loaderOrder "before": a slot's loaders are emitted before the slot
|
|
1207
|
+
// segment, matching the prior inlined order.
|
|
1208
|
+
// `entry.shortCode` inside the helper is `orphan.shortCode` (orphan is passed
|
|
1209
|
+
// as `entry`), so the parallel ids + loader shortCodeOverride are unchanged.
|
|
1210
|
+
const parallelResult = await resolveParallelSegmentsWithRevalidation(
|
|
1211
|
+
orphan,
|
|
1212
|
+
params,
|
|
1213
|
+
context,
|
|
1214
|
+
belongsToRoute,
|
|
1215
|
+
clientSegmentIds,
|
|
1216
|
+
prevParams,
|
|
1217
|
+
request,
|
|
1218
|
+
prevUrl,
|
|
1219
|
+
nextUrl,
|
|
1220
|
+
routeKey,
|
|
1221
|
+
deps,
|
|
1222
|
+
actionContext,
|
|
1223
|
+
stale,
|
|
1224
|
+
{ parentChainDefault: "force-render", loaderOrder: "before" },
|
|
1225
|
+
);
|
|
1226
|
+
segments.push(...parallelResult.segments);
|
|
1227
|
+
matchedIds.push(...parallelResult.matchedIds);
|
|
1202
1228
|
|
|
1203
1229
|
return { segments, matchedIds };
|
|
1204
1230
|
}
|
|
@@ -1216,7 +1242,6 @@ export async function resolveAllSegmentsWithRevalidation<TEnv>(
|
|
|
1216
1242
|
request: Request,
|
|
1217
1243
|
prevUrl: URL,
|
|
1218
1244
|
nextUrl: URL,
|
|
1219
|
-
loaderPromises: Map<string, Promise<any>>,
|
|
1220
1245
|
actionContext: ActionContext | undefined,
|
|
1221
1246
|
interceptResult: { intercept: any; entry: EntryData } | null,
|
|
1222
1247
|
localRouteName: string,
|
|
@@ -1250,7 +1275,7 @@ export async function resolveAllSegmentsWithRevalidation<TEnv>(
|
|
|
1250
1275
|
|
|
1251
1276
|
const nonParallelEntry = entry as Exclude<EntryData, { type: "parallel" }>;
|
|
1252
1277
|
if (entry.type === "cache") {
|
|
1253
|
-
const store =
|
|
1278
|
+
const store = RangoContext.getStore();
|
|
1254
1279
|
if (store) store.insideCacheScope = true;
|
|
1255
1280
|
}
|
|
1256
1281
|
const doneEntry = track(`segment:${entry.id}`, 1);
|
|
@@ -1268,7 +1293,6 @@ export async function resolveAllSegmentsWithRevalidation<TEnv>(
|
|
|
1268
1293
|
request,
|
|
1269
1294
|
prevUrl,
|
|
1270
1295
|
nextUrl,
|
|
1271
|
-
loaderPromises,
|
|
1272
1296
|
deps,
|
|
1273
1297
|
actionContext,
|
|
1274
1298
|
stale,
|