@rangojs/router 0.0.0-experimental.1b930379 → 0.0.0-experimental.1c0bdfad
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 +12 -0
- package/README.md +245 -49
- package/dist/bin/rango.js +441 -134
- package/dist/testing/vitest.js +82 -0
- package/dist/vite/index.js +3453 -1240
- package/dist/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
- package/package.json +76 -21
- package/skills/api-client/SKILL.md +211 -0
- package/skills/breadcrumbs/SKILL.md +64 -2
- package/skills/bundle-analysis/SKILL.md +159 -0
- package/skills/cache-guide/SKILL.md +247 -23
- package/skills/caching/SKILL.md +318 -15
- package/skills/composability/SKILL.md +27 -2
- package/skills/css/SKILL.md +76 -0
- package/skills/debug-manifest/SKILL.md +4 -2
- 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 +83 -23
- package/skills/i18n/SKILL.md +276 -0
- package/skills/intercept/SKILL.md +50 -6
- package/skills/layout/SKILL.md +35 -9
- package/skills/links/SKILL.md +249 -17
- package/skills/loader/SKILL.md +279 -53
- package/skills/middleware/SKILL.md +52 -13
- package/skills/migrate-nextjs/SKILL.md +584 -0
- package/skills/migrate-react-router/SKILL.md +771 -0
- package/skills/mime-routes/SKILL.md +28 -1
- package/skills/observability/SKILL.md +172 -0
- package/skills/parallel/SKILL.md +203 -7
- package/skills/prerender/SKILL.md +153 -109
- package/skills/rango/SKILL.md +251 -22
- package/skills/react-compiler/SKILL.md +168 -0
- package/skills/response-routes/SKILL.md +123 -48
- package/skills/route/SKILL.md +101 -5
- package/skills/router-setup/SKILL.md +116 -8
- package/skills/scripts/SKILL.md +179 -0
- 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 +103 -0
- package/skills/testing/cache-prerender.md +127 -0
- package/skills/testing/client-components.md +124 -0
- package/skills/testing/e2e-parity.md +125 -0
- package/skills/testing/flight.md +91 -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 +123 -0
- package/skills/typesafety/SKILL.md +332 -29
- package/skills/use-cache/SKILL.md +46 -14
- 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/connection-warmup.ts +134 -0
- package/src/browser/cookie-name.ts +140 -0
- package/src/browser/event-controller.ts +197 -150
- 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 +111 -31
- package/src/browser/navigation-client.ts +201 -67
- package/src/browser/navigation-store-handle.ts +38 -0
- package/src/browser/navigation-store.ts +76 -67
- package/src/browser/navigation-transaction.ts +18 -66
- package/src/browser/network-error-handler.ts +34 -7
- package/src/browser/partial-update.ts +187 -112
- package/src/browser/prefetch/cache.ts +230 -35
- package/src/browser/prefetch/fetch.ts +338 -39
- package/src/browser/prefetch/queue.ts +126 -20
- package/src/browser/prefetch/resource-ready.ts +77 -0
- package/src/browser/rango-state.ts +158 -76
- package/src/browser/react/Link.tsx +111 -16
- package/src/browser/react/NavigationProvider.tsx +135 -120
- package/src/browser/react/ScrollRestoration.tsx +10 -6
- package/src/browser/react/context.ts +7 -2
- package/src/browser/react/filter-segment-order.ts +66 -7
- package/src/browser/react/index.ts +0 -48
- package/src/browser/react/location-state-shared.ts +178 -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-href.tsx +8 -1
- package/src/browser/react/use-link-status.ts +33 -8
- package/src/browser/react/use-navigation.ts +32 -7
- 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 +80 -5
- package/src/browser/rsc-router.tsx +218 -76
- package/src/browser/scroll-restoration.ts +54 -42
- package/src/browser/segment-reconciler.ts +36 -9
- package/src/browser/segment-structure-assert.ts +2 -2
- package/src/browser/server-action-bridge.ts +222 -61
- package/src/browser/types.ts +91 -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 +5 -1
- package/src/build/index.ts +8 -2
- package/src/build/prefix-tree-utils.ts +123 -0
- package/src/build/route-trie.ts +165 -36
- package/src/build/route-types/ast-route-extraction.ts +15 -8
- package/src/build/route-types/codegen.ts +16 -5
- package/src/build/route-types/include-resolution.ts +125 -24
- package/src/build/route-types/param-extraction.ts +6 -3
- package/src/build/route-types/per-module-writer.ts +22 -6
- package/src/build/route-types/router-processing.ts +272 -96
- package/src/build/route-types/scan-filter.ts +9 -2
- package/src/build/route-types/source-scan.ts +216 -0
- package/src/build/runtime-discovery.ts +9 -20
- package/src/cache/cache-error.ts +104 -0
- package/src/cache/cache-key-utils.ts +29 -13
- package/src/cache/cache-policy.ts +108 -34
- package/src/cache/cache-runtime.ts +214 -48
- package/src/cache/cache-scope.ts +236 -89
- package/src/cache/cache-tag.ts +103 -0
- package/src/cache/cf/cf-base64.ts +33 -0
- package/src/cache/cf/cf-cache-constants.ts +127 -0
- package/src/cache/cf/cf-cache-store.ts +2224 -171
- package/src/cache/cf/cf-cache-types.ts +349 -0
- package/src/cache/cf/cf-kv-utils.ts +46 -0
- package/src/cache/cf/cf-tag-marker-memo.ts +105 -0
- package/src/cache/cf/index.ts +11 -17
- package/src/cache/document-cache.ts +89 -27
- package/src/cache/handle-snapshot.ts +70 -0
- package/src/cache/index.ts +11 -20
- package/src/cache/memory-segment-store.ts +136 -37
- package/src/cache/profile-registry.ts +31 -31
- package/src/cache/read-through-swr.ts +41 -11
- package/src/cache/segment-codec.ts +9 -17
- package/src/cache/tag-invalidation.ts +230 -0
- package/src/cache/taint.ts +55 -0
- package/src/cache/types.ts +37 -100
- package/src/client.rsc.tsx +44 -21
- package/src/client.tsx +119 -290
- package/src/cloudflare/index.ts +11 -0
- package/src/cloudflare/tracing.ts +109 -0
- package/src/component-utils.ts +19 -0
- package/src/components/DefaultDocument.tsx +8 -2
- package/src/context-var.ts +84 -2
- package/src/debug.ts +2 -2
- package/src/decode-loader-results.ts +52 -0
- package/src/defer.ts +196 -0
- package/src/deps/ssr.ts +0 -1
- package/src/encode-kv.ts +49 -0
- package/src/errors.ts +30 -4
- package/src/escape-script.ts +52 -0
- package/src/handle.ts +70 -22
- package/src/handles/MetaTags.tsx +56 -19
- package/src/handles/Scripts.tsx +183 -0
- package/src/handles/breadcrumbs.ts +37 -8
- package/src/handles/is-thenable.ts +19 -0
- package/src/handles/meta.ts +51 -40
- package/src/handles/script.ts +244 -0
- package/src/host/cookie-handler.ts +9 -60
- package/src/host/errors.ts +0 -24
- package/src/host/index.ts +8 -2
- package/src/host/pattern-matcher.ts +23 -52
- 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 +93 -12
- package/src/index.ts +133 -15
- package/src/internal-debug.ts +11 -10
- 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/outlet-context.ts +1 -1
- package/src/outlet-provider.tsx +1 -5
- package/src/prerender/param-hash.ts +16 -16
- package/src/prerender/store.ts +37 -41
- package/src/prerender.ts +198 -82
- package/src/redirect-origin.ts +100 -0
- package/src/regex-escape.ts +8 -0
- package/src/render-error-thrower.tsx +20 -0
- package/src/response-utils.ts +62 -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 +469 -276
- package/src/route-definition/helper-factories.ts +29 -139
- package/src/route-definition/helpers-types.ts +113 -37
- package/src/route-definition/index.ts +3 -0
- package/src/route-definition/redirect.ts +53 -12
- 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 +7 -17
- package/src/route-types.ts +37 -41
- package/src/router/basename.ts +14 -0
- package/src/router/content-negotiation.ts +164 -17
- package/src/router/error-handling.ts +45 -18
- package/src/router/find-match.ts +45 -22
- package/src/router/handler-context.ts +83 -39
- package/src/router/instrument.ts +350 -0
- package/src/router/intercept-resolution.ts +50 -24
- package/src/router/lazy-includes.ts +19 -53
- package/src/router/loader-resolution.ts +274 -56
- package/src/router/logging.ts +5 -8
- package/src/router/manifest.ts +49 -45
- 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 +33 -6
- package/src/router/match-middleware/cache-lookup.ts +214 -263
- package/src/router/match-middleware/cache-store.ts +73 -33
- package/src/router/match-middleware/intercept-resolution.ts +8 -28
- package/src/router/match-middleware/segment-resolution.ts +52 -18
- package/src/router/match-pipelines.ts +1 -42
- package/src/router/match-result.ts +104 -40
- package/src/router/metrics.ts +5 -34
- package/src/router/middleware-types.ts +13 -142
- package/src/router/middleware.ts +270 -172
- package/src/router/navigation-snapshot.ts +131 -0
- package/src/router/params-util.ts +23 -0
- package/src/router/pattern-matching.ts +132 -90
- package/src/router/prefetch-cache-ttl.ts +51 -0
- package/src/router/prerender-match.ts +195 -56
- package/src/router/preview-match.ts +32 -102
- package/src/router/request-classification.ts +276 -0
- package/src/router/revalidation.ts +123 -73
- package/src/router/route-snapshot.ts +244 -0
- package/src/router/router-context.ts +8 -28
- package/src/router/router-interfaces.ts +115 -35
- package/src/router/router-options.ts +172 -15
- package/src/router/router-registry.ts +2 -5
- package/src/router/segment-resolution/fresh.ts +264 -77
- package/src/router/segment-resolution/helpers.ts +115 -30
- package/src/router/segment-resolution/loader-cache.ts +63 -37
- package/src/router/segment-resolution/revalidation.ts +474 -385
- 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 +5 -1
- package/src/router/segment-wrappers.ts +8 -5
- package/src/router/state-cookie-name.ts +33 -0
- package/src/router/substitute-pattern-params.ts +56 -0
- package/src/router/telemetry-otel.ts +161 -199
- package/src/router/telemetry.ts +96 -19
- package/src/router/timeout.ts +0 -20
- package/src/router/tracing.ts +206 -0
- package/src/router/trie-matching.ts +163 -59
- package/src/router/types.ts +10 -63
- package/src/router/url-params.ts +44 -0
- package/src/router.ts +163 -55
- package/src/rsc/handler-context.ts +3 -2
- package/src/rsc/handler.ts +658 -511
- package/src/rsc/helpers.ts +168 -46
- package/src/rsc/index.ts +2 -5
- package/src/rsc/json-route-result.ts +38 -0
- package/src/rsc/loader-fetch.ts +127 -31
- package/src/rsc/manifest-init.ts +33 -42
- package/src/rsc/origin-guard.ts +39 -25
- package/src/rsc/progressive-enhancement.ts +77 -11
- package/src/rsc/redirect-guard.ts +99 -0
- package/src/rsc/response-cache-serve.ts +238 -0
- package/src/rsc/response-error.ts +79 -12
- package/src/rsc/response-route-handler.ts +99 -189
- package/src/rsc/rsc-rendering.ts +105 -72
- package/src/rsc/runtime-warnings.ts +23 -10
- package/src/rsc/server-action.ts +263 -112
- package/src/rsc/ssr-setup.ts +18 -2
- package/src/rsc/types.ts +32 -6
- package/src/runtime-env.ts +18 -0
- package/src/search-params.ts +35 -30
- package/src/segment-content-promise.ts +67 -0
- package/src/segment-loader-promise.ts +149 -0
- package/src/segment-system.tsx +281 -129
- package/src/serialize.ts +243 -0
- package/src/server/context.ts +309 -61
- package/src/server/cookie-parse.ts +32 -0
- package/src/server/cookie-store.ts +80 -5
- package/src/server/handle-store.ts +40 -38
- package/src/server/loader-registry.ts +26 -46
- package/src/server/request-context.ts +398 -172
- package/src/ssr/index.tsx +25 -16
- 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 +701 -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 +257 -0
- package/src/testing/generated-routes.ts +183 -0
- package/src/testing/index.ts +99 -0
- package/src/testing/internal/context.ts +371 -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 +343 -0
- package/src/testing/render-route.tsx +581 -0
- package/src/testing/run-loader.ts +385 -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 +20 -58
- package/src/theme/ThemeScript.tsx +7 -9
- package/src/theme/constants.ts +52 -13
- package/src/theme/index.ts +0 -7
- package/src/theme/theme-context.ts +1 -5
- package/src/theme/theme-script.ts +22 -21
- 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 +233 -81
- package/src/types/index.ts +1 -10
- package/src/types/loader-types.ts +44 -15
- package/src/types/request-scope.ts +112 -0
- package/src/types/route-config.ts +6 -50
- package/src/types/route-entry.ts +19 -7
- package/src/types/segments.ts +37 -14
- 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 +214 -132
- 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 +57 -6
- package/src/vite/discovery/virtual-module-codegen.ts +14 -34
- package/src/vite/index.ts +6 -0
- package/src/vite/inject-client-debug.ts +36 -0
- package/src/vite/plugin-types.ts +155 -65
- package/src/vite/plugins/cjs-to-esm.ts +16 -19
- 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 +49 -98
- package/src/vite/plugins/expose-id-utils.ts +96 -51
- package/src/vite/plugins/expose-ids/export-analysis.ts +101 -34
- package/src/vite/plugins/expose-ids/handler-transform.ts +15 -64
- package/src/vite/plugins/expose-ids/loader-transform.ts +14 -24
- package/src/vite/plugins/expose-ids/router-transform.ts +118 -29
- package/src/vite/plugins/expose-internal-ids.ts +553 -317
- package/src/vite/plugins/performance-tracks.ts +89 -0
- package/src/vite/plugins/refresh-cmd.ts +89 -27
- package/src/vite/plugins/use-cache-transform.ts +73 -83
- package/src/vite/plugins/version-injector.ts +21 -25
- package/src/vite/plugins/version-plugin.ts +46 -37
- package/src/vite/plugins/virtual-entries.ts +13 -18
- package/src/vite/rango.ts +238 -295
- package/src/vite/router-discovery.ts +940 -149
- package/src/vite/utils/ast-handler-extract.ts +26 -35
- package/src/vite/utils/banner.ts +4 -4
- package/src/vite/utils/bundle-analysis.ts +10 -15
- package/src/vite/utils/client-chunks.ts +184 -0
- package/src/vite/utils/directive-prologue.ts +40 -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 +81 -34
- package/src/vite/utils/shared-utils.ts +92 -42
- package/src/browser/action-response-classifier.ts +0 -99
- 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/network-error-thrower.tsx +0 -23
- package/src/router/middleware-cookies.ts +0 -55
|
@@ -7,7 +7,11 @@
|
|
|
7
7
|
|
|
8
8
|
import type { ReactNode } from "react";
|
|
9
9
|
import { invariant } from "../../errors";
|
|
10
|
-
import
|
|
10
|
+
import {
|
|
11
|
+
getParallelEntries,
|
|
12
|
+
getParallelSlotEntries,
|
|
13
|
+
type EntryData,
|
|
14
|
+
} from "../../server/context";
|
|
11
15
|
import type {
|
|
12
16
|
HandlerContext,
|
|
13
17
|
InternalHandlerContext,
|
|
@@ -21,53 +25,18 @@ import {
|
|
|
21
25
|
tryStaticSlot,
|
|
22
26
|
resolveLayoutComponent,
|
|
23
27
|
resolveWithErrorBoundary,
|
|
28
|
+
warnOnStreamedResponse,
|
|
29
|
+
buildLoaderErrorContext,
|
|
24
30
|
} from "./helpers.js";
|
|
31
|
+
import { applyViewTransitionDefault } from "./view-transition-default.js";
|
|
25
32
|
import { getRouterContext } from "../router-context.js";
|
|
26
|
-
import {
|
|
27
|
-
import {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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
|
-
}
|
|
33
|
+
import { observeStreamedHandler } from "./streamed-handler-telemetry.js";
|
|
34
|
+
import { observeHandler } from "../instrument.js";
|
|
35
|
+
import {
|
|
36
|
+
track,
|
|
37
|
+
RangoContext,
|
|
38
|
+
runInsideLoaderScope,
|
|
39
|
+
} from "../../server/context.js";
|
|
71
40
|
|
|
72
41
|
// ---------------------------------------------------------------------------
|
|
73
42
|
// Fresh path (full match, no revalidation)
|
|
@@ -91,8 +60,16 @@ export async function resolveLoaders<TEnv>(
|
|
|
91
60
|
const hasLoading = "loading" in entry && entry.loading !== undefined;
|
|
92
61
|
const loadingDisabled = hasLoading && entry.loading === false;
|
|
93
62
|
|
|
63
|
+
// Error context for wrapLoaderPromise: without it, a throwing DSL loader never
|
|
64
|
+
// fires createRouter({ onError }) (phase "loader") nor emits the loader.error
|
|
65
|
+
// telemetry event — wrapLoaderPromise only builds the onError/telemetry path
|
|
66
|
+
// when errorContext is supplied. Built from ctx so the live render path reports
|
|
67
|
+
// loader failures the same way handlers/actions/routing/fetchable-loaders do.
|
|
68
|
+
const errorContext = buildLoaderErrorContext(ctx);
|
|
69
|
+
|
|
94
70
|
if (!loadingDisabled) {
|
|
95
|
-
|
|
71
|
+
// Streaming loaders: promises kick off now, settle during RSC serialization.
|
|
72
|
+
const segments = loaderEntries.map((loaderEntry, i) => {
|
|
96
73
|
const { loader } = loaderEntry;
|
|
97
74
|
const segmentId = `${shortCode}D${i}.${loader.$$id}`;
|
|
98
75
|
return {
|
|
@@ -104,40 +81,63 @@ export async function resolveLoaders<TEnv>(
|
|
|
104
81
|
params: ctx.params,
|
|
105
82
|
loaderId: loader.$$id,
|
|
106
83
|
loaderData: deps.wrapLoaderPromise(
|
|
107
|
-
|
|
84
|
+
runInsideLoaderScope(() =>
|
|
85
|
+
resolveLoaderData(loaderEntry, ctx, ctx.pathname),
|
|
86
|
+
),
|
|
108
87
|
entry,
|
|
109
88
|
segmentId,
|
|
110
89
|
ctx.pathname,
|
|
90
|
+
errorContext,
|
|
111
91
|
),
|
|
112
92
|
belongsToRoute,
|
|
113
93
|
};
|
|
114
94
|
});
|
|
95
|
+
|
|
96
|
+
return segments;
|
|
115
97
|
}
|
|
116
98
|
|
|
117
99
|
// Loading disabled: still start all loaders in parallel, but only emit
|
|
118
100
|
// settled promises so handlers don't stream loading placeholders.
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
101
|
+
//
|
|
102
|
+
// Wrap each loader promise with wrapLoaderPromise BEFORE awaiting. The wrapped
|
|
103
|
+
// promise resolves to a LoaderDataResult and never rejects, routing a failed
|
|
104
|
+
// loader to its own per-loader error boundary. Awaiting the RAW promises here
|
|
105
|
+
// instead would (1) propagate a rejection to the segment-level boundary,
|
|
106
|
+
// collapsing the whole entry and discarding successful sibling data, and
|
|
107
|
+
// (2) leave the other in-flight raw promises without a .catch, producing
|
|
108
|
+
// unhandled rejections. Mirrors the loading path and intercept-resolution.
|
|
109
|
+
const pendingLoaderData = loaderEntries.map((loaderEntry, i) => {
|
|
110
|
+
const { loader } = loaderEntry;
|
|
111
|
+
const segmentId = `${shortCode}D${i}.${loader.$$id}`;
|
|
112
|
+
const wrapped = deps.wrapLoaderPromise(
|
|
113
|
+
runInsideLoaderScope(() =>
|
|
114
|
+
resolveLoaderData(loaderEntry, ctx, ctx.pathname),
|
|
115
|
+
),
|
|
116
|
+
entry,
|
|
117
|
+
segmentId,
|
|
118
|
+
ctx.pathname,
|
|
119
|
+
errorContext,
|
|
120
|
+
);
|
|
121
|
+
return { wrapped, segmentId };
|
|
122
|
+
});
|
|
123
|
+
await Promise.all(pendingLoaderData.map((p) => p.wrapped));
|
|
123
124
|
|
|
124
125
|
return loaderEntries.map((loaderEntry, i) => {
|
|
125
126
|
const { loader } = loaderEntry;
|
|
126
|
-
const
|
|
127
|
+
const pending = pendingLoaderData[i]!;
|
|
128
|
+
// The "loader:<id>" perf metric is recorded by observePhase at the single
|
|
129
|
+
// loader-metering site (useLoader, reached via ctx.use during
|
|
130
|
+
// resolveLoaderData), with the real per-loader duration rather than a
|
|
131
|
+
// Promise.all batch ceiling.
|
|
127
132
|
return {
|
|
128
|
-
id: segmentId,
|
|
133
|
+
id: pending.segmentId,
|
|
129
134
|
namespace: entry.id,
|
|
130
135
|
type: "loader" as const,
|
|
131
136
|
index: i,
|
|
132
137
|
component: null,
|
|
133
138
|
params: ctx.params,
|
|
134
139
|
loaderId: loader.$$id,
|
|
135
|
-
loaderData:
|
|
136
|
-
pendingLoaderData[i]!,
|
|
137
|
-
entry,
|
|
138
|
-
segmentId,
|
|
139
|
-
ctx.pathname,
|
|
140
|
-
),
|
|
140
|
+
loaderData: pending.wrapped,
|
|
141
141
|
belongsToRoute,
|
|
142
142
|
};
|
|
143
143
|
});
|
|
@@ -149,6 +149,15 @@ export async function resolveLoaders<TEnv>(
|
|
|
149
149
|
export interface ResolveSegmentOptions {
|
|
150
150
|
/** When true, skip resolveLoaders() calls (used for pre-rendering) */
|
|
151
151
|
skipLoaders?: boolean;
|
|
152
|
+
/**
|
|
153
|
+
* When true, a thrown render error is re-thrown instead of being converted
|
|
154
|
+
* into an error-boundary segment. Set only by the pre-render path so a
|
|
155
|
+
* build-time render failure (and a `throw new Skip()` inside a render fn)
|
|
156
|
+
* surfaces to the build instead of being silently baked into a frozen error
|
|
157
|
+
* page served as a 200 (issue #587). The live request path leaves this unset,
|
|
158
|
+
* so error boundaries keep catching at request time.
|
|
159
|
+
*/
|
|
160
|
+
throwOnError?: boolean;
|
|
152
161
|
}
|
|
153
162
|
|
|
154
163
|
/**
|
|
@@ -190,14 +199,20 @@ export async function resolveSegment<TEnv>(
|
|
|
190
199
|
index: 0,
|
|
191
200
|
component,
|
|
192
201
|
loading: entry.loading === false ? null : entry.loading,
|
|
193
|
-
transition:
|
|
202
|
+
transition: applyViewTransitionDefault(
|
|
203
|
+
entry.transition,
|
|
204
|
+
deps.viewTransitionDefault,
|
|
205
|
+
),
|
|
194
206
|
params,
|
|
195
207
|
belongsToRoute: false,
|
|
196
208
|
layoutName: entry.id,
|
|
197
209
|
...(entry.mountPath ? { mountPath: entry.mountPath } : {}),
|
|
198
210
|
});
|
|
199
211
|
|
|
200
|
-
|
|
212
|
+
const resolvedParallelEntries = new Set<string>();
|
|
213
|
+
for (const { slot, entry: parallelEntry } of getParallelSlotEntries(
|
|
214
|
+
entry.parallel,
|
|
215
|
+
)) {
|
|
201
216
|
const parallelSegments = await resolveParallelEntry(
|
|
202
217
|
parallelEntry,
|
|
203
218
|
params,
|
|
@@ -207,8 +222,11 @@ export async function resolveSegment<TEnv>(
|
|
|
207
222
|
deps,
|
|
208
223
|
options,
|
|
209
224
|
routeKey,
|
|
225
|
+
[slot],
|
|
226
|
+
!resolvedParallelEntries.has(parallelEntry.id),
|
|
210
227
|
);
|
|
211
228
|
segments.push(...parallelSegments);
|
|
229
|
+
resolvedParallelEntries.add(parallelEntry.id);
|
|
212
230
|
}
|
|
213
231
|
|
|
214
232
|
for (const orphan of entry.layout) {
|
|
@@ -244,10 +262,18 @@ export async function resolveSegment<TEnv>(
|
|
|
244
262
|
entry.shortCode,
|
|
245
263
|
);
|
|
246
264
|
if (component === undefined) {
|
|
265
|
+
// For Passthrough routes at runtime, use the live handler instead of
|
|
266
|
+
// the build handler. At build time (context.build === true), always
|
|
267
|
+
// use the build handler from entry.handler.
|
|
268
|
+
const handler =
|
|
269
|
+
!context.build && entry.liveHandler ? entry.liveHandler : entry.handler;
|
|
247
270
|
const doneRouteHandler = track(`handler:${entry.id}`, 2);
|
|
248
271
|
if (entry.loading) {
|
|
249
|
-
const result = handleHandlerResult(
|
|
272
|
+
const result = handleHandlerResult(
|
|
273
|
+
observeHandler(entry.id, handler, context),
|
|
274
|
+
);
|
|
250
275
|
if (result instanceof Promise) {
|
|
276
|
+
warnOnStreamedResponse(result, entry.id);
|
|
251
277
|
result.finally(doneRouteHandler).catch(() => {});
|
|
252
278
|
const tracked = deps.trackHandler(result, {
|
|
253
279
|
segmentId: entry.shortCode,
|
|
@@ -267,7 +293,9 @@ export async function resolveSegment<TEnv>(
|
|
|
267
293
|
component = result;
|
|
268
294
|
}
|
|
269
295
|
} else {
|
|
270
|
-
component = handleHandlerResult(
|
|
296
|
+
component = handleHandlerResult(
|
|
297
|
+
await observeHandler(entry.id, handler, context),
|
|
298
|
+
);
|
|
271
299
|
doneRouteHandler();
|
|
272
300
|
}
|
|
273
301
|
}
|
|
@@ -282,11 +310,15 @@ export async function resolveSegment<TEnv>(
|
|
|
282
310
|
deps,
|
|
283
311
|
options,
|
|
284
312
|
routeKey,
|
|
313
|
+
entry,
|
|
285
314
|
);
|
|
286
315
|
segments.push(...orphanSegments);
|
|
287
316
|
}
|
|
288
317
|
|
|
289
|
-
|
|
318
|
+
const resolvedParallelEntries = new Set<string>();
|
|
319
|
+
for (const { slot, entry: parallelEntry } of getParallelSlotEntries(
|
|
320
|
+
entry.parallel,
|
|
321
|
+
)) {
|
|
290
322
|
const parallelSegments = await resolveParallelEntry(
|
|
291
323
|
parallelEntry,
|
|
292
324
|
params,
|
|
@@ -296,8 +328,11 @@ export async function resolveSegment<TEnv>(
|
|
|
296
328
|
deps,
|
|
297
329
|
options,
|
|
298
330
|
routeKey,
|
|
331
|
+
[slot],
|
|
332
|
+
!resolvedParallelEntries.has(parallelEntry.id),
|
|
299
333
|
);
|
|
300
334
|
segments.push(...parallelSegments);
|
|
335
|
+
resolvedParallelEntries.add(parallelEntry.id);
|
|
301
336
|
}
|
|
302
337
|
|
|
303
338
|
segments.push({
|
|
@@ -305,9 +340,12 @@ export async function resolveSegment<TEnv>(
|
|
|
305
340
|
namespace: entry.id,
|
|
306
341
|
type: "route",
|
|
307
342
|
index: 0,
|
|
308
|
-
component,
|
|
343
|
+
component: component ?? null,
|
|
309
344
|
loading: entry.loading === false ? null : entry.loading,
|
|
310
|
-
transition:
|
|
345
|
+
transition: applyViewTransitionDefault(
|
|
346
|
+
entry.transition,
|
|
347
|
+
deps.viewTransitionDefault,
|
|
348
|
+
),
|
|
311
349
|
params,
|
|
312
350
|
belongsToRoute: true,
|
|
313
351
|
...(entry.mountPath ? { mountPath: entry.mountPath } : {}),
|
|
@@ -331,6 +369,9 @@ export async function resolveOrphanLayout<TEnv>(
|
|
|
331
369
|
deps: SegmentResolutionDeps<TEnv>,
|
|
332
370
|
options?: ResolveSegmentOptions,
|
|
333
371
|
routeKey?: string,
|
|
372
|
+
/** Parent route entry — its loaders are inherited by the layout so
|
|
373
|
+
* parallel slots inside this layout can access them via useLoader(). */
|
|
374
|
+
parentRouteEntry?: EntryData,
|
|
334
375
|
): Promise<ResolvedSegment[]> {
|
|
335
376
|
invariant(
|
|
336
377
|
orphan.type === "layout" || orphan.type === "cache",
|
|
@@ -346,6 +387,30 @@ export async function resolveOrphanLayout<TEnv>(
|
|
|
346
387
|
deps,
|
|
347
388
|
);
|
|
348
389
|
segments.push(...loaderSegments);
|
|
390
|
+
|
|
391
|
+
// Inherit parent route's loaders so parallel slots inside this layout
|
|
392
|
+
// can access them via useLoader(). Without this, the route's loaders
|
|
393
|
+
// are only in the route's OutletProvider (rendered as <Outlet /> content),
|
|
394
|
+
// which is a child — not a parent — of the layout's context.
|
|
395
|
+
if (
|
|
396
|
+
parentRouteEntry &&
|
|
397
|
+
parentRouteEntry.loader &&
|
|
398
|
+
parentRouteEntry.loader.length > 0 &&
|
|
399
|
+
Object.keys(orphan.parallel).length > 0
|
|
400
|
+
) {
|
|
401
|
+
const inheritedLoaders = await resolveLoaders(
|
|
402
|
+
parentRouteEntry,
|
|
403
|
+
context,
|
|
404
|
+
belongsToRoute,
|
|
405
|
+
deps,
|
|
406
|
+
orphan.shortCode,
|
|
407
|
+
);
|
|
408
|
+
// Tag as inherited so buildMatchResult can deduplicate when safe
|
|
409
|
+
for (const s of inheritedLoaders) {
|
|
410
|
+
s._inherited = true;
|
|
411
|
+
}
|
|
412
|
+
segments.push(...inheritedLoaders);
|
|
413
|
+
}
|
|
349
414
|
}
|
|
350
415
|
|
|
351
416
|
// Handler-first: orphan layout handler executes before its parallels
|
|
@@ -364,11 +429,17 @@ export async function resolveOrphanLayout<TEnv>(
|
|
|
364
429
|
belongsToRoute,
|
|
365
430
|
layoutName: orphan.id,
|
|
366
431
|
loading: orphan.loading === false ? null : orphan.loading,
|
|
367
|
-
transition:
|
|
432
|
+
transition: applyViewTransitionDefault(
|
|
433
|
+
orphan.transition,
|
|
434
|
+
deps.viewTransitionDefault,
|
|
435
|
+
),
|
|
368
436
|
...(orphan.mountPath ? { mountPath: orphan.mountPath } : {}),
|
|
369
437
|
});
|
|
370
438
|
|
|
371
|
-
|
|
439
|
+
const resolvedParallelEntries = new Set<string>();
|
|
440
|
+
for (const { slot, entry: parallelEntry } of getParallelSlotEntries(
|
|
441
|
+
orphan.parallel,
|
|
442
|
+
)) {
|
|
372
443
|
const parallelSegments = await resolveParallelEntry(
|
|
373
444
|
parallelEntry,
|
|
374
445
|
params,
|
|
@@ -378,8 +449,11 @@ export async function resolveOrphanLayout<TEnv>(
|
|
|
378
449
|
deps,
|
|
379
450
|
options,
|
|
380
451
|
routeKey,
|
|
452
|
+
[slot],
|
|
453
|
+
!resolvedParallelEntries.has(parallelEntry.id),
|
|
381
454
|
);
|
|
382
455
|
segments.push(...parallelSegments);
|
|
456
|
+
resolvedParallelEntries.add(parallelEntry.id);
|
|
383
457
|
}
|
|
384
458
|
|
|
385
459
|
return segments;
|
|
@@ -397,6 +471,8 @@ export async function resolveParallelEntry<TEnv>(
|
|
|
397
471
|
deps: SegmentResolutionDeps<TEnv>,
|
|
398
472
|
options?: ResolveSegmentOptions,
|
|
399
473
|
routeKey?: string,
|
|
474
|
+
slotNames?: `@${string}`[],
|
|
475
|
+
includeLoaders: boolean = true,
|
|
400
476
|
): Promise<ResolvedSegment[]> {
|
|
401
477
|
invariant(
|
|
402
478
|
parallelEntry.type === "parallel",
|
|
@@ -411,7 +487,12 @@ export async function resolveParallelEntry<TEnv>(
|
|
|
411
487
|
| ReactNode
|
|
412
488
|
>;
|
|
413
489
|
|
|
414
|
-
|
|
490
|
+
const slotsToResolve = slotNames ?? (Object.keys(slots) as `@${string}`[]);
|
|
491
|
+
|
|
492
|
+
for (const slot of slotsToResolve) {
|
|
493
|
+
// Try static lookup first — in production, handler bodies are evicted
|
|
494
|
+
// and replaced with stubs that have no .handler property (undefined).
|
|
495
|
+
// The static store holds the pre-rendered component for these slots.
|
|
415
496
|
let component: ReactNode | undefined = await tryStaticSlot(
|
|
416
497
|
parallelEntry,
|
|
417
498
|
slot,
|
|
@@ -419,6 +500,18 @@ export async function resolveParallelEntry<TEnv>(
|
|
|
419
500
|
);
|
|
420
501
|
|
|
421
502
|
if (component === undefined) {
|
|
503
|
+
const handler = slots[slot];
|
|
504
|
+
if (handler === undefined) {
|
|
505
|
+
continue;
|
|
506
|
+
}
|
|
507
|
+
// Pin `_currentSegmentId` to the slot's own id so handle pushes from
|
|
508
|
+
// inside the slot handler get their own bucket in the HandleStore.
|
|
509
|
+
// Parent-keying would collapse them into the parent layout's bucket;
|
|
510
|
+
// the partial-update merge then replaces the parent's bucket on a
|
|
511
|
+
// slot-only revalidation and drops layout-pushed Meta/Breadcrumbs.
|
|
512
|
+
// filterSegmentOrder() retains slot ids so the client preserves them.
|
|
513
|
+
(context as InternalHandlerContext<any, TEnv>)._currentSegmentId =
|
|
514
|
+
`${parentShortCode}.${slot}`;
|
|
422
515
|
const doneParallelHandler = track(
|
|
423
516
|
`handler:${parallelEntry.id}.${slot}`,
|
|
424
517
|
2,
|
|
@@ -427,7 +520,9 @@ export async function resolveParallelEntry<TEnv>(
|
|
|
427
520
|
parallelEntry.loading !== undefined && parallelEntry.loading !== false;
|
|
428
521
|
if (hasLoadingFallback) {
|
|
429
522
|
const result =
|
|
430
|
-
typeof handler === "function"
|
|
523
|
+
typeof handler === "function"
|
|
524
|
+
? observeHandler(`${parallelEntry.id}.${slot}`, handler, context)
|
|
525
|
+
: handler;
|
|
431
526
|
if (result instanceof Promise) {
|
|
432
527
|
result.finally(doneParallelHandler).catch(() => {});
|
|
433
528
|
const tracked = deps.trackHandler(result, {
|
|
@@ -449,7 +544,13 @@ export async function resolveParallelEntry<TEnv>(
|
|
|
449
544
|
}
|
|
450
545
|
} else {
|
|
451
546
|
component =
|
|
452
|
-
typeof handler === "function"
|
|
547
|
+
typeof handler === "function"
|
|
548
|
+
? await observeHandler(
|
|
549
|
+
`${parallelEntry.id}.${slot}`,
|
|
550
|
+
handler,
|
|
551
|
+
context,
|
|
552
|
+
)
|
|
553
|
+
: handler;
|
|
453
554
|
doneParallelHandler();
|
|
454
555
|
}
|
|
455
556
|
}
|
|
@@ -461,7 +562,10 @@ export async function resolveParallelEntry<TEnv>(
|
|
|
461
562
|
index: 0,
|
|
462
563
|
component,
|
|
463
564
|
loading: parallelEntry.loading === false ? null : parallelEntry.loading,
|
|
464
|
-
transition:
|
|
565
|
+
transition: applyViewTransitionDefault(
|
|
566
|
+
parallelEntry.transition,
|
|
567
|
+
deps.viewTransitionDefault,
|
|
568
|
+
),
|
|
465
569
|
params,
|
|
466
570
|
slot,
|
|
467
571
|
belongsToRoute,
|
|
@@ -472,7 +576,7 @@ export async function resolveParallelEntry<TEnv>(
|
|
|
472
576
|
});
|
|
473
577
|
}
|
|
474
578
|
|
|
475
|
-
if (!
|
|
579
|
+
if (!options?.skipLoaders && includeLoaders) {
|
|
476
580
|
const loaderSegments = await resolveLoaders(
|
|
477
581
|
parallelEntry,
|
|
478
582
|
context,
|
|
@@ -480,6 +584,15 @@ export async function resolveParallelEntry<TEnv>(
|
|
|
480
584
|
deps,
|
|
481
585
|
parentShortCode,
|
|
482
586
|
);
|
|
587
|
+
// Tag parallel-owned loaders so renderSegments can stream them
|
|
588
|
+
// using the parallel's loading() instead of awaiting on the layout
|
|
589
|
+
const parallelLoading =
|
|
590
|
+
parallelEntry.loading === false ? undefined : parallelEntry.loading;
|
|
591
|
+
if (parallelLoading) {
|
|
592
|
+
for (const seg of loaderSegments) {
|
|
593
|
+
seg.parallelLoading = parallelLoading;
|
|
594
|
+
}
|
|
595
|
+
}
|
|
483
596
|
segments.push(...loaderSegments);
|
|
484
597
|
}
|
|
485
598
|
|
|
@@ -515,6 +628,13 @@ export async function resolveAllSegments<TEnv>(
|
|
|
515
628
|
} catch {}
|
|
516
629
|
|
|
517
630
|
for (const entry of entries) {
|
|
631
|
+
// Set ALS flag when entering a cache() boundary so that ctx.get()
|
|
632
|
+
// can guard non-cacheable variable reads. Also guards response-level
|
|
633
|
+
// side effects (headers.set). Persists for all descendant entries.
|
|
634
|
+
if (entry.type === "cache") {
|
|
635
|
+
const store = RangoContext.getStore();
|
|
636
|
+
if (store) store.insideCacheScope = true;
|
|
637
|
+
}
|
|
518
638
|
const doneEntry = track(`segment:${entry.id}`, 1);
|
|
519
639
|
const resolvedSegments = await resolveWithErrorBoundary(
|
|
520
640
|
entry,
|
|
@@ -534,6 +654,7 @@ export async function resolveAllSegments<TEnv>(
|
|
|
534
654
|
deps,
|
|
535
655
|
{ request: safeRequest, url: context.url, routeKey, telemetry },
|
|
536
656
|
context.pathname,
|
|
657
|
+
options?.throwOnError,
|
|
537
658
|
);
|
|
538
659
|
doneEntry();
|
|
539
660
|
// Deduplicate by segment ID. include() scopes can produce entries that
|
|
@@ -559,11 +680,77 @@ export async function resolveLoadersOnly<TEnv>(
|
|
|
559
680
|
deps: SegmentResolutionDeps<TEnv>,
|
|
560
681
|
): Promise<ResolvedSegment[]> {
|
|
561
682
|
const loaderSegments: ResolvedSegment[] = [];
|
|
683
|
+
const seenIds = new Set<string>();
|
|
684
|
+
|
|
685
|
+
async function collectEntryLoaders(
|
|
686
|
+
entry: EntryData,
|
|
687
|
+
belongsToRoute: boolean,
|
|
688
|
+
shortCodeOverride?: string,
|
|
689
|
+
): Promise<void> {
|
|
690
|
+
// Skip if all loaders from this entry have already been resolved
|
|
691
|
+
// via a parent (e.g., cache boundary wrapping a layout with shared loaders).
|
|
692
|
+
const entryLoaders = entry.loader ?? [];
|
|
693
|
+
const sc = shortCodeOverride ?? entry.shortCode;
|
|
694
|
+
const allAlreadySeen =
|
|
695
|
+
entryLoaders.length > 0 &&
|
|
696
|
+
entryLoaders.every((le, i) =>
|
|
697
|
+
seenIds.has(`${sc}D${i}.${le.loader.$$id}`),
|
|
698
|
+
);
|
|
699
|
+
if (!allAlreadySeen) {
|
|
700
|
+
const segments = await resolveLoaders(
|
|
701
|
+
entry,
|
|
702
|
+
context,
|
|
703
|
+
belongsToRoute,
|
|
704
|
+
deps,
|
|
705
|
+
shortCodeOverride,
|
|
706
|
+
);
|
|
707
|
+
for (const seg of segments) {
|
|
708
|
+
if (!seenIds.has(seg.id)) {
|
|
709
|
+
seenIds.add(seg.id);
|
|
710
|
+
loaderSegments.push(seg);
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
const seenParallelEntryIds = new Set<string>();
|
|
716
|
+
for (const parallelEntry of getParallelEntries(entry.parallel)) {
|
|
717
|
+
if (seenParallelEntryIds.has(parallelEntry.id)) continue;
|
|
718
|
+
seenParallelEntryIds.add(parallelEntry.id);
|
|
719
|
+
await collectEntryLoaders(parallelEntry, belongsToRoute, entry.shortCode);
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
const childBelongsToRoute = belongsToRoute || entry.type === "route";
|
|
723
|
+
for (const layoutEntry of entry.layout) {
|
|
724
|
+
await collectEntryLoaders(layoutEntry, childBelongsToRoute);
|
|
725
|
+
// Inherit route loaders for orphan layouts with parallels.
|
|
726
|
+
// Resolve directly — do NOT re-enter collectEntryLoaders with the
|
|
727
|
+
// route entry, as that would re-iterate route.layout and loop.
|
|
728
|
+
if (
|
|
729
|
+
entry.type === "route" &&
|
|
730
|
+
entry.loader &&
|
|
731
|
+
entry.loader.length > 0 &&
|
|
732
|
+
Object.keys(layoutEntry.parallel).length > 0
|
|
733
|
+
) {
|
|
734
|
+
const inherited = await resolveLoaders(
|
|
735
|
+
entry,
|
|
736
|
+
context,
|
|
737
|
+
childBelongsToRoute,
|
|
738
|
+
deps,
|
|
739
|
+
layoutEntry.shortCode,
|
|
740
|
+
);
|
|
741
|
+
for (const seg of inherited) {
|
|
742
|
+
if (!seenIds.has(seg.id)) {
|
|
743
|
+
seenIds.add(seg.id);
|
|
744
|
+
seg._inherited = true;
|
|
745
|
+
loaderSegments.push(seg);
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
}
|
|
562
751
|
|
|
563
752
|
for (const entry of entries) {
|
|
564
|
-
|
|
565
|
-
const segments = await resolveLoaders(entry, context, belongsToRoute, deps);
|
|
566
|
-
loaderSegments.push(...segments);
|
|
753
|
+
await collectEntryLoaders(entry, entry.type === "route");
|
|
567
754
|
}
|
|
568
755
|
|
|
569
756
|
return loaderSegments;
|