@rangojs/router 0.0.0-experimental.e9c0b2f2 → 0.0.0-experimental.ea9f40f2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +6 -10
- package/README.md +289 -938
- package/dist/bin/rango.js +271 -46
- package/dist/vite/index.js +673 -193
- package/package.json +10 -8
- package/skills/api-client/SKILL.md +1 -1
- package/skills/breadcrumbs/SKILL.md +31 -14
- package/skills/cache-guide/SKILL.md +5 -2
- package/skills/caching/SKILL.md +59 -4
- package/skills/catalog.json +271 -0
- package/skills/comparison/SKILL.md +50 -0
- package/skills/comparison/agents/openai.yaml +4 -0
- package/skills/comparison/references/framework-comparison.md +837 -0
- package/skills/composability/SKILL.md +83 -2
- package/skills/debug-manifest/SKILL.md +1 -1
- package/skills/defer-hydration/SKILL.md +235 -0
- package/skills/document-cache/SKILL.md +9 -1
- package/skills/fonts/SKILL.md +1 -1
- package/skills/handler-use/SKILL.md +8 -8
- package/skills/hooks/SKILL.md +54 -892
- package/skills/hooks/data.md +273 -0
- package/skills/hooks/handle-and-actions.md +103 -0
- package/skills/hooks/navigation.md +110 -0
- package/skills/hooks/outlets.md +41 -0
- package/skills/hooks/state.md +228 -0
- package/skills/hooks/urls.md +135 -0
- package/skills/host-router/SKILL.md +4 -4
- package/skills/i18n/SKILL.md +1 -1
- package/skills/intercept/SKILL.md +46 -14
- package/skills/layout/SKILL.md +27 -10
- package/skills/links/SKILL.md +1 -1
- package/skills/loader/SKILL.md +23 -1
- package/skills/middleware/SKILL.md +7 -3
- package/skills/migrate-nextjs/SKILL.md +167 -6
- package/skills/migrate-react-router/SKILL.md +59 -677
- package/skills/migrate-react-router/cloudflare-workers.md +129 -0
- package/skills/migrate-react-router/component-migration.md +196 -0
- package/skills/migrate-react-router/data-and-actions.md +225 -0
- package/skills/migrate-react-router/route-mapping.md +271 -0
- package/skills/mime-routes/SKILL.md +1 -1
- package/skills/observability/SKILL.md +9 -1
- package/skills/parallel/SKILL.md +23 -4
- package/skills/ppr/SKILL.md +622 -0
- package/skills/prerender/SKILL.md +28 -18
- package/skills/rango/SKILL.md +84 -25
- package/skills/response-routes/SKILL.md +15 -1
- package/skills/route/SKILL.md +71 -4
- package/skills/router-setup/SKILL.md +14 -3
- package/skills/scripts/SKILL.md +1 -1
- package/skills/server-actions/SKILL.md +3 -2
- package/skills/shell-manifest/SKILL.md +185 -0
- package/skills/streams-and-websockets/SKILL.md +1 -1
- package/skills/tailwind/SKILL.md +1 -1
- package/skills/testing/SKILL.md +2 -1
- package/skills/testing/handles.md +4 -2
- package/skills/testing/render-handler.md +15 -14
- package/skills/testing/reverse-and-types.md +8 -7
- package/skills/theme/SKILL.md +1 -1
- package/skills/typesafety/SKILL.md +45 -919
- package/skills/typesafety/env-and-bindings.md +254 -0
- package/skills/typesafety/generated-files-and-cli.md +335 -0
- package/skills/typesafety/params-and-search.md +153 -0
- package/skills/typesafety/route-types.md +209 -0
- package/skills/use-cache/SKILL.md +30 -3
- package/skills/vercel/SKILL.md +1 -1
- package/skills/view-transitions/SKILL.md +44 -1
- package/src/browser/event-controller.ts +62 -10
- package/src/browser/logging.ts +28 -0
- package/src/browser/merge-segment-loaders.ts +6 -4
- package/src/browser/navigation-bridge.ts +65 -16
- package/src/browser/navigation-client.ts +32 -2
- package/src/browser/navigation-store.ts +128 -14
- package/src/browser/network-error-handler.ts +34 -7
- package/src/browser/partial-update.ts +76 -17
- package/src/browser/prefetch/cache.ts +51 -11
- package/src/browser/prefetch/fetch.ts +59 -21
- package/src/browser/prefetch/queue.ts +19 -4
- package/src/browser/react/Link.tsx +13 -3
- package/src/browser/react/NavigationProvider.tsx +108 -4
- package/src/browser/response-adapter.ts +38 -9
- package/src/browser/rsc-router.tsx +54 -4
- package/src/browser/scroll-restoration.ts +7 -5
- package/src/browser/segment-reconciler.ts +31 -21
- package/src/browser/server-action-bridge.ts +22 -10
- package/src/browser/types.ts +54 -1
- package/src/build/generate-manifest.ts +155 -131
- package/src/build/index.ts +3 -1
- package/src/build/route-trie.ts +35 -7
- package/src/build/route-types/include-resolution.ts +347 -47
- package/src/build/runtime-discovery.ts +4 -1
- package/src/cache/cache-key-utils.ts +29 -0
- package/src/cache/cache-runtime.ts +262 -71
- package/src/cache/cache-scope.ts +2 -17
- package/src/cache/cache-tag.ts +60 -14
- package/src/cache/cf/cf-cache-store.ts +243 -20
- package/src/cache/document-cache.ts +54 -21
- package/src/cache/index.ts +1 -0
- package/src/cache/memory-segment-store.ts +110 -3
- package/src/cache/profile-registry.ts +15 -0
- package/src/cache/read-through-swr.ts +15 -1
- package/src/cache/segment-codec.ts +4 -4
- package/src/cache/shell-snapshot.ts +417 -0
- package/src/cache/types.ts +158 -0
- package/src/cache/vercel/vercel-cache-store.ts +401 -124
- package/src/client.rsc.tsx +0 -3
- package/src/client.tsx +0 -3
- package/src/cloudflare/tracing.ts +7 -8
- package/src/defer.ts +11 -22
- package/src/handle.ts +37 -15
- package/src/handles/MetaTags.tsx +16 -82
- package/src/handles/breadcrumbs.ts +12 -14
- package/src/handles/deferred-resolution.ts +127 -0
- package/src/handles/is-thenable.ts +7 -8
- package/src/handles/meta.ts +7 -44
- package/src/host/errors.ts +15 -0
- package/src/host/index.ts +1 -0
- package/src/index.rsc.ts +8 -2
- package/src/index.ts +19 -13
- package/src/internal-debug.ts +11 -8
- package/src/prerender.ts +17 -4
- package/src/redirect-origin.ts +14 -0
- package/src/render-error-thrower.tsx +20 -0
- package/src/route-content-wrapper.tsx +12 -5
- package/src/route-definition/dsl-helpers.ts +21 -32
- package/src/route-definition/helper-factories.ts +0 -2
- package/src/route-definition/helpers-types.ts +43 -43
- package/src/route-definition/index.ts +1 -2
- package/src/route-definition/resolve-handler-use.ts +0 -1
- package/src/route-definition/use-item-types.ts +3 -6
- package/src/route-map-builder.ts +41 -4
- package/src/route-types.ts +0 -5
- package/src/router/find-match.ts +86 -8
- package/src/router/instrument.ts +9 -4
- package/src/router/lazy-includes.ts +72 -12
- package/src/router/loader-resolution.ts +14 -2
- package/src/router/manifest.ts +56 -11
- package/src/router/match-api.ts +76 -32
- package/src/router/match-handlers.ts +181 -135
- package/src/router/match-middleware/background-revalidation.ts +40 -23
- package/src/router/match-middleware/cache-store.ts +39 -24
- package/src/router/match-result.ts +35 -15
- package/src/router/middleware.ts +64 -38
- package/src/router/navigation-snapshot.ts +7 -5
- package/src/router/parse-pattern.ts +115 -0
- package/src/router/pattern-matching.ts +53 -64
- package/src/router/prefetch-limits.ts +37 -0
- package/src/router/prerender-match.ts +11 -5
- package/src/router/preview-match.ts +3 -1
- package/src/router/request-classification.ts +23 -8
- package/src/router/route-snapshot.ts +14 -2
- package/src/router/router-context.ts +3 -1
- package/src/router/router-interfaces.ts +32 -1
- package/src/router/router-options.ts +30 -0
- package/src/router/segment-resolution/fresh.ts +39 -3
- package/src/router/segment-resolution/loader-cache.ts +93 -2
- package/src/router/segment-resolution/loader-mask.ts +60 -0
- package/src/router/segment-resolution/loader-snapshot.ts +259 -0
- package/src/router/segment-resolution/mask-nested.ts +83 -0
- package/src/router/segment-resolution/revalidation.ts +3 -0
- package/src/router/segment-resolution/view-transition-default.ts +35 -15
- package/src/router/substitute-pattern-params.ts +54 -35
- package/src/router/telemetry-otel.ts +6 -8
- package/src/router/telemetry.ts +9 -1
- package/src/router/tracing.ts +14 -5
- package/src/router/trie-matching.ts +19 -11
- package/src/router/url-params.ts +13 -0
- package/src/router.ts +47 -16
- package/src/rsc/full-payload.ts +70 -0
- package/src/rsc/handler.ts +60 -33
- package/src/rsc/manifest-init.ts +1 -1
- package/src/rsc/nonce.ts +10 -1
- package/src/rsc/progressive-enhancement.ts +61 -4
- package/src/rsc/redirect-guard.ts +2 -1
- package/src/rsc/rsc-rendering.ts +429 -37
- package/src/rsc/server-action.ts +25 -2
- package/src/rsc/shell-capture.ts +1190 -0
- package/src/rsc/shell-serve.ts +181 -0
- package/src/rsc/transition-gate.ts +89 -0
- package/src/rsc/types.ts +30 -0
- package/src/segment-loader-promise.ts +18 -0
- package/src/segment-system.tsx +149 -14
- package/src/server/context.ts +67 -9
- package/src/server/cookie-store.ts +73 -1
- package/src/server/loader-registry.ts +13 -1
- package/src/server/request-context.ts +169 -10
- package/src/ssr/index.tsx +462 -178
- package/src/ssr/inject-rsc-eager.ts +167 -0
- package/src/ssr/ssr-root.tsx +228 -0
- package/src/testing/collect-handle.ts +14 -8
- package/src/testing/dispatch.ts +152 -40
- package/src/testing/generated-routes.ts +27 -11
- package/src/testing/index.ts +6 -0
- package/src/testing/render-handler.ts +14 -0
- package/src/testing/render-route.tsx +13 -10
- package/src/testing/run-transition-when.ts +164 -0
- package/src/theme/ThemeProvider.tsx +36 -26
- package/src/types/handler-context.ts +1 -1
- package/src/types/index.ts +2 -0
- package/src/types/route-config.ts +19 -7
- package/src/types/segments.ts +100 -0
- package/src/urls/include-helper.ts +10 -8
- package/src/urls/include-provider.ts +71 -0
- package/src/urls/index.ts +1 -0
- package/src/urls/path-helper-types.ts +44 -12
- package/src/urls/path-helper.ts +5 -0
- package/src/urls/pattern-types.ts +36 -0
- package/src/urls/type-extraction.ts +43 -18
- package/src/urls/urls-function.ts +0 -1
- package/src/vercel/tracing.ts +7 -7
- package/src/vite/discovery/dev-prerender-cache.ts +117 -0
- package/src/vite/discovery/discover-routers.ts +1 -1
- package/src/vite/discovery/discovery-errors.ts +61 -0
- package/src/vite/index.ts +7 -0
- package/src/vite/inject-client-debug.ts +88 -0
- package/src/vite/plugins/vercel-output.ts +114 -25
- package/src/vite/plugins/version-injector.ts +22 -7
- package/src/vite/plugins/virtual-entries.ts +80 -22
- package/src/vite/rango.ts +29 -19
- package/src/vite/router-discovery.ts +171 -43
- package/src/vite/utils/prerender-utils.ts +17 -4
- package/src/vite/utils/shared-utils.ts +47 -0
- package/src/network-error-thrower.tsx +0 -18
|
@@ -32,6 +32,10 @@ Common reasons to migrate:
|
|
|
32
32
|
- **Build-time rendering** — `Static()` and `Prerender()` provide explicit
|
|
33
33
|
build-time rendering instead of mixing rendering and caching behind conventions.
|
|
34
34
|
See: `/prerender`
|
|
35
|
+
- **Partial prerendering, shipped** — the `ppr` path option caches a page's
|
|
36
|
+
HTML shell and resumes only the live holes on each request; loaders stay
|
|
37
|
+
fresh. The equivalent of Next's `experimental_ppr`, stable and per-route.
|
|
38
|
+
See: `/ppr`
|
|
35
39
|
- **Composable route tree** — layouts, includes, middleware, parallels, and
|
|
36
40
|
intercepts compose directly in the route definition.
|
|
37
41
|
See: `/composability`, `/parallel`, `/intercept`
|
|
@@ -43,6 +47,34 @@ Common reasons to migrate:
|
|
|
43
47
|
|
|
44
48
|
Work route-by-route, bottom-up. Start with leaf pages, then layouts, then middleware. Verify each route works before moving to the next.
|
|
45
49
|
|
|
50
|
+
## Replace imports, never shim Next
|
|
51
|
+
|
|
52
|
+
Do NOT create mock `next/*` modules, Vite aliases for `next/*`, or compatibility
|
|
53
|
+
wrapper components (a local `Link` that forwards `href` to `to`, a fake
|
|
54
|
+
`useRouter`, a stubbed `next/headers`). Shims freeze Next semantics into the
|
|
55
|
+
app, hide unsupported behavior until runtime, and keep `next` in the dependency
|
|
56
|
+
graph — the migration looks done but isn't. Replace every `next/*` import at
|
|
57
|
+
its call site with the real Rango API:
|
|
58
|
+
|
|
59
|
+
| Next import | Replace with |
|
|
60
|
+
| ---------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
|
|
61
|
+
| `next/link` `Link` | `Link` from `@rangojs/router/client` — rename `href` to `to` (see §6) |
|
|
62
|
+
| `next/navigation` `useRouter`, `usePathname`, `useSearchParams`, `useParams` | same names from `@rangojs/router/client` |
|
|
63
|
+
| `next/navigation` `redirect`, `notFound` | `redirect`, `notFound` from `@rangojs/router` |
|
|
64
|
+
| `next/headers` `cookies`, `headers` | `cookies()`, `headers()` from `@rangojs/router` (server-only) |
|
|
65
|
+
| `next/cache` `revalidateTag`, `unstable_cache` | `updateTag`/`revalidateTag` from `@rangojs/router`; `"use cache"` (see §3 and `/use-cache`) |
|
|
66
|
+
| `next/server` `NextResponse`, `NextRequest` | web-standard `Response`/`Request`; middleware via `router.use()` (see §4) |
|
|
67
|
+
| `next/image` `Image` | plain `<img>` (keep explicit `width`/`height`) or your CDN's image URL — no built-in optimizer |
|
|
68
|
+
| `next/font` | see `/fonts` |
|
|
69
|
+
| `next/script` `Script` | see `/scripts` |
|
|
70
|
+
| `next-themes` | `theme: true` in `createRouter` (see §10) |
|
|
71
|
+
|
|
72
|
+
If an import has no row here and no obvious Rango equivalent, stop and surface
|
|
73
|
+
it to the user — do not mock it to keep the build green.
|
|
74
|
+
|
|
75
|
+
Done means: `grep -rn "from ['\"]next" src/ app/` returns nothing, and `next`
|
|
76
|
+
is gone from `package.json`.
|
|
77
|
+
|
|
46
78
|
## 1. Project Setup
|
|
47
79
|
|
|
48
80
|
Replace Next.js tooling with Vite + Rango:
|
|
@@ -88,6 +120,21 @@ The Document component replaces `app/layout.tsx`'s `<html>` wrapper. See `/route
|
|
|
88
120
|
| `app/shop/[...path]/page.tsx` | `path("/shop/:path+", CatchAll, { name: "shopCatchAll" })` |
|
|
89
121
|
| `app/docs/[[...slug]]/page.tsx` | `path("/docs/:slug*", Docs, { name: "docs" })` |
|
|
90
122
|
|
|
123
|
+
The catch-all remainder is a single string at `ctx.params.<name>` with the `/`
|
|
124
|
+
separators preserved — split it to recover the array Next gives you:
|
|
125
|
+
|
|
126
|
+
```typescript
|
|
127
|
+
// app/docs/[[...slug]]/page.tsx -> params.slug is string[] | undefined in Next
|
|
128
|
+
path("/docs/:slug*", (ctx) => {
|
|
129
|
+
// "" for /docs, "a/b/c" for /docs/a/b/c
|
|
130
|
+
const slug = ctx.params.slug === "" ? [] : ctx.params.slug.split("/");
|
|
131
|
+
return <Docs slug={slug} />;
|
|
132
|
+
}, { name: "docs" });
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
`[...path]` (required, ≥1 segment) maps to `:path+`; `[[...slug]]` (optional,
|
|
136
|
+
matches the bare parent too) maps to `:slug*` — which binds `""` at `/docs`.
|
|
137
|
+
|
|
91
138
|
### Layouts
|
|
92
139
|
|
|
93
140
|
```typescript
|
|
@@ -153,6 +200,18 @@ export const marketingPatterns = urls(({ path }) => [
|
|
|
153
200
|
include("/", marketingPatterns, { name: "marketing" }),
|
|
154
201
|
```
|
|
155
202
|
|
|
203
|
+
Next.js code-splits each route segment automatically. Rango's eager `include()`
|
|
204
|
+
bundles the group into the entry chunk; to get Next-style per-section splitting,
|
|
205
|
+
pass an async provider so the group loads on the first request under its prefix:
|
|
206
|
+
|
|
207
|
+
```typescript
|
|
208
|
+
// urls/admin.tsx: `export default adminPatterns` — loads on first /admin request
|
|
209
|
+
include("/admin", () => import("./urls/admin"), { name: "admin" }),
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
Route types, `href()`, and prerender still see every route in the split group.
|
|
213
|
+
See `/composability`.
|
|
214
|
+
|
|
156
215
|
### Parallel routes
|
|
157
216
|
|
|
158
217
|
In Next.js, `@sidebar` and `@main` are both named slots. In Rango, the main content
|
|
@@ -193,9 +252,9 @@ The main content always goes through `<Outlet />` via the `path()` handler.
|
|
|
193
252
|
// Rango: explicit intercept in layout
|
|
194
253
|
layout(<ShopLayout />, () => [
|
|
195
254
|
path("/product/:id", ProductPage, { name: "product" }),
|
|
196
|
-
intercept("@modal", ".product", <ProductModal />,
|
|
197
|
-
when(
|
|
198
|
-
|
|
255
|
+
intercept("@modal", ".product", <ProductModal />, {
|
|
256
|
+
when: ({ from }) => from.pathname.startsWith("/shop"),
|
|
257
|
+
}),
|
|
199
258
|
])
|
|
200
259
|
```
|
|
201
260
|
|
|
@@ -288,14 +347,110 @@ export const Product = Passthrough(ProductDef, async (ctx) => {
|
|
|
288
347
|
Use `Passthrough()` whenever the Next.js route has `dynamicParams: true` (the
|
|
289
348
|
default) or serves an open-ended param space. See `/prerender` for full API.
|
|
290
349
|
|
|
350
|
+
### Rendering-mode segment config
|
|
351
|
+
|
|
352
|
+
Next.js route segment config maps onto Rango's explicit primitives:
|
|
353
|
+
|
|
354
|
+
| Next.js segment config | Rango |
|
|
355
|
+
| --------------------------------------------------- | ------------------------------------------------------------ |
|
|
356
|
+
| `dynamic = "force-static"` + `generateStaticParams` | `Static()` / `Prerender()` (see `/prerender`) |
|
|
357
|
+
| `revalidate = 60` (ISR) | `cache({ ttl: 60, swr: ... })` on the route (see `/caching`) |
|
|
358
|
+
| `dynamic = "force-dynamic"` | the default — routes are dynamic unless you cache them |
|
|
359
|
+
| `dynamicParams = true` | `Passthrough()` (above) |
|
|
360
|
+
| `experimental_ppr = true` | the `ppr` path option (below, and `/ppr`) |
|
|
361
|
+
|
|
362
|
+
### Partial prerendering → the `ppr` path option
|
|
363
|
+
|
|
364
|
+
Next.js PPR statically prerenders a shell at build time and streams the parts
|
|
365
|
+
inside `<Suspense>` at request time. Rango ships the same model as a path
|
|
366
|
+
option — the shell is captured at runtime into the app cache store and resumed
|
|
367
|
+
on later requests, with the holes rendered fresh per request:
|
|
368
|
+
|
|
369
|
+
```typescript
|
|
370
|
+
// Next.js: app/products/[id]/page.tsx
|
|
371
|
+
export const experimental_ppr = true;
|
|
372
|
+
export default async function Page({ params }) {
|
|
373
|
+
return (
|
|
374
|
+
<ProductShell>
|
|
375
|
+
<Suspense fallback={<PriceSkeleton />}>
|
|
376
|
+
<LivePrice id={params.id} />
|
|
377
|
+
</Suspense>
|
|
378
|
+
</ProductShell>
|
|
379
|
+
);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// Rango, step 1 — direct carry-over. Your Suspense tree IS the hole model:
|
|
383
|
+
// hand the un-awaited promise down, keep the boundary, add the ppr option.
|
|
384
|
+
// No loader, no loading(), no restructuring.
|
|
385
|
+
function ProductPage(ctx: HandlerContext) {
|
|
386
|
+
const price = fetchPrice(ctx.params.id); // pending promise — NOT awaited
|
|
387
|
+
return (
|
|
388
|
+
<ProductShell>
|
|
389
|
+
<Suspense fallback={<PriceSkeleton />}>
|
|
390
|
+
<LivePrice price={price} /> {/* use(price) inside */}
|
|
391
|
+
</Suspense>
|
|
392
|
+
</ProductShell>
|
|
393
|
+
);
|
|
394
|
+
}
|
|
395
|
+
path("/products/:id", ProductPage, {
|
|
396
|
+
name: "product",
|
|
397
|
+
ppr: { ttl: 600, swr: 120 }, // or ppr: true (default ttl 300s)
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
// Rango, step 2 (optional refinement) — promote the fetch to a loader for a
|
|
401
|
+
// GUARANTEED hole: loaders are masked at capture and fresh on every serve,
|
|
402
|
+
// even when the value resolves instantly (a raw promise that settles fast
|
|
403
|
+
// would bake into the shell). loading() is the loader's hole boundary.
|
|
404
|
+
path(
|
|
405
|
+
"/products/:id",
|
|
406
|
+
ProductPage,
|
|
407
|
+
{ name: "product", ppr: { ttl: 600, swr: 120 } },
|
|
408
|
+
() => [loader(LivePriceLoader), loading(<PriceSkeleton />)],
|
|
409
|
+
),
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
Differences that matter during migration:
|
|
413
|
+
|
|
414
|
+
- **The Suspense/promise model carries over.** As in Next, a still-pending
|
|
415
|
+
promise handed to a component that suspends under its own `<Suspense>`
|
|
416
|
+
postpones at capture and becomes a hole — existing Next PPR trees keep
|
|
417
|
+
working as-is, no `loading()` required. One container rule everywhere
|
|
418
|
+
(handlers, handles, loaders): awaited/settled data bakes into the shell; a
|
|
419
|
+
promise nested inside your data stays a live hole. For loaders, `loading()`
|
|
420
|
+
selects the lane: present = guaranteed live (masked at capture, fresh every
|
|
421
|
+
serve, immune to fast resolution — prefer it for per-request data); absent =
|
|
422
|
+
the bake lane (the settled container bakes and is snapshot-pinned per shell,
|
|
423
|
+
nested promises stay live). Identity reads (`cookies()`/`headers()`) where
|
|
424
|
+
the value would bake refuse the capture by construction.
|
|
425
|
+
- **Shell freshness is explicit.** Next's PPR shell is fixed until the next
|
|
426
|
+
build; Rango's has `ttl`/`swr`/`tags` per route, and `updateTag()` /
|
|
427
|
+
`revalidateTag()` drop the shell (`revalidate()` does not — it is a data
|
|
428
|
+
lever and never touches shell HTML).
|
|
429
|
+
- **`cookies()`/`headers()` in shell material THROW during capture** (in Next
|
|
430
|
+
they silently force dynamic rendering). Per-user reads must move behind a
|
|
431
|
+
`loading()` boundary (the live loader lane) or into a nested promise — the
|
|
432
|
+
refusal surfaces at migration time, which is the point.
|
|
433
|
+
- **A store is required.** PPR needs the app-level `createRouter({ cache })`
|
|
434
|
+
store to implement the shell family (`MemorySegmentCacheStore`,
|
|
435
|
+
`CFCacheStore`, `VercelCacheStore`). Without one the route quietly stays
|
|
436
|
+
fully dynamic with a once-per-key warning.
|
|
437
|
+
- **Middleware still guards every serve.** Auth middleware (global or route
|
|
438
|
+
DSL) runs before any shell byte on HIT and MISS alike — no Next-style "PPR
|
|
439
|
+
bypasses middleware" caveats to migrate around.
|
|
440
|
+
|
|
441
|
+
A route without `ppr` pays zero cost. See `/ppr` for the full execution matrix,
|
|
442
|
+
hole rules, and pitfalls.
|
|
443
|
+
|
|
291
444
|
### Revalidation: two distinct axes
|
|
292
445
|
|
|
293
446
|
Next.js conflates two things under "revalidation." Rango separates them — and
|
|
294
447
|
tag-based cache invalidation now maps directly.
|
|
295
448
|
|
|
296
449
|
**1. Cache invalidation (bust cached values) — direct equivalent.** Tag entries
|
|
297
|
-
with `cache({ tags })` or
|
|
298
|
-
`
|
|
450
|
+
with `cache({ tags })` or runtime `cacheTag(...tags)`. `cacheTag()` works inside a
|
|
451
|
+
`"use cache"` function (tags that entry) AND render-callable in a plain server
|
|
452
|
+
component (no `"use cache"` needed — it tags the document / PPR shell the component
|
|
453
|
+
renders into). Then invalidate by tag:
|
|
299
454
|
|
|
300
455
|
```typescript
|
|
301
456
|
// Next.js Rango
|
|
@@ -581,4 +736,10 @@ See `/theme` for full API including system detection and cookie persistence.
|
|
|
581
736
|
10. [ ] Migrate API routes to `path.json()` / `path.text()`
|
|
582
737
|
11. [ ] Update metadata to use `Meta` handle + `<MetaTags />` in document head
|
|
583
738
|
12. [ ] Replace `next-themes` with `theme: true` in createRouter (see `/theme`)
|
|
584
|
-
13. [ ]
|
|
739
|
+
13. [ ] Map rendering-mode segment config: `revalidate = N` → `cache({ ttl })`,
|
|
740
|
+
`force-static` → `Static()`/`Prerender()`, `experimental_ppr` → the
|
|
741
|
+
`ppr` path option (loader + `loading()` as the hole)
|
|
742
|
+
14. [ ] Run `npx rango generate src/` to generate route types
|
|
743
|
+
15. [ ] Verify no shims: `grep -rn "from ['\"]next" src/ app/` returns nothing,
|
|
744
|
+
no mock `next/*` modules or aliases exist, and `next` is out of
|
|
745
|
+
`package.json`
|