@rangojs/router 0.16.0 → 0.17.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/README.md +254 -89
- package/dist/testing/vitest.js +252 -0
- package/dist/types/browser/navigation-transaction.d.ts +6 -0
- package/dist/types/browser/partial-update.d.ts +11 -0
- package/dist/types/browser/react/ScrollRestoration.d.ts +6 -6
- package/dist/types/browser/rsc-router.d.ts +1 -1
- package/dist/types/browser/scroll-restoration.d.ts +7 -0
- package/dist/types/cache/cache-exec-scope.d.ts +18 -1
- package/dist/types/cache/cache-scope.d.ts +34 -5
- package/dist/types/cache/handle-capture.d.ts +41 -3
- package/dist/types/cache/handle-snapshot.d.ts +53 -13
- package/dist/types/cache/segment-codec.d.ts +10 -3
- package/dist/types/cache/taint.d.ts +1 -1
- package/dist/types/cache/types.d.ts +15 -3
- package/dist/types/client.d.ts +4 -4
- package/dist/types/client.rsc.d.ts +4 -5
- package/dist/types/components/DefaultDocument.d.ts +5 -2
- package/dist/types/context-var.d.ts +6 -5
- package/dist/types/escape-script.d.ts +1 -1
- package/dist/types/handles/MetaTags.d.ts +5 -4
- package/dist/types/handles/Scripts.d.ts +8 -7
- package/dist/types/handles/meta.d.ts +2 -2
- package/dist/types/handles/script.d.ts +17 -16
- package/dist/types/href-client.d.ts +1 -1
- package/dist/types/html.d.ts +12 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.rsc.d.ts +3 -2
- package/dist/types/loader-store.d.ts +7 -4
- package/dist/types/reverse.d.ts +36 -4
- package/dist/types/route-definition/dsl-helpers.d.ts +3 -3
- package/dist/types/route-definition/helpers-types.d.ts +33 -9
- package/dist/types/route-definition/resolve-handler-use.d.ts +23 -1
- package/dist/types/route-types.d.ts +1 -1
- package/dist/types/router/debug-manifest.d.ts +3 -2
- package/dist/types/router/error-handling.d.ts +20 -0
- package/dist/types/router/handler-context.d.ts +6 -0
- package/dist/types/router/match-middleware/background-revalidation.d.ts +46 -5
- package/dist/types/router/middleware-types.d.ts +3 -2
- package/dist/types/router/router-context.d.ts +3 -1
- package/dist/types/router/router-interfaces.d.ts +24 -2
- package/dist/types/router/router-options.d.ts +8 -5
- package/dist/types/router/segment-resolution/fresh.d.ts +10 -0
- package/dist/types/router/segment-resolution/loader-cache.d.ts +37 -1
- package/dist/types/router/segment-resolution/loader-mask.d.ts +9 -19
- package/dist/types/router/segment-resolution/loader-snapshot.d.ts +3 -3
- package/dist/types/router/segment-resolution/mask-nested.d.ts +7 -7
- package/dist/types/router/segment-resolution/revalidation.d.ts +6 -2
- package/dist/types/router/segment-wrappers.d.ts +1 -1
- package/dist/types/rsc/response-route-handler.d.ts +1 -1
- package/dist/types/rsc/rsc-rendering.d.ts +1 -2
- package/dist/types/rsc/shell-capture-constants.d.ts +8 -0
- package/dist/types/rsc/shell-capture.d.ts +5 -4
- package/dist/types/rsc/ssr-setup.d.ts +6 -6
- package/dist/types/rsc/types.d.ts +10 -0
- package/dist/types/server/context.d.ts +33 -3
- package/dist/types/server/handle-store.d.ts +26 -2
- package/dist/types/server/request-context.d.ts +24 -21
- package/dist/types/ssr/index.d.ts +15 -0
- package/dist/types/static-handler.d.ts +4 -1
- package/dist/types/testing/generated-routes.d.ts +2 -2
- package/dist/types/testing/internal/context.d.ts +1 -1
- package/dist/types/testing/render-route.d.ts +32 -6
- package/dist/types/testing/vitest-stubs/plugin-rsc.d.ts +28 -5
- package/dist/types/testing/vitest.d.ts +35 -1
- package/dist/types/theme/ThemeScript.d.ts +3 -3
- package/dist/types/types/cache-types.d.ts +14 -10
- package/dist/types/types/handler-context.d.ts +14 -0
- package/dist/types/types/loader-types.d.ts +8 -6
- package/dist/types/urls/response-types.d.ts +3 -12
- package/dist/types/use-loader.d.ts +12 -3
- package/dist/types/vite/plugins/use-cache-transform.d.ts +33 -2
- package/dist/types/vite/rango.d.ts +1 -1
- package/dist/vite/index.js +1381 -1346
- package/package.json +2 -2
- package/skills/api-client/SKILL.md +22 -10
- package/skills/breadcrumbs/SKILL.md +65 -22
- package/skills/bundle-analysis/SKILL.md +22 -22
- package/skills/cache-guide/SKILL.md +207 -97
- package/skills/caching/SKILL.md +295 -93
- package/skills/catalog.json +16 -16
- package/skills/client-urls/SKILL.md +53 -31
- package/skills/cloudflare/SKILL.md +72 -20
- package/skills/cloudflare/references/d1-and-local-dev.md +15 -2
- package/skills/cloudflare/references/streaming-and-deploy.md +10 -3
- package/skills/cloudflare/references/webhooks-and-crypto.md +9 -2
- package/skills/comparison/SKILL.md +5 -0
- package/skills/comparison/references/framework-comparison.md +39 -33
- package/skills/composability/SKILL.md +38 -29
- package/skills/css/SKILL.md +10 -13
- package/skills/debug-manifest/SKILL.md +74 -31
- package/skills/defer-hydration/SKILL.md +2 -1
- package/skills/deployment-caching/SKILL.md +6 -1
- package/skills/document-cache/SKILL.md +92 -28
- package/skills/fonts/SKILL.md +16 -8
- package/skills/handler-use/SKILL.md +53 -17
- package/skills/hooks/SKILL.md +28 -20
- package/skills/hooks/data.md +23 -3
- package/skills/hooks/handle-and-actions.md +31 -16
- package/skills/hooks/navigation.md +39 -10
- package/skills/hooks/outlets.md +13 -6
- package/skills/hooks/state.md +37 -15
- package/skills/hooks/urls.md +7 -5
- package/skills/host-router/SKILL.md +64 -12
- package/skills/i18n/SKILL.md +60 -34
- package/skills/intercept/SKILL.md +161 -26
- package/skills/layout/SKILL.md +106 -50
- package/skills/links/SKILL.md +51 -19
- package/skills/loader/SKILL.md +242 -106
- package/skills/middleware/SKILL.md +119 -23
- package/skills/migrate-nextjs/SKILL.md +100 -45
- package/skills/migrate-nextjs/backend-host-swap.md +5 -2
- package/skills/migrate-react-router/SKILL.md +49 -38
- package/skills/migrate-react-router/component-migration.md +25 -6
- package/skills/migrate-react-router/data-and-actions.md +18 -5
- package/skills/migrate-react-router/route-mapping.md +12 -6
- package/skills/mime-routes/SKILL.md +12 -7
- package/skills/observability/SKILL.md +28 -14
- package/skills/parallel/SKILL.md +75 -39
- package/skills/ppr/SKILL.md +345 -150
- package/skills/prerender/SKILL.md +189 -100
- package/skills/rango/SKILL.md +65 -34
- package/skills/react-compiler/SKILL.md +6 -5
- package/skills/response-routes/SKILL.md +133 -25
- package/skills/route/SKILL.md +165 -60
- package/skills/router-setup/SKILL.md +418 -240
- package/skills/scripts/SKILL.md +31 -20
- package/skills/server-actions/SKILL.md +220 -59
- package/skills/shell-manifest/SKILL.md +12 -7
- package/skills/streams-and-websockets/SKILL.md +32 -14
- package/skills/tailwind/SKILL.md +37 -33
- package/skills/testing/SKILL.md +68 -32
- package/skills/testing/cache-prerender.md +55 -29
- package/skills/testing/client-components.md +59 -6
- package/skills/testing/e2e-parity.md +12 -2
- package/skills/testing/flight.md +19 -11
- package/skills/testing/handles.md +8 -5
- package/skills/testing/loader.md +8 -5
- package/skills/testing/middleware.md +8 -3
- package/skills/testing/predicates.md +120 -0
- package/skills/testing/render-handler.md +70 -22
- package/skills/testing/response-routes.md +11 -4
- package/skills/testing/reverse-and-types.md +20 -14
- package/skills/testing/server-actions.md +12 -3
- package/skills/testing/server-tree.md +9 -0
- package/skills/testing/setup.md +9 -5
- package/skills/theme/SKILL.md +106 -29
- package/skills/typesafety/SKILL.md +20 -9
- package/skills/typesafety/env-and-bindings.md +126 -47
- package/skills/typesafety/generated-files-and-cli.md +26 -14
- package/skills/typesafety/params-and-search.md +44 -19
- package/skills/typesafety/route-types.md +50 -14
- package/skills/use-cache/SKILL.md +200 -82
- package/skills/vercel/SKILL.md +21 -11
- package/skills/view-transitions/SKILL.md +41 -20
- package/src/__augment-tests__/augmented.check.ts +26 -1
- package/src/browser/index.ts +2 -2
- package/src/browser/navigation-bridge.ts +3 -4
- package/src/browser/navigation-transaction.ts +22 -2
- package/src/browser/partial-update.ts +35 -25
- package/src/browser/react/ScrollRestoration.tsx +6 -6
- package/src/browser/rsc-router.tsx +1 -1
- package/src/browser/scroll-restoration.ts +53 -6
- package/src/cache/cache-exec-scope.ts +37 -4
- package/src/cache/cache-key-utils.ts +1 -6
- package/src/cache/cache-runtime.ts +230 -61
- package/src/cache/cache-scope.ts +76 -12
- package/src/cache/document-cache.ts +15 -0
- package/src/cache/handle-capture.ts +90 -10
- package/src/cache/handle-snapshot.ts +132 -28
- package/src/cache/segment-codec.ts +26 -7
- package/src/cache/tag-invalidation.ts +4 -3
- package/src/cache/taint.ts +1 -1
- package/src/cache/types.ts +16 -3
- package/src/client.rsc.tsx +10 -9
- package/src/client.tsx +5 -4
- package/src/components/DefaultDocument.tsx +7 -2
- package/src/context-var.ts +6 -5
- package/src/escape-script.ts +1 -1
- package/src/handles/MetaTags.tsx +13 -11
- package/src/handles/Scripts.tsx +11 -10
- package/src/handles/meta.ts +2 -2
- package/src/handles/script.ts +17 -16
- package/src/href-client.ts +1 -1
- package/src/html.ts +12 -0
- package/src/index.rsc.ts +5 -0
- package/src/index.ts +1 -0
- package/src/loader-store.ts +7 -4
- package/src/prerender/build-shell-capture.ts +8 -11
- package/src/reverse.ts +44 -4
- package/src/route-definition/dsl-helpers.ts +117 -30
- package/src/route-definition/helpers-types.ts +33 -9
- package/src/route-definition/resolve-handler-use.ts +121 -6
- package/src/route-types.ts +0 -3
- package/src/router/debug-manifest.ts +10 -2
- package/src/router/error-handling.ts +51 -15
- package/src/router/handler-context.ts +34 -25
- package/src/router/intercept-resolution.ts +125 -39
- package/src/router/loader-resolution.ts +56 -11
- package/src/router/match-api.ts +12 -17
- package/src/router/match-middleware/background-revalidation.ts +161 -96
- package/src/router/match-middleware/cache-lookup.ts +93 -10
- package/src/router/match-middleware/cache-store.ts +17 -90
- package/src/router/middleware-types.ts +3 -5
- package/src/router/middleware.ts +52 -9
- package/src/router/prerender-match.ts +53 -19
- package/src/router/route-snapshot.ts +11 -1
- package/src/router/router-context.ts +2 -0
- package/src/router/router-interfaces.ts +25 -2
- package/src/router/router-options.ts +8 -5
- package/src/router/segment-resolution/fresh.ts +44 -12
- package/src/router/segment-resolution/loader-cache.ts +205 -45
- package/src/router/segment-resolution/loader-mask.ts +12 -22
- package/src/router/segment-resolution/loader-snapshot.ts +6 -6
- package/src/router/segment-resolution/mask-nested.ts +7 -7
- package/src/router/segment-resolution/revalidation.ts +34 -6
- package/src/router/segment-wrappers.ts +4 -0
- package/src/rsc/render-pipeline.ts +3 -0
- package/src/rsc/response-route-handler.ts +11 -3
- package/src/rsc/rsc-rendering.ts +8 -45
- package/src/rsc/shell-capture-constants.ts +13 -0
- package/src/rsc/shell-capture.ts +111 -90
- package/src/rsc/ssr-setup.ts +14 -8
- package/src/rsc/types.ts +11 -0
- package/src/server/context.ts +88 -11
- package/src/server/cookie-store.ts +5 -4
- package/src/server/handle-store.ts +232 -7
- package/src/server/request-context.ts +35 -30
- package/src/ssr/index.tsx +40 -7
- package/src/static-handler.ts +4 -1
- package/src/testing/flight-runtime.d.ts +21 -0
- package/src/testing/generated-routes.ts +2 -2
- package/src/testing/internal/context.ts +1 -1
- package/src/testing/internal/flight-client-globals.ts +7 -5
- package/src/testing/render-route.tsx +85 -24
- package/src/testing/run-loader.ts +5 -1
- package/src/testing/vitest-stubs/plugin-rsc.ts +123 -20
- package/src/testing/vitest.ts +62 -1
- package/src/theme/ThemeScript.tsx +3 -3
- package/src/types/cache-types.ts +14 -10
- package/src/types/handler-context.ts +14 -0
- package/src/types/loader-types.ts +8 -6
- package/src/urls/response-types.ts +3 -23
- package/src/use-loader.tsx +12 -3
- package/src/vite/plugins/use-cache-transform.ts +118 -74
- package/src/vite/rango.ts +7 -3
|
@@ -8,14 +8,14 @@
|
|
|
8
8
|
*
|
|
9
9
|
* Must be placed in the <head> element of your document, before any stylesheets.
|
|
10
10
|
*
|
|
11
|
-
* Note: when theme is enabled in the router config, `<
|
|
11
|
+
* Note: when theme is enabled in the router config, `<Html.Meta />` ALREADY
|
|
12
12
|
* renders this FOUC script. Use `<ThemeScript />` only if you do NOT render
|
|
13
|
-
* `<
|
|
13
|
+
* `<Html.Meta />`. Rendering both is safe — the inline script guards the
|
|
14
14
|
* matchMedia listener registration against double-running — but it is redundant.
|
|
15
15
|
*
|
|
16
16
|
* @example
|
|
17
17
|
* ```tsx
|
|
18
|
-
* // In your document component. Use ThemeScript only when you do not render
|
|
18
|
+
* // In your document component. Use ThemeScript only when you do not render Html.Meta.
|
|
19
19
|
* import { ThemeScript } from "@rangojs/router/theme";
|
|
20
20
|
*
|
|
21
21
|
* export function Document({ children }) {
|
package/src/types/cache-types.ts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Context passed to cache condition/key/tags functions
|
|
2
|
+
* Context passed to cache condition/key/tags functions (the full
|
|
3
|
+
* RequestContext).
|
|
3
4
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
5
|
+
* Ordering: global middleware wraps the whole request, and on RSC routes route
|
|
6
|
+
* middleware wraps the match/render pipeline (rsc/handler.ts executeRender), so
|
|
7
|
+
* variables they set before `next()` are readable via `ctx.get()`. On response
|
|
8
|
+
* routes the cache lookup runs BEFORE route middleware, which wraps only the
|
|
9
|
+
* handler run on a miss (rsc/response-route-handler.ts); only global-middleware
|
|
10
|
+
* variables are populated there.
|
|
10
11
|
*/
|
|
11
12
|
export type { RequestContext as CacheContext } from "../server/request-context.js";
|
|
12
13
|
|
|
@@ -109,7 +110,8 @@ export interface CacheOptions<TEnv = unknown> {
|
|
|
109
110
|
* Return false to skip cache for this request (always fetch fresh).
|
|
110
111
|
*
|
|
111
112
|
* Has access to full RequestContext including env, request, params, cookies, etc.
|
|
112
|
-
*
|
|
113
|
+
* Runs after global and route middleware on RSC routes; on response routes,
|
|
114
|
+
* before route middleware (see CacheContext).
|
|
113
115
|
*
|
|
114
116
|
* @example
|
|
115
117
|
* ```typescript
|
|
@@ -131,7 +133,8 @@ export interface CacheOptions<TEnv = unknown> {
|
|
|
131
133
|
* Bypasses default key generation AND store's keyGenerator.
|
|
132
134
|
*
|
|
133
135
|
* Has access to full RequestContext including env, request, params, cookies, etc.
|
|
134
|
-
*
|
|
136
|
+
* Runs after global and route middleware on RSC routes; on response routes,
|
|
137
|
+
* before route middleware (see CacheContext).
|
|
135
138
|
*
|
|
136
139
|
* @example
|
|
137
140
|
* ```typescript
|
|
@@ -153,7 +156,8 @@ export interface CacheOptions<TEnv = unknown> {
|
|
|
153
156
|
* Tags for cache invalidation.
|
|
154
157
|
* Can be a static array or a function that returns tags.
|
|
155
158
|
*
|
|
156
|
-
* The built-in `MemorySegmentCacheStore
|
|
159
|
+
* The built-in `MemorySegmentCacheStore`, `CFCacheStore`, and
|
|
160
|
+
* `VercelCacheStore` index by tag.
|
|
157
161
|
* Invalidate on demand with `updateTag(...tags)` (awaitable, read-your-own-writes;
|
|
158
162
|
* for server actions) or `revalidateTag(...tags)` (background hard-purge, not
|
|
159
163
|
* awaited; for route handlers / webhooks). For `CFCacheStore`, distributed
|
|
@@ -455,6 +455,20 @@ export type InternalHandlerContext<
|
|
|
455
455
|
* table (so a loader cannot await its own in-flight memoized promise).
|
|
456
456
|
*/
|
|
457
457
|
_loaderCacheOriginalUse?: (item: any) => any;
|
|
458
|
+
/**
|
|
459
|
+
* @internal A loader-cache HIT asks before replaying a loader's recorded
|
|
460
|
+
* pushes: true when that loader has not run in this request and no other
|
|
461
|
+
* replay delivered it; its later run then replaces the replayed values
|
|
462
|
+
* (HandleStore.pushReplayed). One copy per loader per request. See
|
|
463
|
+
* setupLoaderAccess.
|
|
464
|
+
*/
|
|
465
|
+
_claimLoaderPushes?: (loaderId: string) => boolean;
|
|
466
|
+
/**
|
|
467
|
+
* @internal Run a loader on a fresh executor (own memo map): a loader-cache
|
|
468
|
+
* stale refresh, whose diverted pushes must not take a dependency's run
|
|
469
|
+
* from the page. See setupLoaderAccess.
|
|
470
|
+
*/
|
|
471
|
+
_runLoaderIsolated?: (loader: any) => Promise<any>;
|
|
458
472
|
};
|
|
459
473
|
|
|
460
474
|
/**
|
|
@@ -28,9 +28,10 @@ import type { RequestScope } from "./request-scope.js";
|
|
|
28
28
|
* return await db.cart.get(user.id);
|
|
29
29
|
* });
|
|
30
30
|
*
|
|
31
|
-
* //
|
|
32
|
-
*
|
|
33
|
-
*
|
|
31
|
+
* // createLoader's one type parameter is the data type; params are
|
|
32
|
+
* // Record<string, string | undefined>:
|
|
33
|
+
* const ProductLoader = createLoader<Product>(async (ctx) => {
|
|
34
|
+
* const { slug } = ctx.params; // string | undefined
|
|
34
35
|
* return await db.products.findBySlug(slug);
|
|
35
36
|
* });
|
|
36
37
|
* ```
|
|
@@ -243,9 +244,10 @@ export type LoadOptions =
|
|
|
243
244
|
* return await db.cart.get(ctx.get("user").id);
|
|
244
245
|
* });
|
|
245
246
|
*
|
|
246
|
-
* //
|
|
247
|
-
*
|
|
248
|
-
*
|
|
247
|
+
* // createLoader's one type parameter is the data type; params are
|
|
248
|
+
* // Record<string, string | undefined>:
|
|
249
|
+
* export const ProductLoader = createLoader<Product>(async (ctx) => {
|
|
250
|
+
* const { slug } = ctx.params; // string | undefined
|
|
249
251
|
* return await db.products.findBySlug(slug);
|
|
250
252
|
* });
|
|
251
253
|
*
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ContextVar } from "../context-var.js";
|
|
2
|
-
import type {
|
|
2
|
+
import type { GlobalReverseFunction } from "../reverse.js";
|
|
3
3
|
import type {
|
|
4
4
|
DefaultReverseRouteMap,
|
|
5
5
|
DefaultVars,
|
|
@@ -7,27 +7,6 @@ import type {
|
|
|
7
7
|
import type { UseItems, ResponseRouteUseItem } from "../route-types.js";
|
|
8
8
|
import type { RequestScope } from "../types/request-scope.js";
|
|
9
9
|
|
|
10
|
-
/**
|
|
11
|
-
* Reverse function for response handler contexts.
|
|
12
|
-
* Global names get autocomplete and param validation from the generated route map.
|
|
13
|
-
* Local `.name` calls are accepted but not validated (scope unknown at type level).
|
|
14
|
-
*/
|
|
15
|
-
type ResponseReverseFunction = [DefaultReverseRouteMap] extends [
|
|
16
|
-
Record<string, string>,
|
|
17
|
-
]
|
|
18
|
-
? (
|
|
19
|
-
name: string,
|
|
20
|
-
params?: Record<string, string>,
|
|
21
|
-
search?: Record<string, unknown>,
|
|
22
|
-
) => string
|
|
23
|
-
: ReverseFunction<DefaultReverseRouteMap> & {
|
|
24
|
-
(
|
|
25
|
-
name: `.${string}`,
|
|
26
|
-
params?: Record<string, string>,
|
|
27
|
-
search?: Record<string, unknown>,
|
|
28
|
-
): string;
|
|
29
|
-
};
|
|
30
|
-
|
|
31
10
|
/**
|
|
32
11
|
* Symbol marking a route as a response route (non-RSC).
|
|
33
12
|
* Stored on PathOptions and UrlPatterns to signal the trie to short-circuit.
|
|
@@ -99,7 +78,8 @@ export interface ResponseHandlerContext<
|
|
|
99
78
|
params: TParams;
|
|
100
79
|
/** @internal Phantom property for params type invariance. Prevents mounting handlers on wrong routes. */
|
|
101
80
|
readonly _paramCheck?: (params: TParams) => TParams;
|
|
102
|
-
|
|
81
|
+
/** Global route names only; see {@link GlobalReverseFunction}. */
|
|
82
|
+
reverse: GlobalReverseFunction<DefaultReverseRouteMap>;
|
|
103
83
|
/** Read a variable set by middleware via ctx.set(key, value) or ctx.set(ContextVar, value). */
|
|
104
84
|
get: {
|
|
105
85
|
<T>(contextVar: ContextVar<T>): T | undefined;
|
package/src/use-loader.tsx
CHANGED
|
@@ -117,6 +117,14 @@ export type LoadFunction<T> = (options?: LoadOptions) => Promise<T>;
|
|
|
117
117
|
|
|
118
118
|
export interface UseLoaderResult<T> {
|
|
119
119
|
data: T;
|
|
120
|
+
/**
|
|
121
|
+
* True while this loader family (`loader.$$id`) is in flight: a `load()` /
|
|
122
|
+
* group refresh, or a held navigation still streaming a re-run of this
|
|
123
|
+
* loader. Family-keyed, not segment-keyed: a layout `useLoader` of the same
|
|
124
|
+
* `createLoader` the child route is re-running also reports true, even if
|
|
125
|
+
* the layout copy is not replaced. Not used for the initial streamed read
|
|
126
|
+
* (that suspends).
|
|
127
|
+
*/
|
|
120
128
|
isLoading: boolean;
|
|
121
129
|
error: Error | null;
|
|
122
130
|
load: LoadFunction<T>;
|
|
@@ -125,6 +133,7 @@ export interface UseLoaderResult<T> {
|
|
|
125
133
|
|
|
126
134
|
export interface UseFetchLoaderResult<T> {
|
|
127
135
|
data: T | undefined;
|
|
136
|
+
/** Same family-keyed contract as {@link UseLoaderResult.isLoading}. */
|
|
128
137
|
isLoading: boolean;
|
|
129
138
|
error: Error | null;
|
|
130
139
|
load: LoadFunction<T>;
|
|
@@ -560,7 +569,7 @@ function useLoaderInternal<T>(
|
|
|
560
569
|
* @example Basic usage - accessing route loader data
|
|
561
570
|
* ```tsx
|
|
562
571
|
* "use client";
|
|
563
|
-
* import { useLoader } from "
|
|
572
|
+
* import { useLoader } from "@rangojs/router/client";
|
|
564
573
|
* import { CartLoader } from "../loaders/cart";
|
|
565
574
|
*
|
|
566
575
|
* // In route definition: loader(CartLoader)
|
|
@@ -608,7 +617,7 @@ export function useLoader<T>(
|
|
|
608
617
|
* @example On-demand fetching
|
|
609
618
|
* ```tsx
|
|
610
619
|
* "use client";
|
|
611
|
-
* import { useFetchLoader } from "
|
|
620
|
+
* import { useFetchLoader } from "@rangojs/router/client";
|
|
612
621
|
* import { SearchLoader } from "../loaders/search";
|
|
613
622
|
*
|
|
614
623
|
* export function SearchResults() {
|
|
@@ -670,7 +679,7 @@ export function useFetchLoader<T>(
|
|
|
670
679
|
* @example
|
|
671
680
|
* ```tsx
|
|
672
681
|
* "use client";
|
|
673
|
-
* import { useLoader, useRefreshLoaders } from "
|
|
682
|
+
* import { useLoader, useRefreshLoaders } from "@rangojs/router/client";
|
|
674
683
|
*
|
|
675
684
|
* function Profile() {
|
|
676
685
|
* const { data } = useLoader(ProfileLoader, { key: userId, refreshGroup: "account" });
|
|
@@ -16,14 +16,18 @@
|
|
|
16
16
|
* - transformHoistInlineDirective() for function-level hoisting
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
-
import type { Plugin } from "vite";
|
|
19
|
+
import type { Plugin, Rollup } from "vite";
|
|
20
20
|
import type { ModuleExportMeta } from "@vitejs/plugin-rsc/transforms";
|
|
21
21
|
import path from "node:path";
|
|
22
|
-
import MagicString from "magic-string";
|
|
23
22
|
import { normalizePath, hashId } from "./expose-id-utils.js";
|
|
24
|
-
import {
|
|
23
|
+
import type { Counter } from "../debug.js";
|
|
25
24
|
|
|
26
|
-
|
|
25
|
+
// No static import of `magic-string` or `../debug.js` (the `debug` package):
|
|
26
|
+
// `rangoUseCacheTransform()` bundles this module into `dist/testing/vitest.js`,
|
|
27
|
+
// whose static imports are node builtins only, and every package it needs
|
|
28
|
+
// loads lazily, as `rangoUseClientTransform()`'s `vite` and plugin-rsc
|
|
29
|
+
// imports do. src/__tests__/installed-vitest-config.test.ts loads that file
|
|
30
|
+
// from a fixture holding only `package.json` and `dist/`.
|
|
27
31
|
|
|
28
32
|
const CACHE_RUNTIME_IMPORT = "@rangojs/router/cache-runtime";
|
|
29
33
|
|
|
@@ -33,12 +37,13 @@ const LAYOUT_TEMPLATE_PATTERN = /\/(layout|template)\.(tsx?|jsx?)$/;
|
|
|
33
37
|
|
|
34
38
|
export const USE_CACHE_DIRECTIVE_RE: RegExp = /^use cache(:\s*[\w-]+)?$/;
|
|
35
39
|
|
|
36
|
-
|
|
40
|
+
/**
|
|
41
|
+
* @param counter `rango:transform` timing counter, created by `rango()`
|
|
42
|
+
* (`createCounter` in ../debug.js); omitted when debugging is off.
|
|
43
|
+
*/
|
|
44
|
+
export function useCacheTransform(counter?: Counter): Plugin {
|
|
37
45
|
let projectRoot = "";
|
|
38
46
|
let isBuild = false;
|
|
39
|
-
let rscTransforms: typeof import("@vitejs/plugin-rsc/transforms") | null =
|
|
40
|
-
null;
|
|
41
|
-
const counter = createCounter(debug, "use-cache");
|
|
42
47
|
|
|
43
48
|
return {
|
|
44
49
|
name: "@rangojs/router:use-cache",
|
|
@@ -56,75 +61,15 @@ export function useCacheTransform(): Plugin {
|
|
|
56
61
|
async transform(code, id) {
|
|
57
62
|
// Only process in RSC environment
|
|
58
63
|
if (this.environment?.name !== "rsc") return;
|
|
59
|
-
|
|
60
|
-
// Quick bail: no "use cache" in source
|
|
61
|
-
if (!code.includes("use cache")) return;
|
|
62
|
-
|
|
63
|
-
// Skip node_modules and virtual modules
|
|
64
|
-
if (id.includes("/node_modules/") || id.startsWith("\0")) return;
|
|
65
|
-
|
|
66
|
-
// Only JS/TS files
|
|
67
|
-
if (!/\.(tsx?|jsx?|mjs)$/.test(id)) return;
|
|
64
|
+
if (!isUseCacheCandidate(code, id)) return;
|
|
68
65
|
|
|
69
66
|
const start = counter ? performance.now() : 0;
|
|
70
67
|
try {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
rscTransforms = await import("@vitejs/plugin-rsc/transforms");
|
|
74
|
-
} catch {
|
|
75
|
-
return;
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
const {
|
|
80
|
-
hasDirective,
|
|
81
|
-
transformWrapExport,
|
|
82
|
-
transformHoistInlineDirective,
|
|
83
|
-
} = rscTransforms;
|
|
84
|
-
|
|
85
|
-
let ast: any;
|
|
86
|
-
try {
|
|
87
|
-
const { parseAst } = await import("vite");
|
|
88
|
-
ast = parseAst(code, { lang: "tsx" });
|
|
89
|
-
} catch {
|
|
90
|
-
return;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// plugin-rsc 0.5.34 `matchDirective` does `stmt.directive.match(...)`
|
|
94
|
-
// after `"directive" in node`. Vite/oxc parseAst now emits
|
|
95
|
-
// `directive: null` on ordinary ExpressionStatements, so a file that
|
|
96
|
-
// mixes a `"use cache"` function with a sibling handler whose first
|
|
97
|
-
// statement is an expression throws and the wrap is dropped.
|
|
98
|
-
stripNullDirectiveFields(ast);
|
|
99
|
-
|
|
100
|
-
const filePath = normalizePath(path.relative(projectRoot, id));
|
|
101
|
-
const isLayoutOrTemplate = LAYOUT_TEMPLATE_PATTERN.test(id);
|
|
102
|
-
|
|
103
|
-
if (hasDirective(ast.body, "use cache")) {
|
|
104
|
-
return transformFileLevelUseCache(
|
|
105
|
-
code,
|
|
106
|
-
ast,
|
|
107
|
-
filePath,
|
|
108
|
-
id,
|
|
109
|
-
isBuild,
|
|
110
|
-
isLayoutOrTemplate,
|
|
111
|
-
transformWrapExport,
|
|
112
|
-
hasDirective,
|
|
113
|
-
);
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
const functionResult = transformFunctionLevelUseCache(
|
|
117
|
-
code,
|
|
118
|
-
ast,
|
|
119
|
-
filePath,
|
|
120
|
-
id,
|
|
68
|
+
return await transformUseCache(code, id, {
|
|
69
|
+
root: projectRoot,
|
|
121
70
|
isBuild,
|
|
122
|
-
|
|
123
|
-
);
|
|
124
|
-
|
|
125
|
-
warnOnNearMissDirectives(ast, id, this.warn.bind(this));
|
|
126
|
-
|
|
127
|
-
if (functionResult) return functionResult;
|
|
71
|
+
warn: (message) => this.warn(message),
|
|
72
|
+
});
|
|
128
73
|
} finally {
|
|
129
74
|
counter?.record(id, performance.now() - start);
|
|
130
75
|
}
|
|
@@ -132,7 +77,105 @@ export function useCacheTransform(): Plugin {
|
|
|
132
77
|
};
|
|
133
78
|
}
|
|
134
79
|
|
|
135
|
-
|
|
80
|
+
/** Options for {@link transformUseCache}. */
|
|
81
|
+
export interface UseCacheTransformOptions {
|
|
82
|
+
/** Root the ids are relative to (Vite's `config.root`). */
|
|
83
|
+
root: string;
|
|
84
|
+
/** Build ids hash the path (`hashId`); dev ids are `<path>#<export>`. */
|
|
85
|
+
isBuild: boolean;
|
|
86
|
+
/** Sink for the near-miss directive warning. */
|
|
87
|
+
warn: (message: string) => void;
|
|
88
|
+
/**
|
|
89
|
+
* Called when `@vitejs/plugin-rsc/transforms` fails to load; the module is
|
|
90
|
+
* then left unwrapped. The rango plugin omits it (skip silently).
|
|
91
|
+
*/
|
|
92
|
+
onTransformsError?: (error: unknown) => void;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** Whether a module may carry a `"use cache"` directive the transform wraps. */
|
|
96
|
+
export function isUseCacheCandidate(code: string, id: string): boolean {
|
|
97
|
+
return (
|
|
98
|
+
// Quick bail: no "use cache" in source
|
|
99
|
+
code.includes("use cache") &&
|
|
100
|
+
// Skip node_modules and virtual modules
|
|
101
|
+
!id.includes("/node_modules/") &&
|
|
102
|
+
!id.startsWith("\0") &&
|
|
103
|
+
// Only JS/TS files
|
|
104
|
+
/\.(tsx?|jsx?|mjs)$/.test(id)
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
let rscTransforms: typeof import("@vitejs/plugin-rsc/transforms") | null = null;
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Wrap a module's `"use cache"` functions with `registerCachedFunction`. The
|
|
112
|
+
* rango plugin runs it in the rsc environment; `rangoUseCacheTransform()`
|
|
113
|
+
* (testing/vitest.ts) runs it in a Vitest project, whose environment is `ssr`.
|
|
114
|
+
* Call it only for an {@link isUseCacheCandidate} module.
|
|
115
|
+
*/
|
|
116
|
+
export async function transformUseCache(
|
|
117
|
+
code: string,
|
|
118
|
+
id: string,
|
|
119
|
+
options: UseCacheTransformOptions,
|
|
120
|
+
): Promise<{ code: string; map: Rollup.ExistingRawSourceMap } | undefined> {
|
|
121
|
+
if (!rscTransforms) {
|
|
122
|
+
try {
|
|
123
|
+
rscTransforms = await import("@vitejs/plugin-rsc/transforms");
|
|
124
|
+
} catch (error) {
|
|
125
|
+
options.onTransformsError?.(error);
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const { hasDirective, transformWrapExport, transformHoistInlineDirective } =
|
|
131
|
+
rscTransforms;
|
|
132
|
+
|
|
133
|
+
let ast: any;
|
|
134
|
+
try {
|
|
135
|
+
const { parseAst } = await import("vite");
|
|
136
|
+
ast = parseAst(code, { lang: "tsx" });
|
|
137
|
+
} catch {
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// plugin-rsc 0.5.34 `matchDirective` does `stmt.directive.match(...)`
|
|
142
|
+
// after `"directive" in node`. Vite/oxc parseAst now emits
|
|
143
|
+
// `directive: null` on ordinary ExpressionStatements, so a file that
|
|
144
|
+
// mixes a `"use cache"` function with a sibling handler whose first
|
|
145
|
+
// statement is an expression throws and the wrap is dropped.
|
|
146
|
+
stripNullDirectiveFields(ast);
|
|
147
|
+
|
|
148
|
+
const filePath = normalizePath(path.relative(options.root, id));
|
|
149
|
+
const isLayoutOrTemplate = LAYOUT_TEMPLATE_PATTERN.test(id);
|
|
150
|
+
|
|
151
|
+
if (hasDirective(ast.body, "use cache")) {
|
|
152
|
+
return transformFileLevelUseCache(
|
|
153
|
+
code,
|
|
154
|
+
ast,
|
|
155
|
+
filePath,
|
|
156
|
+
id,
|
|
157
|
+
options.isBuild,
|
|
158
|
+
isLayoutOrTemplate,
|
|
159
|
+
transformWrapExport,
|
|
160
|
+
hasDirective,
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const functionResult = transformFunctionLevelUseCache(
|
|
165
|
+
code,
|
|
166
|
+
ast,
|
|
167
|
+
filePath,
|
|
168
|
+
id,
|
|
169
|
+
options.isBuild,
|
|
170
|
+
transformHoistInlineDirective,
|
|
171
|
+
);
|
|
172
|
+
|
|
173
|
+
warnOnNearMissDirectives(ast, id, options.warn);
|
|
174
|
+
|
|
175
|
+
if (functionResult) return functionResult;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
async function transformFileLevelUseCache(
|
|
136
179
|
code: string,
|
|
137
180
|
ast: any,
|
|
138
181
|
filePath: string,
|
|
@@ -193,6 +236,7 @@ function transformFileLevelUseCache(
|
|
|
193
236
|
}
|
|
194
237
|
|
|
195
238
|
if (exportNames.length === 0) {
|
|
239
|
+
const { default: MagicString } = await import("magic-string");
|
|
196
240
|
const s = new MagicString(code);
|
|
197
241
|
const directive = findFileLevelDirective(ast);
|
|
198
242
|
if (directive) {
|
package/src/vite/rango.ts
CHANGED
|
@@ -45,7 +45,7 @@ import { createVersionInjectorPlugin } from "./plugins/version-injector.js";
|
|
|
45
45
|
import { createCjsToEsmPlugin } from "./plugins/cjs-to-esm.js";
|
|
46
46
|
import { createRouterDiscoveryPlugin } from "./router-discovery.js";
|
|
47
47
|
import { performanceTracksPlugin } from "./plugins/performance-tracks.js";
|
|
48
|
-
import { createRangoDebugger, NS } from "./debug.js";
|
|
48
|
+
import { createCounter, createRangoDebugger, NS } from "./debug.js";
|
|
49
49
|
|
|
50
50
|
const debugConfig = createRangoDebugger(NS.config);
|
|
51
51
|
|
|
@@ -100,7 +100,7 @@ export { hasUseClientDirective };
|
|
|
100
100
|
* plugins: [
|
|
101
101
|
* react(),
|
|
102
102
|
* rango({ preset: 'cloudflare' }),
|
|
103
|
-
* cloudflare({ viteEnvironment: { name: 'rsc' } }),
|
|
103
|
+
* cloudflare({ viteEnvironment: { name: 'rsc', childEnvironments: ['ssr'] } }),
|
|
104
104
|
* ],
|
|
105
105
|
* });
|
|
106
106
|
* ```
|
|
@@ -565,7 +565,11 @@ export async function rango(options?: RangoOptions): Promise<PluginOption[]> {
|
|
|
565
565
|
plugins.push(createLoaderDirectiveGuardPlugin());
|
|
566
566
|
plugins.push(createLoaderScanStubPlugin());
|
|
567
567
|
plugins.push(exposeActionId());
|
|
568
|
-
plugins.push(
|
|
568
|
+
plugins.push(
|
|
569
|
+
useCacheTransform(
|
|
570
|
+
createCounter(createRangoDebugger(NS.transform), "use-cache"),
|
|
571
|
+
),
|
|
572
|
+
);
|
|
569
573
|
plugins.push(exposeInternalIds());
|
|
570
574
|
plugins.push(exposeRouterId());
|
|
571
575
|
plugins.push(createVersionPlugin());
|