@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
|
@@ -1,15 +1,22 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: prerender
|
|
3
|
-
description: Pre-render route segments at build time with Prerender
|
|
3
|
+
description: Pre-render route segments at build time with Prerender (with getParams), Passthrough live fallback, Static segments, and Skip. Use when a page's content is mostly static and shouldn't render on every request, speeding up cold responses, reading build-only data (files, build env), or deciding which routes to prerender vs render live.
|
|
4
4
|
argument-hint: [passthrough]
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Pre-rendering with Prerender
|
|
8
8
|
|
|
9
|
-
Pre-rendering is **caching at build time**.
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
Pre-rendering is **caching at build time**. `Prerender()` renders a route's
|
|
10
|
+
segments once per known param set during `vite build`, and `Static()` renders a
|
|
11
|
+
single segment once. Same serialization format, same deserialization path, same
|
|
12
|
+
segment system as the runtime `cache()`. The worker handles every request --
|
|
13
|
+
there are NO static .html or .rsc files served from assets. At runtime the
|
|
14
|
+
worker reads the pre-computed Flight payloads instead of executing handler code,
|
|
15
|
+
exactly like a cache hit; middleware and loaders still run on every request.
|
|
16
|
+
|
|
17
|
+
Use it when the page content is known at build time (markdown files, a CMS
|
|
18
|
+
snapshot, a fixed list of products) or needs build-only APIs such as `node:fs`.
|
|
19
|
+
The handler code and its imports are removed from the production bundle.
|
|
13
20
|
|
|
14
21
|
## Not this skill if…
|
|
15
22
|
|
|
@@ -17,10 +24,11 @@ pre-computed Flight payloads instead of executing handler code.
|
|
|
17
24
|
staying live per request — see `/ppr`.
|
|
18
25
|
- You want runtime segment caching with TTL/SWR — that is the `cache()` DSL:
|
|
19
26
|
see `/caching`. Prerender is the same cache filled at build time.
|
|
27
|
+
- You are unsure which cache layer you need — start at `/cache-guide`.
|
|
20
28
|
|
|
21
29
|
## API: Prerender
|
|
22
30
|
|
|
23
|
-
###
|
|
31
|
+
### Route without params
|
|
24
32
|
|
|
25
33
|
```typescript
|
|
26
34
|
import { Prerender } from "@rangojs/router";
|
|
@@ -96,7 +104,7 @@ functions — no `ctx.build` branching needed.
|
|
|
96
104
|
| Unknown params | Handler evicted, no live fallback | Live handler runs at request time |
|
|
97
105
|
| `ctx.passthrough()` | Throws (not on Passthrough route) | Skips artifact, defers to live handler |
|
|
98
106
|
| Bundle size | Build handler code + imports removed | Build handler evicted, live handler kept |
|
|
99
|
-
| `revalidate()` |
|
|
107
|
+
| `revalidate()` | No effect on prerendered segments | Allowed (live handler can re-render) |
|
|
100
108
|
| `loading()` | Ignored (segments fully resolved) | Works for live fallback renders |
|
|
101
109
|
|
|
102
110
|
### When to use Passthrough
|
|
@@ -138,12 +146,19 @@ interface BuildContext<TParams> {
|
|
|
138
146
|
search?: Record<string, unknown>,
|
|
139
147
|
): string; // URL generation
|
|
140
148
|
passthrough(): PrerenderPassthroughResult; // Skip local artifact (Passthrough routes only)
|
|
141
|
-
dynamic(): void; // No-op in Prerender/Static handlers; use middleware for PPR shell opt-out
|
|
142
149
|
env: DefaultEnv; // Available when buildEnv is configured in rango() (throws otherwise)
|
|
143
|
-
//
|
|
150
|
+
// ctx.request, ctx.res, ctx.headers throw: there is no request at build time.
|
|
151
|
+
// ctx.waitUntil() is a no-op. ctx.dynamic() exists at runtime but is a no-op
|
|
152
|
+
// here -- opt a URL out of a build shell from middleware instead.
|
|
144
153
|
}
|
|
145
154
|
```
|
|
146
155
|
|
|
156
|
+
`getParams` receives a smaller `GetParamsContext` (`build`, `dev`, `env`, `set`,
|
|
157
|
+
`reverse`). Values it stores with `ctx.set()` are copied into every param set's
|
|
158
|
+
handler context and read there with `ctx.get()`. `env` is the `buildEnv` from
|
|
159
|
+
`rango()` — build-time bindings shared by the whole build, not a live request
|
|
160
|
+
env.
|
|
161
|
+
|
|
147
162
|
Use `createVar<T>()` to share typed data from a Prerender handler to child layouts:
|
|
148
163
|
|
|
149
164
|
```typescript
|
|
@@ -199,6 +214,13 @@ export const BlogPost = cph(getParams, handler);
|
|
|
199
214
|
All patterns support whole-file stubbing, expression stubbing, and build-time
|
|
200
215
|
module tracking. The same applies to `Static`.
|
|
201
216
|
|
|
217
|
+
Anything else gets no build-injected id, and the call throws
|
|
218
|
+
`Prerender: missing $$id` when the module loads. Unsupported shapes include
|
|
219
|
+
`export let`/`export var`, and calling `Prerender(...)` inline — for example
|
|
220
|
+
directly inside `path()`. Declare it as an exported `const` and pass the
|
|
221
|
+
binding. (`Passthrough(def, liveHandler)` needs no id and can be written inline
|
|
222
|
+
in `path()`.)
|
|
223
|
+
|
|
202
224
|
## Handler Eviction
|
|
203
225
|
|
|
204
226
|
In production builds, `Prerender` exports are replaced with stubs:
|
|
@@ -221,8 +243,9 @@ In client and SSR environments, ALL prerender handlers are always stubbed.
|
|
|
221
243
|
|
|
222
244
|
## Sub-use Semantics
|
|
223
245
|
|
|
224
|
-
Everything inside the path's use() callback is part of the
|
|
225
|
-
pre-rendered
|
|
246
|
+
Everything inside the path's use() callback is part of the pre-rendered route
|
|
247
|
+
subtree (the "B segment" in the design docs) and gets pre-rendered. Loaders are
|
|
248
|
+
the exception — they stay live:
|
|
226
249
|
|
|
227
250
|
```typescript
|
|
228
251
|
path("/blog/:slug", BlogPost, { name: "blog.post" }, () => [
|
|
@@ -233,34 +256,69 @@ path("/blog/:slug", BlogPost, { name: "blog.post" }, () => [
|
|
|
233
256
|
])
|
|
234
257
|
```
|
|
235
258
|
|
|
236
|
-
|
|
237
|
-
|
|
259
|
+
Only the route's own `Prerender` handler is evicted from the bundle. If a
|
|
260
|
+
parallel or child layout uses build-only APIs such as `node:fs`, wrap it in
|
|
261
|
+
`Static()` (see "Static segments" below) so the Vite plugin can stub it too.
|
|
262
|
+
`layout()` and `parallel()` accept `Static()` definitions; they do not accept
|
|
263
|
+
`Prerender()` definitions.
|
|
238
264
|
|
|
239
265
|
```typescript
|
|
240
|
-
// sidebar.tsx -- uses node:fs, must be
|
|
241
|
-
|
|
266
|
+
// sidebar.tsx -- uses node:fs, so it must be stubbed out of the bundle
|
|
267
|
+
import { Static } from "@rangojs/router";
|
|
268
|
+
|
|
269
|
+
export const BlogSidebar = Static(async () => {
|
|
242
270
|
const files = await fs.readdir("content/blog/");
|
|
243
|
-
return <Sidebar posts={files.map(f => basename(f, ".md"))} />;
|
|
271
|
+
return <Sidebar posts={files.map((f) => basename(f, ".md"))} />;
|
|
244
272
|
});
|
|
245
273
|
|
|
246
274
|
// urls.tsx
|
|
247
275
|
path("/blog/:slug", BlogPost, { name: "blog.post" }, () => [
|
|
248
|
-
parallel({ "@sidebar": BlogSidebar }),
|
|
276
|
+
parallel({ "@sidebar": BlogSidebar }), // stubbable, node:fs excluded
|
|
249
277
|
])
|
|
250
278
|
```
|
|
251
279
|
|
|
280
|
+
## Static segments
|
|
281
|
+
|
|
282
|
+
`Static(handler, options?)` renders ONE segment once at build time. It has no
|
|
283
|
+
params and produces no URLs; it works on `layout()`, `parallel()`, and `path()`.
|
|
284
|
+
Use it for build-time chrome such as a docs navigation built from files, or for
|
|
285
|
+
a static route with no params.
|
|
286
|
+
|
|
287
|
+
```typescript
|
|
288
|
+
import { Static } from "@rangojs/router";
|
|
289
|
+
|
|
290
|
+
export const DocsNav = Static(async (ctx) => {
|
|
291
|
+
const docs = await readDocsIndex(); // build-only I/O
|
|
292
|
+
return <Nav docs={docs} />;
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
// urls.tsx
|
|
296
|
+
layout(DocsNav, () => [
|
|
297
|
+
path("/docs/:slug", DocPage, { name: "doc" }),
|
|
298
|
+
]);
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
- The handler receives `StaticBuildContext`: `build`, `dev`, `env` (with
|
|
302
|
+
`buildEnv`), `get`/`set`, `use(handle)`, and `reverse`. `params`, `url`,
|
|
303
|
+
`pathname`, and `request` throw.
|
|
304
|
+
- `Static(handler, { passthrough: true })` skips the build render and keeps the
|
|
305
|
+
handler in the bundle, rendering live at request time.
|
|
306
|
+
- `Static()` always renders sequentially (no `concurrency` option) and supports
|
|
307
|
+
`throw new Skip()` (see "Skipping Entries with Skip").
|
|
308
|
+
- The same export rules as `Prerender` apply (`export const X = Static(...)`).
|
|
309
|
+
|
|
252
310
|
## Interaction with DSL Items
|
|
253
311
|
|
|
254
|
-
| DSL item | Behavior with Prerender
|
|
255
|
-
| -------------- |
|
|
256
|
-
| `loader()` | Live at runtime, bundled normally. Use `cache()` for caching.
|
|
257
|
-
| `revalidate()` |
|
|
258
|
-
| `cache()` | Orthogonal --
|
|
259
|
-
| `layout()` | Child layouts inside path are pre-rendered. Parent layouts are live.
|
|
260
|
-
| `parallel()` | Parallel slots inside path are pre-rendered.
|
|
261
|
-
| `middleware()` | Skipped while collecting build-time Flight payloads (no request). For `Prerender` + `ppr`,
|
|
262
|
-
| `loading()` | Ignored without Passthrough. Works for live fallback with Passthrough.
|
|
263
|
-
| `intercept()` | Pre-rendered at build time. Intercept variant stored under `/i` key alongside main segments. At runtime, the correct variant is served based on `ctx.isIntercept`. `when` config conditions are skipped at build time (all intercepts are pre-rendered unconditionally).
|
|
312
|
+
| DSL item | Behavior with Prerender |
|
|
313
|
+
| -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
314
|
+
| `loader()` | Live at runtime, bundled normally. Use `cache()` for caching. |
|
|
315
|
+
| `revalidate()` | Without Passthrough: no effect on the prerendered segments (re-served from the build artifact); loader revalidation still works. With Passthrough: re-renders through the live handler. |
|
|
316
|
+
| `cache()` | Orthogonal -- prerendered segments bypass the runtime cache. Use `cache()` on parent layouts and loaders. |
|
|
317
|
+
| `layout()` | Child layouts inside path are pre-rendered. Parent layouts are live. |
|
|
318
|
+
| `parallel()` | Parallel slots inside path are pre-rendered. |
|
|
319
|
+
| `middleware()` | Skipped while collecting build-time Flight payloads (no request). For `Prerender` + `ppr`, the build-shell step replays global and route middleware with `ctx.build === true`; `ctx.dynamic()` skips that shell. At request time middleware runs normally on every request. |
|
|
320
|
+
| `loading()` | Ignored without Passthrough. Works for live fallback with Passthrough. |
|
|
321
|
+
| `intercept()` | Pre-rendered at build time. Intercept variant stored under `/i` key alongside main segments. At runtime, the correct variant is served based on `ctx.isIntercept`. `when` config conditions are skipped at build time (all intercepts are pre-rendered unconditionally). |
|
|
264
322
|
|
|
265
323
|
When Passthrough revalidation is enabled, remember that revalidation is
|
|
266
324
|
still partial: opting a child segment into revalidation does not
|
|
@@ -269,9 +327,9 @@ implicitly re-run outer prerender-derived handlers/layouts.
|
|
|
269
327
|
## Prerender + PPR Build Shells
|
|
270
328
|
|
|
271
329
|
A `Prerender` page may also declare `ppr` on the path option. The build still
|
|
272
|
-
stores the Flight payload first. After that,
|
|
273
|
-
shell for each generated URL so the
|
|
274
|
-
`x-rango-shell: HIT`.
|
|
330
|
+
stores the Flight payload first. After that, a build-shell step ("producer B"
|
|
331
|
+
in the design docs) tries to bake the HTML shell for each generated URL so the
|
|
332
|
+
first document request can be an `x-rango-shell: HIT`.
|
|
275
333
|
|
|
276
334
|
That shell capture is request-shaped enough to run middleware safely:
|
|
277
335
|
|
|
@@ -284,6 +342,11 @@ Use `ctx.build` inside middleware to avoid runtime-only side effects during
|
|
|
284
342
|
build shell capture, or call `ctx.dynamic()` to leave that route to runtime
|
|
285
343
|
PPR. Runtime requests still run the normal middleware chain.
|
|
286
344
|
|
|
345
|
+
Build shells are keyed by pathname and served only for requests without a query
|
|
346
|
+
string (after `cache.searchParams` filtering). A URL with search params has its
|
|
347
|
+
own shell identity and is captured at runtime. The runtime shell store is read
|
|
348
|
+
first, so a runtime capture supersedes the baked entry once it exists.
|
|
349
|
+
|
|
287
350
|
### Freshness of a build shell
|
|
288
351
|
|
|
289
352
|
A build-baked shell is not on a wall clock the way a pure runtime `ppr` shell is.
|
|
@@ -305,24 +368,25 @@ redeploy) instead of a shorter `ttl`.
|
|
|
305
368
|
|
|
306
369
|
`ctx.dynamic()` opts a request off the shell axis ONLY. A `Prerender` route has
|
|
307
370
|
no live handler to fall back to (it was evicted), so a `dynamic()` request still
|
|
308
|
-
serves the build-baked
|
|
371
|
+
serves the build-baked segments — fresh loaders in their holes, not a fresh
|
|
309
372
|
handler render. There is no "fully dynamic" render for a prerendered route.
|
|
310
373
|
|
|
311
374
|
## Dev Mode
|
|
312
375
|
|
|
313
|
-
In dev
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
376
|
+
In dev there is no build pass and no handler stubbing. Instead, a request to a
|
|
377
|
+
prerendered route is rendered **on demand** through the dev server's
|
|
378
|
+
`/__rsc_prerender` endpoint, which runs the same build-time resolution in
|
|
379
|
+
Node.js (so `node:fs` and `buildEnv` work even when the app itself runs in
|
|
380
|
+
workerd). This applies to both the Node and the Cloudflare presets.
|
|
381
|
+
|
|
382
|
+
- The handler receives `BuildContext` (`ctx.build === true`, `ctx.dev === true`),
|
|
383
|
+
and segments resolve exactly as in the production prerender.
|
|
384
|
+
- Results are memoized until you edit a file in the router's module graph, so
|
|
385
|
+
`getParams()` and handler side effects run once per edit, not once per request.
|
|
386
|
+
The endpoint reports `x-rango-prerender-cache: HIT | MISS`.
|
|
387
|
+
- If the on-demand render has no entry for the URL (unknown param, `Skip`,
|
|
388
|
+
`ctx.passthrough()`, or a render error, which is logged), the request falls
|
|
389
|
+
through to a live render — the Passthrough live handler when there is one.
|
|
326
390
|
|
|
327
391
|
## Storage Layout
|
|
328
392
|
|
|
@@ -378,13 +442,28 @@ export const BlogPost = Prerender(
|
|
|
378
442
|
},
|
|
379
443
|
);
|
|
380
444
|
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
}
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
To serve skipped params live at runtime, give the definition its own name and
|
|
448
|
+
wrap it with `Passthrough()`; the live handler renders them:
|
|
449
|
+
|
|
450
|
+
```typescript
|
|
451
|
+
import { Prerender, Passthrough, Skip } from "@rangojs/router";
|
|
452
|
+
|
|
453
|
+
export const BlogPostDef = Prerender(
|
|
454
|
+
async () => [{ slug: "published" }, { slug: "draft" }],
|
|
455
|
+
async (ctx) => {
|
|
456
|
+
if (ctx.params.slug === "draft") {
|
|
457
|
+
throw new Skip("Draft articles are not pre-rendered");
|
|
458
|
+
}
|
|
459
|
+
return <PostPage slug={ctx.params.slug} />;
|
|
460
|
+
},
|
|
461
|
+
);
|
|
462
|
+
|
|
463
|
+
// Live handler: runs at request time for "draft" and any unknown slug
|
|
464
|
+
export const BlogPost = Passthrough(BlogPostDef, async (ctx) => (
|
|
465
|
+
<PostPage slug={ctx.params.slug} />
|
|
466
|
+
));
|
|
388
467
|
```
|
|
389
468
|
|
|
390
469
|
Skipped entries are excluded from the build output. With `Passthrough()`,
|
|
@@ -416,6 +495,12 @@ rango({ prerender: { onError: "warn" } }); // default is "fail"
|
|
|
416
495
|
| `return ctx.passthrough()` | Skip entry, log PASS (Passthrough routes) | Skip entry, log PASS |
|
|
417
496
|
| `throw new Skip("reason")` | Skip entry, log SKIP, continue | Skip entry, log SKIP, continue |
|
|
418
497
|
| `throw new Error("reason")` | Log FAIL, stop ALL pre-rendering, fail build | Log WARN, skip the URL, continue |
|
|
498
|
+
| A child component throws | Log FAIL, stop ALL pre-rendering, fail build | Log WARN, skip the URL, continue |
|
|
499
|
+
|
|
500
|
+
The last row covers a handler that returns fine but whose tree holds an async
|
|
501
|
+
server component that throws while the page is rendered at build, for example
|
|
502
|
+
because its fetch fails. That error goes through the same policy instead of being
|
|
503
|
+
baked as an error row, and a `Skip` thrown there skips the URL.
|
|
419
504
|
|
|
420
505
|
With `"warn"` the errored entry is logged and left un-baked (never served as a baked
|
|
421
506
|
200 error page). `"warn"` is a build-unblock, not a runtime contract: the route falls
|
|
@@ -427,7 +512,8 @@ fallback) or `throw new Skip()` (an intentional skip — works in the render fn,
|
|
|
427
512
|
only `getParams()`); otherwise prefer the default `"fail"`.
|
|
428
513
|
|
|
429
514
|
Both `Skip` and hard errors propagate to the router's `onError` callback with phase
|
|
430
|
-
`"prerender"` or `"static"
|
|
515
|
+
`"prerender"` or `"static"`; skipped entries (a `Skip`, or an error under
|
|
516
|
+
`"warn"`) carry `metadata.skipped: true`.
|
|
431
517
|
|
|
432
518
|
### Build logs
|
|
433
519
|
|
|
@@ -454,13 +540,9 @@ the build continues.
|
|
|
454
540
|
|
|
455
541
|
### Dev mode behavior
|
|
456
542
|
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
**Non-Node runtimes using `/__rsc_prerender`** — `Skip` participates in the
|
|
461
|
-
on-demand prerender path, so build-style skip logic does run for that request.
|
|
462
|
-
The dev prerender endpoint treats it like a prerender miss and the request
|
|
463
|
-
falls back according to normal dev/runtime behavior.
|
|
543
|
+
In dev, `Skip` runs inside the on-demand `/__rsc_prerender` render, so
|
|
544
|
+
build-style skip logic runs for that request. The endpoint treats it as a
|
|
545
|
+
prerender miss and the request falls through to a live render (see "Dev Mode").
|
|
464
546
|
|
|
465
547
|
## Per-Param Passthrough with ctx.passthrough()
|
|
466
548
|
|
|
@@ -564,23 +646,48 @@ say) is request-time data — delivery follows the loader race model, see
|
|
|
564
646
|
|
|
565
647
|
### Server actions work normally
|
|
566
648
|
|
|
567
|
-
Actions
|
|
568
|
-
|
|
569
|
-
|
|
649
|
+
Actions run normally; what changes is the re-render after them. Loaders are live
|
|
650
|
+
and are revalidated by actions as usual.
|
|
651
|
+
|
|
652
|
+
- **Plain `Prerender`**: the handler was evicted, so the action re-render serves
|
|
653
|
+
the stored prerender entry again. The prerendered output stays frozen; the
|
|
654
|
+
action's return value reaches the client as usual.
|
|
655
|
+
- **`Passthrough()`**: the action re-render runs the live handler, replacing the
|
|
656
|
+
prerendered output. To keep the frozen tree mounted after actions (for example
|
|
657
|
+
so a client component's form state survives), suppress action
|
|
658
|
+
revalidation on the route while keeping navigation defaults:
|
|
659
|
+
`revalidate((ctx) => (ctx.isAction() ? false : undefined))`.
|
|
660
|
+
|
|
661
|
+
A handler may also embed an inline `"use server"` action that closes over
|
|
662
|
+
build-time values; the captured values are frozen at build and the action body
|
|
663
|
+
runs live when invoked.
|
|
570
664
|
|
|
571
665
|
### Empty getParams
|
|
572
666
|
|
|
573
667
|
If `getParams` returns an empty array, no Flight payloads are written. No error.
|
|
668
|
+
Every request to the route then behaves like an unknown param.
|
|
574
669
|
|
|
575
670
|
### Route name is required
|
|
576
671
|
|
|
577
672
|
Routes using `Prerender` must have a `name` in path options.
|
|
578
673
|
The name is used as the storage key for Flight payloads.
|
|
579
674
|
|
|
580
|
-
###
|
|
675
|
+
### revalidate() needs Passthrough
|
|
676
|
+
|
|
677
|
+
Without `Passthrough()` the handler is evicted, so there is nothing to
|
|
678
|
+
re-render: a revalidation re-serves the prerendered segments from the build
|
|
679
|
+
artifact. `revalidate()` on the route's loaders still works. No warning is
|
|
680
|
+
emitted — wrap the route in `Passthrough()` when its segments must re-render.
|
|
681
|
+
|
|
682
|
+
### Tag invalidation does not reach prerendered segments
|
|
581
683
|
|
|
582
|
-
|
|
583
|
-
|
|
684
|
+
`updateTag()`/`revalidateTag()` evict runtime cache entries. They do not refresh
|
|
685
|
+
a prerendered route's build-time Flight payload, which is served before the tag
|
|
686
|
+
system is consulted, and a `cacheTag()` inside a `"use cache"` function that runs
|
|
687
|
+
during the build has no runtime effect. A redeploy replaces the payload. To make
|
|
688
|
+
a route tag-invalidatable, serve it from the runtime cache instead (`cache()`,
|
|
689
|
+
or a `Passthrough()` live handler). A build-baked **ppr shell** is different: a
|
|
690
|
+
tag it carries does drop it (see "Freshness of a build shell").
|
|
584
691
|
|
|
585
692
|
### loading() is ignored without Passthrough
|
|
586
693
|
|
|
@@ -589,11 +696,13 @@ With `Passthrough()`, `loading()` works for live fallback renders.
|
|
|
589
696
|
|
|
590
697
|
## Complete Example
|
|
591
698
|
|
|
699
|
+
Known guides are pre-rendered; any other slug is rendered live by the
|
|
700
|
+
Passthrough handler.
|
|
701
|
+
|
|
592
702
|
```typescript
|
|
593
703
|
// pages/guides-handler.tsx
|
|
594
704
|
import { Prerender, Passthrough } from "@rangojs/router";
|
|
595
705
|
import { Link } from "@rangojs/router/client";
|
|
596
|
-
import { href } from "../router.js";
|
|
597
706
|
|
|
598
707
|
const knownGuides: Record<string, string> = {
|
|
599
708
|
routing: "Routing Guide",
|
|
@@ -609,9 +718,9 @@ export const GuidesDetailDef = Prerender<{ slug: string }>(
|
|
|
609
718
|
<h1>{title}</h1>
|
|
610
719
|
<p>Slug: {ctx.params.slug}</p>
|
|
611
720
|
<nav>
|
|
612
|
-
<Link to={
|
|
721
|
+
<Link to={ctx.reverse("guides.detail", { slug: "routing" })}>Routing</Link>
|
|
613
722
|
{" | "}
|
|
614
|
-
<Link to={
|
|
723
|
+
<Link to={ctx.reverse("guides.detail", { slug: "dynamic-test" })}>Dynamic</Link>
|
|
615
724
|
</nav>
|
|
616
725
|
</div>
|
|
617
726
|
);
|
|
@@ -625,9 +734,9 @@ export const GuidesDetail = Passthrough(GuidesDetailDef, async (ctx) => {
|
|
|
625
734
|
<h1>{title}</h1>
|
|
626
735
|
<p>Slug: {ctx.params.slug}</p>
|
|
627
736
|
<nav>
|
|
628
|
-
<Link to={
|
|
737
|
+
<Link to={ctx.reverse("guides.detail", { slug: "routing" })}>Routing</Link>
|
|
629
738
|
{" | "}
|
|
630
|
-
<Link to={
|
|
739
|
+
<Link to={ctx.reverse("guides.detail", { slug: "dynamic-test" })}>Dynamic</Link>
|
|
631
740
|
</nav>
|
|
632
741
|
</div>
|
|
633
742
|
);
|
|
@@ -711,41 +820,21 @@ Loaders run fresh at request time for both variants.
|
|
|
711
820
|
|
|
712
821
|
Pre-rendered routes set flags on the route trie leaf at build time:
|
|
713
822
|
|
|
714
|
-
- `pr: true` -- route has pre-rendered
|
|
823
|
+
- `pr: true` -- route has pre-rendered segment data
|
|
715
824
|
- `pt: true` -- route wrapped with `Passthrough()` (live handler available)
|
|
716
825
|
|
|
717
826
|
At runtime, the cache-lookup middleware uses these flags:
|
|
718
827
|
|
|
719
828
|
- `pr + hit` -- serve pre-rendered Flight payload
|
|
720
829
|
- `pr + pt + miss` -- fall through to Passthrough live handler
|
|
721
|
-
- `pr + miss` (no pt) -- fall through
|
|
722
|
-
|
|
723
|
-
## Contributor Checklist
|
|
724
|
-
|
|
725
|
-
Before changing prerender behavior, run these tests.
|
|
726
|
-
|
|
727
|
-
### Tests to run
|
|
728
|
-
|
|
729
|
-
```bash
|
|
730
|
-
# Core prerender e2e (Passthrough, eviction, loaders, sub-use, intercept)
|
|
731
|
-
pnpm --filter @rangojs/router exec playwright test prerender
|
|
732
|
-
|
|
733
|
-
# Prerender-specific unit test
|
|
734
|
-
pnpm --filter @rangojs/router run test:unit -- prerender-passthrough
|
|
735
|
-
|
|
736
|
-
# Semantic matrix (prerender rows cover intercept + ctx propagation)
|
|
737
|
-
pnpm --filter @rangojs/router exec playwright test semantic-matrix
|
|
738
|
-
|
|
739
|
-
# Handler-first (ctx.set/get visibility with prerender handlers)
|
|
740
|
-
pnpm --filter @rangojs/router exec playwright test handler-first
|
|
741
|
-
```
|
|
830
|
+
- `pr + miss` (no pt) -- fall through to the stubbed handler, which throws a
|
|
831
|
+
not-found error ("No prerender data found for this route"); there is no live render
|
|
742
832
|
|
|
743
|
-
|
|
833
|
+
## Related
|
|
744
834
|
|
|
745
|
-
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
-
|
|
749
|
-
|
|
750
|
-
-
|
|
751
|
-
fallback, intercept variant selection) must work in the production build.
|
|
835
|
+
- `/cache-guide` — how prerendering compares with the runtime cache layers
|
|
836
|
+
- `/caching` — runtime `cache()` segments and loader caching
|
|
837
|
+
- `/ppr` — the `ppr` path option; combine with `Prerender` for build-time shells
|
|
838
|
+
- `/shell-manifest` — prerendered shell feeding ids to a live loader
|
|
839
|
+
- `/deployment-caching` — why prerender output is not a CDN static file
|
|
840
|
+
- `/loader` — loaders stay live on prerendered routes
|