@rangojs/router 0.20.0 → 0.22.0
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/dist/bin/rango.js +39 -15
- package/dist/types/browser/event-controller.d.ts +49 -0
- package/dist/types/browser/history-state.d.ts +16 -4
- package/dist/types/browser/navigation-store.d.ts +9 -0
- package/dist/types/browser/navigation-transaction.d.ts +7 -0
- package/dist/types/browser/partial-update.d.ts +2 -1
- package/dist/types/browser/prefetch/default-strategy.d.ts +16 -20
- package/dist/types/browser/rango-state.d.ts +3 -1
- package/dist/types/browser/react/NavigationProvider.d.ts +14 -1
- package/dist/types/browser/react/context.d.ts +25 -0
- package/dist/types/browser/react/location-state-shared.d.ts +84 -10
- package/dist/types/browser/react/location-state.d.ts +7 -2
- package/dist/types/browser/rsc-router.d.ts +13 -2
- package/dist/types/browser/types.d.ts +10 -0
- package/dist/types/build/generate-manifest.d.ts +2 -0
- package/dist/types/cache/background-task.d.ts +22 -0
- package/dist/types/cache/cache-key-utils.d.ts +50 -5
- package/dist/types/cache/cache-policy.d.ts +17 -0
- package/dist/types/cache/cache-scope.d.ts +5 -6
- package/dist/types/cache/cf/cf-cache-store.d.ts +34 -7
- package/dist/types/cache/cf/cf-cache-types.d.ts +12 -4
- package/dist/types/cache/document-cache.d.ts +8 -5
- package/dist/types/cache/handle-snapshot.d.ts +59 -50
- package/dist/types/cache/index.d.ts +3 -1
- package/dist/types/cache/invalidation-order.d.ts +14 -0
- package/dist/types/cache/memory-segment-store.d.ts +33 -1
- package/dist/types/cache/shell-snapshot.d.ts +29 -4
- package/dist/types/cache/store-scope.d.ts +22 -0
- package/dist/types/cache/tag-invalidation.d.ts +7 -6
- package/dist/types/cache/types.d.ts +84 -24
- package/dist/types/cache/vercel/vercel-cache-store.d.ts +35 -17
- package/dist/types/client-urls/navigation.d.ts +7 -1
- package/dist/types/client-urls/optimistic-location.d.ts +10 -4
- package/dist/types/errors.d.ts +6 -0
- package/dist/types/handles/deferred-resolution.d.ts +18 -1
- package/dist/types/internal-suspense-audit.d.ts +1 -0
- package/dist/types/loader-store.d.ts +4 -8
- package/dist/types/outlet-provider.d.ts +2 -1
- package/dist/types/prerender/build-shell-capture.d.ts +8 -6
- package/dist/types/prerender/cloudflare.d.ts +49 -0
- package/dist/types/prerender/create-prerender-trigger.d.ts +121 -0
- package/dist/types/prerender/index.d.ts +12 -0
- package/dist/types/prerender/memory-prerender-store.d.ts +29 -0
- package/dist/types/prerender/on-demand.d.ts +272 -0
- package/dist/types/prerender/producer-guard.d.ts +33 -0
- package/dist/types/prerender/shell-manifest-key.d.ts +1 -18
- package/dist/types/prerender/warm-request.d.ts +65 -0
- package/dist/types/prerender/warm.d.ts +65 -0
- package/dist/types/prerender/writable-store.d.ts +170 -0
- package/dist/types/prerender.d.ts +27 -0
- package/dist/types/reverse.d.ts +1 -1
- package/dist/types/route-content-wrapper.d.ts +3 -1
- package/dist/types/router/error-handling.d.ts +2 -0
- package/dist/types/router/handler-context.d.ts +1 -1
- package/dist/types/router/match-middleware/cache-lookup.d.ts +15 -1
- package/dist/types/router/navigation-snapshot.d.ts +8 -1
- package/dist/types/router/pattern-matching.d.ts +2 -0
- package/dist/types/router/prefetch-default.d.ts +4 -4
- package/dist/types/router/prerender-match.d.ts +23 -1
- package/dist/types/router/route-trie-builder.d.ts +8 -1
- package/dist/types/router/router-context.d.ts +1 -0
- package/dist/types/router/router-interfaces.d.ts +32 -1
- package/dist/types/router/router-options.d.ts +39 -5
- package/dist/types/router/segment-resolution/fresh.d.ts +7 -0
- package/dist/types/router/segment-resolution/loader-cache.d.ts +23 -10
- package/dist/types/router/segment-resolution/revalidation.d.ts +5 -1
- package/dist/types/router/segment-resolution/streamed-handler-boundary.d.ts +43 -0
- package/dist/types/router/trie-matching.d.ts +2 -0
- package/dist/types/router-versions.d.ts +39 -0
- package/dist/types/rsc/handler-context.d.ts +5 -0
- package/dist/types/rsc/shell-build-manifest.d.ts +9 -6
- package/dist/types/rsc/shell-capture-constants.d.ts +20 -5
- package/dist/types/rsc/shell-capture.d.ts +14 -1
- package/dist/types/rsc/shell-serve.d.ts +2 -2
- package/dist/types/rsc/ssr-module-loader.d.ts +15 -0
- package/dist/types/rsc/types.d.ts +12 -6
- package/dist/types/segment-boundary-content.d.ts +17 -0
- package/dist/types/segment-loader-promise.d.ts +2 -6
- package/dist/types/server/build-version-table.d.ts +53 -0
- package/dist/types/server/context.d.ts +12 -4
- package/dist/types/server/handle-store.d.ts +89 -46
- package/dist/types/server/request-context.d.ts +84 -9
- package/dist/types/suspense-audit-react.d.ts +26 -0
- package/dist/types/suspense-audit.d.ts +157 -0
- package/dist/types/testing/action-form.d.ts +34 -0
- package/dist/types/testing/build-versions.d.ts +32 -0
- package/dist/types/testing/dom.entry.d.ts +1 -1
- package/dist/types/testing/flight.entry.d.ts +3 -1
- package/dist/types/testing/index.d.ts +6 -0
- package/dist/types/testing/internal/seed-vars.d.ts +7 -2
- package/dist/types/testing/internal/server-modules.d.ts +8 -0
- package/dist/types/testing/location-state-key.d.ts +5 -1
- package/dist/types/testing/render-route.d.ts +77 -6
- package/dist/types/testing/serve-shell-request.d.ts +55 -4
- package/dist/types/testing/shell-status.d.ts +14 -6
- package/dist/types/testing/vitest-stubs/plugin-rsc.d.ts +9 -6
- package/dist/types/testing/vitest-stubs/version.d.ts +1 -0
- package/dist/types/thenable-status.d.ts +6 -0
- package/dist/types/types/handler-context.d.ts +15 -5
- package/dist/types/types/route-entry.d.ts +5 -0
- package/dist/types/urls/path-helper.d.ts +18 -0
- package/dist/types/vite/discovery/build-versions.d.ts +393 -0
- package/dist/types/vite/discovery/installed-packages.d.ts +26 -0
- package/dist/types/vite/discovery/router-versions-phase.d.ts +52 -0
- package/dist/types/vite/discovery/shell-prerender-phase.d.ts +7 -17
- package/dist/types/vite/discovery/state.d.ts +25 -0
- package/dist/types/vite/encryption-key.d.ts +18 -0
- package/dist/types/vite/inject-client-debug.d.ts +6 -25
- package/dist/types/vite/plugin-types.d.ts +27 -0
- package/dist/types/vite/plugins/{version-injector.d.ts → entry-bootstrap-injector.d.ts} +4 -6
- package/dist/types/vite/plugins/expose-ids/export-analysis.d.ts +1 -0
- package/dist/types/vite/plugins/expose-ids/router-transform.d.ts +14 -0
- package/dist/types/vite/plugins/stable-rsc-output.d.ts +51 -0
- package/dist/types/vite/plugins/version-plugin.d.ts +9 -11
- package/dist/types/vite/plugins/virtual-entries.d.ts +18 -1
- package/dist/types/vite/router-discovery.d.ts +6 -0
- package/dist/types/vite/utils/bundle-analysis.d.ts +6 -2
- package/dist/types/vite/utils/client-chunks.d.ts +2 -0
- package/dist/types/vite/utils/compare-strings.d.ts +7 -0
- package/dist/types/vite/utils/package-resolution.d.ts +8 -0
- package/dist/types/vite/utils/prerender-utils.d.ts +2 -0
- package/dist/vite/index.js +1707 -931
- package/package.json +12 -3
- package/skills/cache-guide/SKILL.md +102 -37
- package/skills/caching/SKILL.md +56 -9
- package/skills/catalog.json +1 -1
- package/skills/client-urls/SKILL.md +16 -0
- package/skills/cloudflare/SKILL.md +133 -1
- package/skills/comparison/references/framework-comparison.md +17 -13
- package/skills/deployment-caching/SKILL.md +33 -12
- package/skills/document-cache/SKILL.md +16 -14
- package/skills/hooks/SKILL.md +25 -0
- package/skills/hooks/handle-and-actions.md +5 -0
- package/skills/hooks/navigation.md +38 -0
- package/skills/hooks/state.md +213 -11
- package/skills/hooks/urls.md +40 -1
- package/skills/host-router/SKILL.md +31 -0
- package/skills/intercept/SKILL.md +6 -0
- package/skills/loader/SKILL.md +25 -5
- package/skills/middleware/SKILL.md +2 -0
- package/skills/migrate-nextjs/SKILL.md +82 -9
- package/skills/ppr/SKILL.md +150 -48
- package/skills/prerender/SKILL.md +651 -10
- package/skills/rango/SKILL.md +9 -7
- package/skills/response-routes/SKILL.md +5 -5
- package/skills/route/SKILL.md +29 -0
- package/skills/router-setup/SKILL.md +15 -1
- package/skills/testing/SKILL.md +32 -27
- package/skills/testing/cache-prerender.md +18 -17
- package/skills/testing/client-components.md +193 -8
- package/skills/testing/handles.md +2 -0
- package/skills/testing/loader.md +1 -1
- package/skills/testing/middleware.md +1 -1
- package/skills/testing/render-handler.md +18 -18
- package/skills/testing/serve-shell-request.md +167 -22
- package/skills/testing/server-actions.md +17 -17
- package/skills/testing/server-tree.md +1 -1
- package/skills/typesafety/generated-files-and-cli.md +1 -1
- package/skills/use-cache/SKILL.md +25 -10
- package/skills/vercel/SKILL.md +5 -3
- package/skills/view-transitions/SKILL.md +9 -4
- package/src/bin/rango.ts +1 -1
- package/src/browser/event-controller.ts +119 -2
- package/src/browser/history-state.ts +39 -17
- package/src/browser/link-interceptor.ts +2 -2
- package/src/browser/navigation-bridge.ts +104 -41
- package/src/browser/navigation-store.ts +26 -0
- package/src/browser/navigation-transaction.ts +24 -12
- package/src/browser/network-error-handler.ts +2 -0
- package/src/browser/partial-update.ts +58 -14
- package/src/browser/prefetch/default-strategy.ts +22 -42
- package/src/browser/rango-state.ts +3 -1
- package/src/browser/react/Link.tsx +37 -43
- package/src/browser/react/NavigationProvider.tsx +85 -1
- package/src/browser/react/context.ts +30 -0
- package/src/browser/react/location-state-shared.ts +200 -34
- package/src/browser/react/location-state.ts +28 -77
- package/src/browser/react/use-action.ts +4 -1
- package/src/browser/react/use-handle.ts +3 -0
- package/src/browser/react/use-link-status.ts +4 -3
- package/src/browser/react/use-navigation.ts +4 -1
- package/src/browser/react/use-params.ts +4 -1
- package/src/browser/react/use-pathname.ts +4 -1
- package/src/browser/react/use-search-params.ts +5 -3
- package/src/browser/react/use-segments.ts +36 -54
- package/src/browser/rsc-router.tsx +93 -31
- package/src/browser/segment-reconciler.ts +3 -3
- package/src/browser/server-action-bridge.ts +43 -7
- package/src/browser/types.ts +11 -0
- package/src/build/generate-manifest.ts +17 -0
- package/src/build/merge-full-manifests.ts +5 -0
- package/src/build/route-trie.ts +1 -0
- package/src/cache/background-task.ts +61 -0
- package/src/cache/cache-key-utils.ts +91 -12
- package/src/cache/cache-policy.ts +11 -2
- package/src/cache/cache-runtime.ts +23 -23
- package/src/cache/cache-scope.ts +48 -36
- package/src/cache/cf/cf-cache-store.ts +91 -66
- package/src/cache/cf/cf-cache-types.ts +12 -4
- package/src/cache/document-cache.ts +35 -14
- package/src/cache/handle-snapshot.ts +118 -86
- package/src/cache/index.ts +5 -0
- package/src/cache/invalidation-order.ts +53 -0
- package/src/cache/memory-segment-store.ts +52 -1
- package/src/cache/shell-snapshot.ts +52 -5
- package/src/cache/store-scope.ts +36 -0
- package/src/cache/tag-invalidation.ts +7 -5
- package/src/cache/types.ts +90 -24
- package/src/cache/vercel/vercel-cache-store.ts +68 -26
- package/src/client-urls/client-root.tsx +1 -0
- package/src/client-urls/navigation.ts +10 -0
- package/src/client-urls/optimistic-location.ts +10 -4
- package/src/client.tsx +40 -3
- package/src/errors.ts +13 -6
- package/src/handles/deferred-resolution.ts +36 -0
- package/src/internal-suspense-audit.ts +12 -0
- package/src/loader-store.ts +4 -9
- package/src/outlet-provider.tsx +25 -1
- package/src/prerender/build-shell-capture.ts +11 -9
- package/src/prerender/cloudflare.ts +130 -0
- package/src/prerender/create-prerender-trigger.ts +780 -0
- package/src/prerender/index.ts +39 -0
- package/src/prerender/memory-prerender-store.ts +115 -0
- package/src/prerender/on-demand.ts +339 -0
- package/src/prerender/producer-guard.ts +66 -0
- package/src/prerender/shell-manifest-key.ts +12 -17
- package/src/prerender/warm-request.ts +154 -0
- package/src/prerender/warm.ts +193 -0
- package/src/prerender/writable-store.ts +278 -0
- package/src/prerender.ts +29 -0
- package/src/reverse.ts +1 -1
- package/src/route-content-wrapper.tsx +107 -13
- package/src/router/error-handling.ts +12 -12
- package/src/router/find-match.ts +1 -0
- package/src/router/handler-context.ts +2 -0
- package/src/router/intercept-resolution.ts +15 -8
- package/src/router/loader-resolution.ts +2 -10
- package/src/router/manifest.ts +2 -0
- package/src/router/match-middleware/background-revalidation.ts +2 -0
- package/src/router/match-middleware/cache-lookup.ts +239 -54
- package/src/router/navigation-snapshot.ts +25 -6
- package/src/router/pattern-matching.ts +14 -0
- package/src/router/prefetch-default.ts +4 -4
- package/src/router/prerender-match.ts +460 -352
- package/src/router/request-classification.ts +15 -12
- package/src/router/route-trie-builder.ts +9 -0
- package/src/router/router-context.ts +1 -0
- package/src/router/router-interfaces.ts +35 -1
- package/src/router/router-options.ts +45 -5
- package/src/router/segment-resolution/fresh.ts +20 -19
- package/src/router/segment-resolution/loader-cache.ts +110 -77
- package/src/router/segment-resolution/revalidation.ts +34 -24
- package/src/router/segment-resolution/streamed-handler-boundary.ts +133 -0
- package/src/router/trie-matching.ts +3 -0
- package/src/router-versions.ts +60 -0
- package/src/router.ts +107 -1
- package/src/rsc/full-payload.ts +6 -2
- package/src/rsc/handler-context.ts +5 -0
- package/src/rsc/handler.ts +64 -7
- package/src/rsc/progressive-enhancement.ts +5 -1
- package/src/rsc/response-cache-serve.ts +10 -4
- package/src/rsc/rsc-rendering.ts +65 -12
- package/src/rsc/shell-build-manifest.ts +17 -12
- package/src/rsc/shell-capture-constants.ts +39 -7
- package/src/rsc/shell-capture.ts +144 -45
- package/src/rsc/shell-serve.ts +12 -6
- package/src/rsc/ssr-module-loader.ts +26 -0
- package/src/rsc/types.ts +12 -6
- package/src/segment-boundary-content.ts +25 -0
- package/src/segment-loader-promise.ts +28 -38
- package/src/segment-system.tsx +117 -57
- package/src/server/build-version-table.ts +130 -0
- package/src/server/context.ts +40 -17
- package/src/server/cookie-store.ts +5 -0
- package/src/server/handle-store.ts +357 -127
- package/src/server/request-context.ts +149 -12
- package/src/ssr/ssr-root.tsx +32 -13
- package/src/suspense-audit-react.tsx +98 -0
- package/src/suspense-audit.ts +1030 -0
- package/src/testing/action-form.ts +66 -0
- package/src/testing/build-versions.ts +64 -0
- package/src/testing/dispatch.ts +6 -0
- package/src/testing/dom.entry.ts +1 -0
- package/src/testing/flight-runtime.d.ts +27 -0
- package/src/testing/flight.entry.ts +4 -0
- package/src/testing/index.ts +22 -0
- package/src/testing/internal/context.ts +2 -1
- package/src/testing/internal/seed-vars.ts +14 -2
- package/src/testing/internal/server-modules.ts +32 -0
- package/src/testing/location-state-key.ts +18 -8
- package/src/testing/render-handler.ts +2 -1
- package/src/testing/render-route.tsx +293 -46
- package/src/testing/serve-shell-request.ts +262 -37
- package/src/testing/shell-status.ts +17 -6
- package/src/testing/vitest-stubs/plugin-rsc.ts +37 -11
- package/src/testing/vitest-stubs/version.ts +4 -1
- package/src/thenable-status.ts +10 -0
- package/src/types/handler-context.ts +15 -5
- package/src/types/route-entry.ts +6 -0
- package/src/urls/path-helper.ts +69 -9
- package/src/use-loader.tsx +57 -30
- package/src/vite/discovery/build-versions.ts +1281 -0
- package/src/vite/discovery/bundle-postprocess.ts +4 -0
- package/src/vite/discovery/discover-routers.ts +15 -0
- package/src/vite/discovery/installed-packages.ts +159 -0
- package/src/vite/discovery/prerender-collection.ts +38 -1
- package/src/vite/discovery/router-versions-phase.ts +336 -0
- package/src/vite/discovery/shell-prerender-phase.ts +33 -43
- package/src/vite/discovery/state.ts +36 -1
- package/src/vite/discovery/virtual-module-codegen.ts +7 -2
- package/src/vite/encryption-key.ts +101 -9
- package/src/vite/inject-client-debug.ts +42 -20
- package/src/vite/plugin-types.ts +28 -0
- package/src/vite/plugins/{version-injector.ts → entry-bootstrap-injector.ts} +7 -22
- package/src/vite/plugins/expose-ids/export-analysis.ts +1 -1
- package/src/vite/plugins/expose-ids/router-transform.ts +36 -15
- package/src/vite/plugins/expose-internal-ids.ts +12 -3
- package/src/vite/plugins/stable-rsc-output.ts +147 -0
- package/src/vite/plugins/version-plugin.ts +19 -24
- package/src/vite/plugins/version.d.ts +17 -2
- package/src/vite/plugins/virtual-entries.ts +39 -6
- package/src/vite/rango.ts +28 -3
- package/src/vite/router-discovery.ts +96 -13
- package/src/vite/utils/bundle-analysis.ts +39 -7
- package/src/vite/utils/client-chunks.ts +5 -1
- package/src/vite/utils/compare-strings.ts +9 -0
- package/src/vite/utils/package-resolution.ts +9 -0
- package/src/vite/utils/prerender-utils.ts +4 -1
- package/src/vite/utils/shared-utils.ts +1 -1
- package/dist/types/segment-content-promise.d.ts +0 -13
- package/src/segment-content-promise.ts +0 -67
package/src/urls/path-helper.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ReactNode } from "react";
|
|
2
|
-
import type { Handler } from "../types.js";
|
|
2
|
+
import type { Handler, InternalHandlerContext } from "../types.js";
|
|
3
3
|
import type { RouteItem, RouteUseItem, UseItems } from "../route-types.js";
|
|
4
4
|
import {
|
|
5
5
|
getUrlPrefix,
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
requireDslContext,
|
|
9
9
|
stampStaticDefScope,
|
|
10
10
|
} from "../server/context";
|
|
11
|
-
import { invariant, DataNotFoundError } from "../errors";
|
|
11
|
+
import { invariant, DataNotFoundError, isDevEnvironment } from "../errors";
|
|
12
12
|
import { validateUserRouteName } from "../route-name.js";
|
|
13
13
|
import {
|
|
14
14
|
isPrerenderHandler,
|
|
@@ -60,6 +60,41 @@ function resolveResponseType(
|
|
|
60
60
|
return options?.[RESPONSE_TYPE];
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
+
const NO_PRERENDER_DATA_MESSAGE = "No prerender data found for this route";
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* A retained on-demand producer must never render on a live production
|
|
67
|
+
* request: bundle eviction is skipped for onDemand routes (the producer body
|
|
68
|
+
* stays available for router.prerender()), so without this gate an
|
|
69
|
+
* overlay+manifest miss on a plain Prerender(..., { onDemand }) route would
|
|
70
|
+
* execute the producer in-request — uncached per-request renders with the
|
|
71
|
+
* personalization guard unarmed. Keep the pre-retention miss contract instead
|
|
72
|
+
* (DataNotFoundError -> 404). Producer runs (build-time bake and
|
|
73
|
+
* router.prerender()) arrive with ctx.build true (createPrerenderContext);
|
|
74
|
+
* Vite dev keeps the documented live fall-through on a dev-store miss, except
|
|
75
|
+
* for a page the overlay marks removed (`_prerenderRemoved`): a live render
|
|
76
|
+
* would show in dev a page production answers with a 404.
|
|
77
|
+
* Pass-through when the route is not onDemand.
|
|
78
|
+
*
|
|
79
|
+
* @internal Exported for the unit test.
|
|
80
|
+
*/
|
|
81
|
+
export function gateOnDemandProducer<TEnv>(
|
|
82
|
+
producer: Handler<any, any, TEnv>,
|
|
83
|
+
isOnDemand: boolean,
|
|
84
|
+
): Handler<any, any, TEnv> {
|
|
85
|
+
if (!isOnDemand) return producer;
|
|
86
|
+
return (ctx) => {
|
|
87
|
+
if (
|
|
88
|
+
!ctx.build &&
|
|
89
|
+
(!isDevEnvironment() ||
|
|
90
|
+
(ctx as InternalHandlerContext<any, TEnv>)._prerenderRemoved)
|
|
91
|
+
) {
|
|
92
|
+
throw new DataNotFoundError(NO_PRERENDER_DATA_MESSAGE);
|
|
93
|
+
}
|
|
94
|
+
return producer(ctx);
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
63
98
|
export function createPathHelper<TEnv>(): PathFn<TEnv> {
|
|
64
99
|
return ((
|
|
65
100
|
pattern: string,
|
|
@@ -117,6 +152,31 @@ export function createPathHelper<TEnv>(): PathFn<TEnv> {
|
|
|
117
152
|
|
|
118
153
|
const namespace = `${ctx.namespace}.${store.getNextIndex("route")}.${routeName}`;
|
|
119
154
|
|
|
155
|
+
// On-demand opt-in lives on the (inner) Prerender def's options for both
|
|
156
|
+
// plain Prerender and Passthrough-wrapped routes. Presence (truthy, not
|
|
157
|
+
// false) marks the route ISR-eligible; the trie od flag + producer retention
|
|
158
|
+
// key off it.
|
|
159
|
+
const onDemandDef = isPassthroughHandler(handler)
|
|
160
|
+
? handler.prerenderDef
|
|
161
|
+
: isPrerenderHandler(handler)
|
|
162
|
+
? handler
|
|
163
|
+
: undefined;
|
|
164
|
+
const onDemandOpt = onDemandDef?.options?.onDemand;
|
|
165
|
+
const isOnDemand = onDemandOpt != null && onDemandOpt !== false;
|
|
166
|
+
|
|
167
|
+
// onDemand routes are excluded from the PPR shell lane (a writable refresh
|
|
168
|
+
// cannot atomically replace the captured shell), so ppr has no effect there.
|
|
169
|
+
// Thrown ahead of the forRoute early return so lazy evaluation of another
|
|
170
|
+
// route cannot skip it.
|
|
171
|
+
if (isOnDemand && options?.ppr !== undefined && options.ppr !== false) {
|
|
172
|
+
throw new Error(
|
|
173
|
+
`[rango] Route "${routeName}" sets both ppr and onDemand: onDemand ` +
|
|
174
|
+
`routes are excluded from the PPR shell lane, so ppr has no ` +
|
|
175
|
+
`effect here and the route serves without the shell fast path. ` +
|
|
176
|
+
`Drop one of the two options.`,
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
|
|
120
180
|
if (ctx.forRoute && routeName !== ctx.forRoute) {
|
|
121
181
|
store.getShortCode("route");
|
|
122
182
|
return { type: "route" } as RouteItem;
|
|
@@ -129,17 +189,16 @@ export function createPathHelper<TEnv>(): PathFn<TEnv> {
|
|
|
129
189
|
? typeof handler.prerenderDef.handler === "function"
|
|
130
190
|
? (handler.prerenderDef.handler as Handler<any, any, TEnv>)
|
|
131
191
|
: () => {
|
|
132
|
-
throw new DataNotFoundError(
|
|
133
|
-
"No prerender data found for this route",
|
|
134
|
-
);
|
|
192
|
+
throw new DataNotFoundError(NO_PRERENDER_DATA_MESSAGE);
|
|
135
193
|
}
|
|
136
194
|
: isPrerenderHandler(handler)
|
|
137
195
|
? typeof handler.handler === "function"
|
|
138
|
-
? (
|
|
196
|
+
? gateOnDemandProducer(
|
|
197
|
+
handler.handler as Handler<any, any, TEnv>,
|
|
198
|
+
isOnDemand,
|
|
199
|
+
)
|
|
139
200
|
: () => {
|
|
140
|
-
throw new DataNotFoundError(
|
|
141
|
-
"No prerender data found for this route",
|
|
142
|
-
);
|
|
201
|
+
throw new DataNotFoundError(NO_PRERENDER_DATA_MESSAGE);
|
|
143
202
|
}
|
|
144
203
|
: isStaticHandler(handler)
|
|
145
204
|
? (handler.handler as Handler<any, any, TEnv>)
|
|
@@ -168,6 +227,7 @@ export function createPathHelper<TEnv>(): PathFn<TEnv> {
|
|
|
168
227
|
prerenderDef: handler as PrerenderHandlerDefinition,
|
|
169
228
|
}
|
|
170
229
|
: {}),
|
|
230
|
+
...(isOnDemand ? { isOnDemand: true as const } : {}),
|
|
171
231
|
...(isStaticHandler(handler)
|
|
172
232
|
? {
|
|
173
233
|
isStaticPrerender: true as const,
|
package/src/use-loader.tsx
CHANGED
|
@@ -14,9 +14,17 @@ import {
|
|
|
14
14
|
type ReactNode,
|
|
15
15
|
} from "react";
|
|
16
16
|
import { OutletContext, type OutletContextValue } from "./outlet-context.js";
|
|
17
|
-
import {
|
|
17
|
+
import {
|
|
18
|
+
EMPTY_LOADER_SNAPSHOT,
|
|
19
|
+
type LoaderEntry,
|
|
20
|
+
type LoaderStore,
|
|
21
|
+
} from "./loader-store.js";
|
|
22
|
+
import { NavigationStoreContext } from "./browser/react/context.js";
|
|
18
23
|
import { decodeLoaderEntry } from "./decode-loader-results.js";
|
|
19
24
|
import { warnAwaitedSsrSuspension } from "./ssr-suspension-warning.js";
|
|
25
|
+
import { SETTLED_THENABLE } from "./thenable-status.js";
|
|
26
|
+
import { useReadAudit } from "./suspense-audit-react.js";
|
|
27
|
+
import { INTERNAL_RANGO_SUSPENSE_AUDIT } from "./internal-suspense-audit.js";
|
|
20
28
|
import type { LoaderDefinition, LoadOptions } from "./types.js";
|
|
21
29
|
|
|
22
30
|
function isShareableGet(options: LoadOptions | undefined): boolean {
|
|
@@ -202,15 +210,18 @@ function useLoaderInternal<T>(
|
|
|
202
210
|
}
|
|
203
211
|
return { contextData: undefined, hasContextData: false };
|
|
204
212
|
}, [context, loader.$$id]);
|
|
205
|
-
|
|
206
213
|
// SPIKE (streaming useLoader): a pending per-loader stream suspends HERE —
|
|
207
|
-
// the implicit-suspense read.
|
|
208
|
-
// conditional call is legal; on replay after resolution it returns
|
|
214
|
+
// the implicit-suspense read. On replay after resolution use() returns
|
|
209
215
|
// synchronously and the decoded value takes the contextData slot. Hooks
|
|
210
216
|
// below never run in a suspended render, so their order is stable across
|
|
211
217
|
// every COMPLETED render.
|
|
212
218
|
const { hasContextData } = walk;
|
|
213
219
|
let contextData = walk.contextData;
|
|
220
|
+
// Dev only (suspense-audit.ts): the stream this reader is handed. The test
|
|
221
|
+
// is a build-time constant, so the hook is called on every render or never.
|
|
222
|
+
if (process.env.NODE_ENV !== "production" && INTERNAL_RANGO_SUSPENSE_AUDIT) {
|
|
223
|
+
useReadAudit(loader.$$id, walk.pendingStream);
|
|
224
|
+
}
|
|
214
225
|
if (walk.pendingStream) {
|
|
215
226
|
warnAwaitedSsrSuspension(
|
|
216
227
|
loader.$$id,
|
|
@@ -218,6 +229,10 @@ function useLoaderInternal<T>(
|
|
|
218
229
|
walk.pendingStream,
|
|
219
230
|
);
|
|
220
231
|
contextData = decodeLoaderEntry(use(walk.pendingStream)) as T;
|
|
232
|
+
} else if (hasContextData) {
|
|
233
|
+
// React logs a render that finishes without use() at a place whose reader
|
|
234
|
+
// suspended in use() on mount, so every read with route context calls it.
|
|
235
|
+
use(SETTLED_THENABLE);
|
|
221
236
|
}
|
|
222
237
|
|
|
223
238
|
const loaderId = loader.$$id;
|
|
@@ -245,10 +260,15 @@ function useLoaderInternal<T>(
|
|
|
245
260
|
const bucketKey =
|
|
246
261
|
effectiveKey === undefined ? loaderId : `${loaderId}::${effectiveKey}`;
|
|
247
262
|
|
|
263
|
+
// The router store's loader data (loader-store.ts). None on the server,
|
|
264
|
+
// where the hook reads the route's loader data only.
|
|
265
|
+
const loaders: LoaderStore | undefined = useContext(NavigationStoreContext)
|
|
266
|
+
?.store?.loaders;
|
|
267
|
+
|
|
248
268
|
const groupRefetch = useCallback(async (): Promise<void> => {
|
|
249
|
-
if (!loaderId) return;
|
|
250
|
-
const requestId =
|
|
251
|
-
|
|
269
|
+
if (!loaderId || !loaders) return;
|
|
270
|
+
const requestId = loaders.reserveRequestId(bucketKey);
|
|
271
|
+
loaders.beginRequest(bucketKey, requestId);
|
|
252
272
|
try {
|
|
253
273
|
const url = new URL(window.location.href);
|
|
254
274
|
url.searchParams.set("_rsc_loader", loaderId);
|
|
@@ -261,15 +281,15 @@ function useLoaderInternal<T>(
|
|
|
261
281
|
if (payload.loaderError) {
|
|
262
282
|
throw new Error(payload.loaderError.message);
|
|
263
283
|
}
|
|
264
|
-
|
|
284
|
+
loaders.finishData(bucketKey, requestId, payload.loaderResult);
|
|
265
285
|
} catch (e) {
|
|
266
286
|
const err = e instanceof Error ? e : new Error(String(e));
|
|
267
|
-
|
|
287
|
+
loaders.finishError(bucketKey, requestId, err);
|
|
268
288
|
throw err;
|
|
269
289
|
} finally {
|
|
270
|
-
|
|
290
|
+
loaders.setLoading(bucketKey, requestId, false);
|
|
271
291
|
}
|
|
272
|
-
}, [loaderId, bucketKey]);
|
|
292
|
+
}, [loaderId, bucketKey, loaders]);
|
|
273
293
|
|
|
274
294
|
// Held-navigation pin: a transition commit whose stream for this loader is
|
|
275
295
|
// still pending announces it (loader-store.ts announcePendingStreams, which
|
|
@@ -283,14 +303,17 @@ function useLoaderInternal<T>(
|
|
|
283
303
|
snapshot: LoaderEntry;
|
|
284
304
|
}>(() => ({
|
|
285
305
|
bucketKey,
|
|
286
|
-
snapshot:
|
|
306
|
+
snapshot: loaders?.getSnapshot(bucketKey) ?? EMPTY_LOADER_SNAPSHOT,
|
|
287
307
|
}));
|
|
308
|
+
// A new bucket (its key changed) reads nothing from the store during
|
|
309
|
+
// render: it renders empty until the effect below delivers its snapshot.
|
|
288
310
|
const sharedSnapshot =
|
|
289
311
|
sharedState.bucketKey === bucketKey
|
|
290
312
|
? sharedState.snapshot
|
|
291
|
-
:
|
|
313
|
+
: EMPTY_LOADER_SNAPSHOT;
|
|
292
314
|
useEffect(() => {
|
|
293
|
-
|
|
315
|
+
if (!loaders) return;
|
|
316
|
+
const initial = loaders.getSnapshot(bucketKey);
|
|
294
317
|
if (initial !== sharedSnapshot) {
|
|
295
318
|
startTransition(() => {
|
|
296
319
|
setSharedState({ bucketKey, snapshot: initial });
|
|
@@ -299,10 +322,10 @@ function useLoaderInternal<T>(
|
|
|
299
322
|
// ephemeral: a reader with no route context has no route-context reset
|
|
300
323
|
// trigger, so its keyed bucket is reference-counted by the store. A
|
|
301
324
|
// route-registered reader makes the bucket sticky (reset via clearFamily).
|
|
302
|
-
return
|
|
325
|
+
return loaders.subscribe(
|
|
303
326
|
bucketKey,
|
|
304
327
|
() => {
|
|
305
|
-
const next =
|
|
328
|
+
const next = loaders.getSnapshot(bucketKey);
|
|
306
329
|
startTransition(() => {
|
|
307
330
|
setSharedState({ bucketKey, snapshot: next });
|
|
308
331
|
});
|
|
@@ -323,7 +346,7 @@ function useLoaderInternal<T>(
|
|
|
323
346
|
// groupKey, and groupRefetch are the only inputs that require a fresh
|
|
324
347
|
// subscription (groupList is memoized on groupKey; groupRefetch is stable
|
|
325
348
|
// per bucketKey).
|
|
326
|
-
}, [bucketKey, hasContextData, groupKey, groupRefetch]);
|
|
349
|
+
}, [bucketKey, hasContextData, groupKey, groupRefetch, loaders]);
|
|
327
350
|
|
|
328
351
|
// Local state holds the result of:
|
|
329
352
|
// - parameterized / mutation `load()` calls (load({ params }), POST,
|
|
@@ -369,10 +392,10 @@ function useLoaderInternal<T>(
|
|
|
369
392
|
lastSharedRequestIdRef.current = null;
|
|
370
393
|
// Reset every sticky bucket of this loader (keyed or not). Ephemeral
|
|
371
394
|
// (unregistered keyed) buckets are left to their refcount lifecycle.
|
|
372
|
-
|
|
395
|
+
loaders?.clearFamily(loaderId);
|
|
373
396
|
prevContextDataRef.current = contextData;
|
|
374
397
|
}
|
|
375
|
-
}, [contextData, loaderId]);
|
|
398
|
+
}, [contextData, loaderId, loaders]);
|
|
376
399
|
|
|
377
400
|
// Read priority: a committed parameterized load() result overrides the shared
|
|
378
401
|
// snapshot; a committed shared snapshot overrides the server-seeded context.
|
|
@@ -408,15 +431,17 @@ function useLoaderInternal<T>(
|
|
|
408
431
|
const bucket = bucketKeyRef.current;
|
|
409
432
|
const hasDedicatedBucket = bucket !== id;
|
|
410
433
|
|
|
411
|
-
const shared =
|
|
412
|
-
|
|
413
|
-
|
|
434
|
+
const shared =
|
|
435
|
+
loaders !== undefined &&
|
|
436
|
+
(hasDedicatedBucket
|
|
437
|
+
? isShareableGet(loadOptions)
|
|
438
|
+
: isPlainRefetch(loadOptions) && hasContextDataRef.current);
|
|
414
439
|
let sharedRequestId = -1;
|
|
415
440
|
let localRequestId = -1;
|
|
416
441
|
if (shared) {
|
|
417
|
-
sharedRequestId =
|
|
442
|
+
sharedRequestId = loaders.reserveRequestId(bucket);
|
|
418
443
|
lastSharedRequestIdRef.current = sharedRequestId;
|
|
419
|
-
|
|
444
|
+
loaders.beginRequest(bucket, sharedRequestId);
|
|
420
445
|
} else {
|
|
421
446
|
localRequestId = ++localRequestIdRef.current;
|
|
422
447
|
setLocalIsLoading(true);
|
|
@@ -503,7 +528,7 @@ function useLoaderInternal<T>(
|
|
|
503
528
|
|
|
504
529
|
const result = payload.loaderResult;
|
|
505
530
|
if (shared) {
|
|
506
|
-
|
|
531
|
+
loaders.finishData(bucket, sharedRequestId, result);
|
|
507
532
|
} else if (localRequestId === localRequestIdRef.current) {
|
|
508
533
|
startTransition(() => {
|
|
509
534
|
setLocalFetchedData({ has: true, value: result });
|
|
@@ -514,7 +539,7 @@ function useLoaderInternal<T>(
|
|
|
514
539
|
} catch (e) {
|
|
515
540
|
const err = e instanceof Error ? e : new Error(String(e));
|
|
516
541
|
if (shared) {
|
|
517
|
-
|
|
542
|
+
loaders.finishError(bucket, sharedRequestId, err);
|
|
518
543
|
} else if (localRequestId === localRequestIdRef.current) {
|
|
519
544
|
setLocalError(err);
|
|
520
545
|
setLocalIsLoading(false);
|
|
@@ -525,11 +550,11 @@ function useLoaderInternal<T>(
|
|
|
525
550
|
return dataRef.current as T;
|
|
526
551
|
} finally {
|
|
527
552
|
if (shared) {
|
|
528
|
-
|
|
553
|
+
loaders.setLoading(bucket, sharedRequestId, false);
|
|
529
554
|
}
|
|
530
555
|
}
|
|
531
556
|
},
|
|
532
|
-
[throwOnError],
|
|
557
|
+
[throwOnError, loaders],
|
|
533
558
|
);
|
|
534
559
|
|
|
535
560
|
if (throwOnError) {
|
|
@@ -708,8 +733,10 @@ export function useFetchLoader<T>(
|
|
|
708
733
|
export function useRefreshLoaders(): (
|
|
709
734
|
groups: string | string[],
|
|
710
735
|
) => Promise<void> {
|
|
736
|
+
const loaders = useContext(NavigationStoreContext)?.store?.loaders;
|
|
711
737
|
return useCallback(
|
|
712
|
-
(groups: string | string[]) =>
|
|
713
|
-
|
|
738
|
+
(groups: string | string[]) =>
|
|
739
|
+
loaders ? loaders.refreshGroups(groups) : Promise.resolve(),
|
|
740
|
+
[loaders],
|
|
714
741
|
);
|
|
715
742
|
}
|