@rangojs/router 0.0.0-experimental.10 → 0.0.0-experimental.100
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 +9 -0
- package/README.md +1037 -4
- package/dist/bin/rango.js +1619 -157
- package/dist/vite/index.js +5762 -2301
- package/dist/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
- package/package.json +71 -63
- package/skills/breadcrumbs/SKILL.md +252 -0
- package/skills/cache-guide/SKILL.md +294 -0
- package/skills/caching/SKILL.md +93 -23
- package/skills/composability/SKILL.md +172 -0
- package/skills/debug-manifest/SKILL.md +12 -8
- package/skills/document-cache/SKILL.md +18 -16
- package/skills/fonts/SKILL.md +6 -4
- package/skills/handler-use/SKILL.md +364 -0
- package/skills/hooks/SKILL.md +367 -71
- package/skills/host-router/SKILL.md +218 -0
- package/skills/i18n/SKILL.md +276 -0
- package/skills/intercept/SKILL.md +176 -8
- package/skills/layout/SKILL.md +124 -3
- package/skills/links/SKILL.md +304 -25
- package/skills/loader/SKILL.md +474 -47
- package/skills/middleware/SKILL.md +207 -37
- package/skills/migrate-nextjs/SKILL.md +562 -0
- package/skills/migrate-react-router/SKILL.md +769 -0
- package/skills/mime-routes/SKILL.md +15 -11
- package/skills/parallel/SKILL.md +272 -1
- package/skills/prerender/SKILL.md +467 -65
- package/skills/rango/SKILL.md +89 -21
- package/skills/response-routes/SKILL.md +152 -91
- package/skills/route/SKILL.md +305 -14
- package/skills/router-setup/SKILL.md +210 -32
- package/skills/server-actions/SKILL.md +739 -0
- package/skills/streams-and-websockets/SKILL.md +283 -0
- package/skills/theme/SKILL.md +9 -8
- package/skills/typesafety/SKILL.md +333 -86
- package/skills/use-cache/SKILL.md +324 -0
- package/skills/view-transitions/SKILL.md +212 -0
- package/src/__internal.ts +102 -4
- package/src/bin/rango.ts +312 -15
- package/src/browser/action-coordinator.ts +97 -0
- package/src/browser/action-response-classifier.ts +99 -0
- package/src/browser/app-shell.ts +52 -0
- package/src/browser/app-version.ts +14 -0
- package/src/browser/event-controller.ts +136 -68
- package/src/browser/history-state.ts +80 -0
- package/src/browser/intercept-utils.ts +52 -0
- package/src/browser/link-interceptor.ts +24 -4
- package/src/browser/logging.ts +55 -0
- package/src/browser/merge-segment-loaders.ts +20 -12
- package/src/browser/navigation-bridge.ts +374 -561
- package/src/browser/navigation-client.ts +228 -70
- package/src/browser/navigation-store.ts +97 -55
- package/src/browser/navigation-transaction.ts +297 -0
- package/src/browser/network-error-handler.ts +61 -0
- package/src/browser/partial-update.ts +376 -315
- package/src/browser/prefetch/cache.ts +314 -0
- package/src/browser/prefetch/fetch.ts +282 -0
- package/src/browser/prefetch/observer.ts +65 -0
- package/src/browser/prefetch/policy.ts +48 -0
- package/src/browser/prefetch/queue.ts +191 -0
- package/src/browser/prefetch/resource-ready.ts +77 -0
- package/src/browser/rango-state.ts +152 -0
- package/src/browser/react/Link.tsx +255 -71
- package/src/browser/react/NavigationProvider.tsx +152 -24
- package/src/browser/react/context.ts +11 -0
- package/src/browser/react/filter-segment-order.ts +55 -0
- package/src/browser/react/index.ts +15 -12
- package/src/browser/react/location-state-shared.ts +95 -53
- package/src/browser/react/location-state.ts +60 -15
- package/src/browser/react/mount-context.ts +6 -1
- package/src/browser/react/nonce-context.ts +23 -0
- package/src/browser/react/shallow-equal.ts +27 -0
- package/src/browser/react/use-action.ts +29 -51
- package/src/browser/react/use-client-cache.ts +5 -3
- package/src/browser/react/use-handle.ts +30 -120
- package/src/browser/react/use-link-status.ts +6 -5
- package/src/browser/react/use-navigation.ts +44 -65
- package/src/browser/react/use-params.ts +78 -0
- package/src/browser/react/use-pathname.ts +47 -0
- package/src/browser/react/use-reverse.ts +99 -0
- package/src/browser/react/use-router.ts +83 -0
- package/src/browser/react/use-search-params.ts +56 -0
- package/src/browser/react/use-segments.ts +85 -99
- package/src/browser/response-adapter.ts +73 -0
- package/src/browser/rsc-router.tsx +246 -64
- package/src/browser/scroll-restoration.ts +127 -52
- package/src/browser/segment-reconciler.ts +243 -0
- package/src/browser/segment-structure-assert.ts +16 -0
- package/src/browser/server-action-bridge.ts +510 -603
- package/src/browser/shallow.ts +6 -1
- package/src/browser/types.ts +158 -48
- package/src/browser/validate-redirect-origin.ts +29 -0
- package/src/build/generate-manifest.ts +84 -23
- package/src/build/generate-route-types.ts +39 -828
- package/src/build/index.ts +4 -5
- package/src/build/route-trie.ts +85 -32
- package/src/build/route-types/ast-helpers.ts +25 -0
- package/src/build/route-types/ast-route-extraction.ts +98 -0
- package/src/build/route-types/codegen.ts +102 -0
- package/src/build/route-types/include-resolution.ts +418 -0
- package/src/build/route-types/param-extraction.ts +48 -0
- package/src/build/route-types/per-module-writer.ts +128 -0
- package/src/build/route-types/router-processing.ts +618 -0
- package/src/build/route-types/scan-filter.ts +85 -0
- package/src/build/runtime-discovery.ts +231 -0
- package/src/cache/background-task.ts +34 -0
- package/src/cache/cache-key-utils.ts +44 -0
- package/src/cache/cache-policy.ts +125 -0
- package/src/cache/cache-runtime.ts +342 -0
- package/src/cache/cache-scope.ts +167 -307
- package/src/cache/cf/cf-cache-store.ts +573 -21
- package/src/cache/cf/index.ts +13 -3
- package/src/cache/document-cache.ts +116 -77
- package/src/cache/handle-capture.ts +81 -0
- package/src/cache/handle-snapshot.ts +41 -0
- package/src/cache/index.ts +1 -15
- package/src/cache/memory-segment-store.ts +191 -13
- package/src/cache/profile-registry.ts +73 -0
- package/src/cache/read-through-swr.ts +134 -0
- package/src/cache/segment-codec.ts +256 -0
- package/src/cache/taint.ts +153 -0
- package/src/cache/types.ts +72 -122
- package/src/client.rsc.tsx +6 -1
- package/src/client.tsx +118 -302
- package/src/component-utils.ts +4 -4
- package/src/components/DefaultDocument.tsx +5 -1
- package/src/context-var.ts +156 -0
- package/src/debug.ts +19 -9
- package/src/errors.ts +77 -7
- package/src/handle.ts +55 -10
- package/src/handles/MetaTags.tsx +73 -20
- package/src/handles/breadcrumbs.ts +66 -0
- package/src/handles/index.ts +1 -0
- package/src/handles/meta.ts +30 -13
- package/src/host/cookie-handler.ts +21 -15
- package/src/host/errors.ts +8 -8
- package/src/host/index.ts +4 -7
- package/src/host/pattern-matcher.ts +27 -27
- package/src/host/router.ts +61 -39
- package/src/host/testing.ts +8 -8
- package/src/host/types.ts +15 -7
- package/src/host/utils.ts +1 -1
- package/src/href-client.ts +65 -45
- package/src/index.rsc.ts +138 -21
- package/src/index.ts +206 -51
- package/src/internal-debug.ts +11 -0
- package/src/loader.rsc.ts +25 -143
- package/src/loader.ts +27 -10
- package/src/network-error-thrower.tsx +3 -1
- package/src/outlet-context.ts +1 -1
- package/src/outlet-provider.tsx +45 -0
- package/src/prerender/param-hash.ts +4 -2
- package/src/prerender/store.ts +159 -13
- package/src/prerender.ts +397 -29
- package/src/response-utils.ts +28 -0
- package/src/reverse.ts +231 -121
- package/src/root-error-boundary.tsx +41 -29
- package/src/route-content-wrapper.tsx +7 -4
- package/src/route-definition/dsl-helpers.ts +1134 -0
- package/src/route-definition/helper-factories.ts +200 -0
- package/src/route-definition/helpers-types.ts +483 -0
- package/src/route-definition/index.ts +55 -0
- package/src/route-definition/redirect.ts +101 -0
- package/src/route-definition/resolve-handler-use.ts +155 -0
- package/src/route-definition.ts +1 -1431
- package/src/route-map-builder.ts +162 -123
- package/src/route-name.ts +53 -0
- package/src/route-types.ts +66 -9
- package/src/router/content-negotiation.ts +215 -0
- package/src/router/debug-manifest.ts +72 -0
- package/src/router/error-handling.ts +9 -9
- package/src/router/find-match.ts +160 -0
- package/src/router/handler-context.ts +418 -86
- package/src/router/intercept-resolution.ts +35 -20
- package/src/router/lazy-includes.ts +237 -0
- package/src/router/loader-resolution.ts +359 -128
- package/src/router/logging.ts +251 -0
- package/src/router/manifest.ts +98 -32
- package/src/router/match-api.ts +196 -261
- package/src/router/match-context.ts +4 -2
- package/src/router/match-handlers.ts +441 -0
- package/src/router/match-middleware/background-revalidation.ts +108 -93
- package/src/router/match-middleware/cache-lookup.ts +415 -86
- package/src/router/match-middleware/cache-store.ts +91 -29
- package/src/router/match-middleware/intercept-resolution.ts +48 -21
- package/src/router/match-middleware/segment-resolution.ts +73 -9
- package/src/router/match-pipelines.ts +10 -45
- package/src/router/match-result.ts +154 -35
- package/src/router/metrics.ts +240 -15
- package/src/router/middleware-cookies.ts +55 -0
- package/src/router/middleware-types.ts +209 -0
- package/src/router/middleware.ts +373 -371
- package/src/router/navigation-snapshot.ts +182 -0
- package/src/router/pattern-matching.ts +292 -52
- package/src/router/prerender-match.ts +502 -0
- package/src/router/preview-match.ts +98 -0
- package/src/router/request-classification.ts +310 -0
- package/src/router/revalidation.ts +152 -39
- package/src/router/route-snapshot.ts +245 -0
- package/src/router/router-context.ts +41 -21
- package/src/router/router-interfaces.ts +484 -0
- package/src/router/router-options.ts +618 -0
- package/src/router/router-registry.ts +24 -0
- package/src/router/segment-resolution/fresh.ts +756 -0
- package/src/router/segment-resolution/helpers.ts +268 -0
- package/src/router/segment-resolution/loader-cache.ts +199 -0
- package/src/router/segment-resolution/revalidation.ts +1407 -0
- package/src/router/segment-resolution/static-store.ts +67 -0
- package/src/router/segment-resolution.ts +21 -1315
- package/src/router/segment-wrappers.ts +291 -0
- package/src/router/substitute-pattern-params.ts +56 -0
- package/src/router/telemetry-otel.ts +299 -0
- package/src/router/telemetry.ts +300 -0
- package/src/router/timeout.ts +148 -0
- package/src/router/trie-matching.ts +111 -39
- package/src/router/types.ts +17 -9
- package/src/router/url-params.ts +49 -0
- package/src/router.ts +642 -2011
- package/src/rsc/handler-context.ts +45 -0
- package/src/rsc/handler.ts +864 -1114
- package/src/rsc/helpers.ts +181 -19
- package/src/rsc/index.ts +0 -20
- package/src/rsc/loader-fetch.ts +229 -0
- package/src/rsc/manifest-init.ts +90 -0
- package/src/rsc/nonce.ts +14 -0
- package/src/rsc/origin-guard.ts +141 -0
- package/src/rsc/progressive-enhancement.ts +395 -0
- package/src/rsc/response-error.ts +37 -0
- package/src/rsc/response-route-handler.ts +360 -0
- package/src/rsc/rsc-rendering.ts +256 -0
- package/src/rsc/runtime-warnings.ts +42 -0
- package/src/rsc/server-action.ts +360 -0
- package/src/rsc/ssr-setup.ts +128 -0
- package/src/rsc/types.ts +52 -11
- package/src/search-params.ts +230 -0
- package/src/segment-content-promise.ts +67 -0
- package/src/segment-loader-promise.ts +122 -0
- package/src/segment-system.tsx +187 -38
- package/src/server/context.ts +333 -59
- package/src/server/cookie-store.ts +190 -0
- package/src/server/fetchable-loader-store.ts +37 -0
- package/src/server/handle-store.ts +113 -15
- package/src/server/loader-registry.ts +24 -64
- package/src/server/request-context.ts +603 -109
- package/src/server.ts +35 -155
- package/src/ssr/index.tsx +107 -30
- package/src/static-handler.ts +126 -0
- package/src/theme/ThemeProvider.tsx +21 -15
- package/src/theme/ThemeScript.tsx +5 -5
- package/src/theme/constants.ts +5 -2
- package/src/theme/index.ts +4 -14
- package/src/theme/theme-context.ts +4 -30
- package/src/theme/theme-script.ts +21 -18
- package/src/types/boundaries.ts +158 -0
- package/src/types/cache-types.ts +198 -0
- package/src/types/error-types.ts +192 -0
- package/src/types/global-namespace.ts +100 -0
- package/src/types/handler-context.ts +764 -0
- package/src/types/index.ts +88 -0
- package/src/types/loader-types.ts +209 -0
- package/src/types/request-scope.ts +126 -0
- package/src/types/route-config.ts +170 -0
- package/src/types/route-entry.ts +120 -0
- package/src/types/segments.ts +167 -0
- package/src/types.ts +1 -1757
- package/src/urls/include-helper.ts +207 -0
- package/src/urls/index.ts +53 -0
- package/src/urls/path-helper-types.ts +372 -0
- package/src/urls/path-helper.ts +364 -0
- package/src/urls/pattern-types.ts +107 -0
- package/src/urls/response-types.ts +108 -0
- package/src/urls/type-extraction.ts +372 -0
- package/src/urls/urls-function.ts +98 -0
- package/src/urls.ts +1 -1282
- package/src/use-loader.tsx +161 -81
- package/src/vite/debug.ts +184 -0
- package/src/vite/discovery/bundle-postprocess.ts +181 -0
- package/src/vite/discovery/discover-routers.ts +376 -0
- package/src/vite/discovery/gate-state.ts +171 -0
- package/src/vite/discovery/prerender-collection.ts +486 -0
- package/src/vite/discovery/route-types-writer.ts +258 -0
- package/src/vite/discovery/self-gen-tracking.ts +73 -0
- package/src/vite/discovery/state.ts +117 -0
- package/src/vite/discovery/virtual-module-codegen.ts +203 -0
- package/src/vite/index.ts +15 -2063
- package/src/vite/plugin-types.ts +103 -0
- package/src/vite/plugins/cjs-to-esm.ts +98 -0
- package/src/vite/plugins/client-ref-dedup.ts +131 -0
- package/src/vite/plugins/client-ref-hashing.ts +117 -0
- package/src/vite/plugins/cloudflare-protocol-loader-hook.d.mts +23 -0
- package/src/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
- package/src/vite/plugins/cloudflare-protocol-stub.ts +214 -0
- package/src/vite/{expose-action-id.ts → plugins/expose-action-id.ts} +107 -64
- package/src/vite/plugins/expose-id-utils.ts +299 -0
- package/src/vite/plugins/expose-ids/export-analysis.ts +296 -0
- package/src/vite/plugins/expose-ids/handler-transform.ts +209 -0
- package/src/vite/plugins/expose-ids/loader-transform.ts +74 -0
- package/src/vite/plugins/expose-ids/router-transform.ts +127 -0
- package/src/vite/plugins/expose-ids/types.ts +45 -0
- package/src/vite/plugins/expose-internal-ids.ts +816 -0
- package/src/vite/plugins/performance-tracks.ts +96 -0
- package/src/vite/plugins/refresh-cmd.ts +127 -0
- package/src/vite/plugins/use-cache-transform.ts +336 -0
- package/src/vite/plugins/version-injector.ts +109 -0
- package/src/vite/plugins/version-plugin.ts +266 -0
- package/src/vite/{virtual-entries.ts → plugins/virtual-entries.ts} +23 -14
- package/src/vite/plugins/virtual-stub-plugin.ts +29 -0
- package/src/vite/rango.ts +497 -0
- package/src/vite/router-discovery.ts +1423 -0
- package/src/vite/utils/ast-handler-extract.ts +517 -0
- package/src/vite/utils/banner.ts +36 -0
- package/src/vite/utils/bundle-analysis.ts +137 -0
- package/src/vite/utils/manifest-utils.ts +70 -0
- package/src/vite/utils/package-resolution.ts +161 -0
- package/src/vite/utils/prerender-utils.ts +222 -0
- package/src/vite/utils/shared-utils.ts +170 -0
- package/CLAUDE.md +0 -43
- package/src/browser/lru-cache.ts +0 -69
- package/src/browser/request-controller.ts +0 -164
- package/src/cache/memory-store.ts +0 -253
- package/src/href-context.ts +0 -33
- package/src/router.gen.ts +0 -6
- package/src/urls.gen.ts +0 -8
- package/src/vite/expose-handle-id.ts +0 -209
- package/src/vite/expose-loader-id.ts +0 -426
- package/src/vite/expose-location-state-id.ts +0 -177
- package/src/vite/expose-prerender-handler-id.ts +0 -429
- package/src/vite/package-resolution.ts +0 -125
- /package/src/vite/{version.d.ts → plugins/version.d.ts} +0 -0
package/skills/links/SKILL.md
CHANGED
|
@@ -1,16 +1,25 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: links
|
|
3
|
-
description: URL generation with ctx.reverse (server), href (client), useHref (mounted), useMount, and scopedReverse
|
|
4
|
-
argument-hint: [href|useHref|useMount|scopedReverse]
|
|
3
|
+
description: URL generation with ctx.reverse (server default), href (client), useHref (mounted), useMount, useReverse, and scopedReverse
|
|
4
|
+
argument-hint: [ctx.reverse|href|useHref|useMount|useReverse|scopedReverse]
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Links & URL Generation
|
|
8
8
|
|
|
9
9
|
@rangojs/router provides different href APIs for server and client contexts.
|
|
10
10
|
|
|
11
|
+
**Default server API: `ctx.reverse()`.** Generate URLs from the handler context — it's typed, auto-fills mount params, and resolves local (`.name`) and absolute (`name.sub`) names.
|
|
12
|
+
|
|
13
|
+
**On the client, two patterns:**
|
|
14
|
+
|
|
15
|
+
1. **Receive URLs as props / loader data / action return.** The default. The server has the full route manifest and handler context — generate URLs there and hand strings to client components.
|
|
16
|
+
2. **`useReverse(routes)`.** Import a generated `routes` map from a `urls()` module's `.gen.ts` and call `reverse(".name", params?)`. Mount-aware via `useMount()`, auto-fills params from `useParams()`, fully typed from the imported map. Use this when a client component needs to generate URLs into a known module without round-tripping through the server.
|
|
17
|
+
|
|
18
|
+
`ctx.reverse()` itself is **server-only** — it depends on the full route manifest and handler context. Client components never import or call it.
|
|
19
|
+
|
|
11
20
|
## Server: ctx.reverse()
|
|
12
21
|
|
|
13
|
-
Available in route handlers via HandlerContext. Resolves named routes using the full route map.
|
|
22
|
+
Available in route handlers via HandlerContext. Resolves named routes using the full route map. This is the default way to generate URLs on the server.
|
|
14
23
|
|
|
15
24
|
```typescript
|
|
16
25
|
import { urls, scopedReverse } from "@rangojs/router";
|
|
@@ -24,27 +33,86 @@ export const shopPatterns = urls(({ path, layout }) => [
|
|
|
24
33
|
]);
|
|
25
34
|
```
|
|
26
35
|
|
|
27
|
-
### Resolution
|
|
36
|
+
### Resolution rules
|
|
28
37
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
3. **Local name** (`cart`) - resolved relative to current route's namespace
|
|
38
|
+
- **`.name`** — local route, resolved within the current `include()` scope
|
|
39
|
+
- **`name`** — global route, from the named-routes definition
|
|
32
40
|
|
|
33
41
|
```typescript
|
|
34
42
|
// Inside a handler within shopPatterns (mounted at /shop)
|
|
35
43
|
path("/product/:slug", (ctx) => {
|
|
36
|
-
ctx.reverse("cart"); // "/shop/cart" (local)
|
|
37
|
-
ctx.reverse("product", { slug: "widget" }); // "/shop/product/widget" (local + params)
|
|
38
|
-
ctx.reverse("blog.post", { slug: "hi" });
|
|
39
|
-
ctx.reverse("/about"); // "/about" (path-based)
|
|
44
|
+
ctx.reverse(".cart"); // "/shop/cart" (local)
|
|
45
|
+
ctx.reverse(".product", { slug: "widget" }); // "/shop/product/widget" (local + params)
|
|
46
|
+
ctx.reverse("blog.post", { slug: "hi" }); // "/blog/hi" (global)
|
|
40
47
|
|
|
41
48
|
return <ProductPage slug={ctx.params.slug} />;
|
|
42
49
|
}, { name: "product" })
|
|
43
50
|
```
|
|
44
51
|
|
|
52
|
+
### Local names (dot-prefixed)
|
|
53
|
+
|
|
54
|
+
Prefix a name with `.` to resolve it within the current `include()` scope. The route is looked up using the include's mount namespace.
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
// urls/magazine.tsx — mounted at include("/magazine", magazinePatterns, { name: "magazine" })
|
|
58
|
+
(ctx) => {
|
|
59
|
+
ctx.reverse(".article", { slug: "design" }); // "/magazine/design"
|
|
60
|
+
ctx.reverse(".author.posts", { authorSlug: "alice" }); // "/magazine/author/alice/posts"
|
|
61
|
+
ctx.reverse(".index"); // "/magazine"
|
|
62
|
+
ctx.reverse(".blog.index"); // THROWS — no magazine.blog.index
|
|
63
|
+
};
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Auto-fill of mount params
|
|
67
|
+
|
|
68
|
+
When routes are mounted via a parameterized `include()`, `ctx.reverse()` automatically fills mount params from `ctx.params`. Inner handlers don't need to pass params that are already known from the current URL match. Explicitly passed params override auto-filled values.
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
// urls/tenant.tsx — mounted at include("/tenant/:tenantId", tenantPatterns, { name: "tenant" })
|
|
72
|
+
export const tenantPatterns = urls(({ path }) => [
|
|
73
|
+
path("/", (ctx) => {
|
|
74
|
+
// tenantId is auto-filled from ctx.params — no need to pass it
|
|
75
|
+
ctx.reverse(".settings"); // "/tenant/acme/settings" (when visiting /tenant/acme)
|
|
76
|
+
ctx.reverse(".user", { userId: "u1" }); // "/tenant/acme/users/u1" (tenantId auto-filled, userId explicit)
|
|
77
|
+
ctx.reverse(".settings", { tenantId: "other" }); // "/tenant/other/settings" (explicit override)
|
|
78
|
+
|
|
79
|
+
// Global names also get auto-filled params
|
|
80
|
+
ctx.reverse("tenant.settings"); // "/tenant/acme/settings"
|
|
81
|
+
return <TenantIndex />;
|
|
82
|
+
}, { name: "index" }),
|
|
83
|
+
path("/settings", SettingsPage, { name: "settings" }),
|
|
84
|
+
path("/users/:userId", UserPage, { name: "user" }),
|
|
85
|
+
]);
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Auto-fill uses `{ ...ctx.params, ...hrefParams }` — current request params are defaults, explicit params win. Params not needed by the target route are silently ignored.
|
|
89
|
+
|
|
90
|
+
### Global names (unprefixed)
|
|
91
|
+
|
|
92
|
+
Unprefixed names resolve against the full named-routes map (the generated `router.named-routes.gen.ts`).
|
|
93
|
+
|
|
94
|
+
```typescript
|
|
95
|
+
(ctx) => {
|
|
96
|
+
ctx.reverse("magazine.index"); // "/magazine"
|
|
97
|
+
ctx.reverse("blog.post", { slug: "hello" }); // "/blog/hello"
|
|
98
|
+
};
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### reverse with search params
|
|
102
|
+
|
|
103
|
+
When a route has a `search` schema, pass a typed search object as the third argument:
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
path("/search", (ctx) => {
|
|
107
|
+
// Generates: /search?q=react&page=2
|
|
108
|
+
const url = ctx.reverse("search", {}, { q: "react", page: 2 });
|
|
109
|
+
return <Link to={url}>Search React</Link>;
|
|
110
|
+
}, { name: "search", search: { q: "string", page: "number?" } })
|
|
111
|
+
```
|
|
112
|
+
|
|
45
113
|
### scopedReverse() - type-safe ctx.reverse
|
|
46
114
|
|
|
47
|
-
Wraps `ctx.reverse` with local route type information for autocomplete and validation:
|
|
115
|
+
Wraps `ctx.reverse` with local route type information for autocomplete and validation. Runtime behavior is identical to `ctx.reverse` — `scopedReverse` is a type-only cast. The same dot-prefix rule applies: local names use `.name`, global names use `name.sub`.
|
|
48
116
|
|
|
49
117
|
```typescript
|
|
50
118
|
import { scopedReverse } from "@rangojs/router";
|
|
@@ -52,18 +120,83 @@ import { scopedReverse } from "@rangojs/router";
|
|
|
52
120
|
path("/product/:slug", (ctx) => {
|
|
53
121
|
const reverse = scopedReverse<typeof shopPatterns>(ctx.reverse);
|
|
54
122
|
|
|
55
|
-
reverse("cart"); //
|
|
56
|
-
reverse("product", { slug: "widget" }); //
|
|
57
|
-
reverse("blog.post");
|
|
58
|
-
reverse("/about"); // Path-based always allowed
|
|
123
|
+
reverse(".cart"); // Local name (dot-prefixed) — resolves in include scope
|
|
124
|
+
reverse(".product", { slug: "widget" }); // Local name with params
|
|
125
|
+
reverse("blog.post", { slug: "hi" }); // Global name (dotted) — full route map
|
|
59
126
|
|
|
60
127
|
return <ProductPage slug={ctx.params.slug} />;
|
|
61
128
|
}, { name: "product" })
|
|
62
129
|
```
|
|
63
130
|
|
|
131
|
+
`reverse()` does not accept raw path strings (`"/about"`). For static paths in client components, use `href("/about")`; on the server, look up the route by name.
|
|
132
|
+
|
|
133
|
+
## Client components: receive URLs as props
|
|
134
|
+
|
|
135
|
+
`ctx.reverse()` is not available inside `"use client"` modules — there is no handler context in the browser bundle. For in-module names, prefer `useReverse(routes)` (see below) and import the relevant `urls/*.gen.js`. For cross-module URLs or one-off names, generate the URL on the server and hand it to the client component using one of these three patterns:
|
|
136
|
+
|
|
137
|
+
1. Pass as a prop from a server component:
|
|
138
|
+
|
|
139
|
+
```tsx
|
|
140
|
+
// server
|
|
141
|
+
function BlogPostPage(ctx: HandlerContext) {
|
|
142
|
+
return <ShareButton url={ctx.reverse(".post", { slug: ctx.params.slug })} />;
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
```tsx
|
|
147
|
+
"use client";
|
|
148
|
+
|
|
149
|
+
export function ShareButton({ url }: { url: string }) {
|
|
150
|
+
return (
|
|
151
|
+
<button onClick={() => navigator.clipboard.writeText(url)}>Share</button>
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
2. Return from a loader (attached to the route via the DSL):
|
|
157
|
+
|
|
158
|
+
```tsx
|
|
159
|
+
// server — loaders/nav.ts
|
|
160
|
+
export const NavLoader = createLoader((ctx) => ({
|
|
161
|
+
home: ctx.reverse("home"),
|
|
162
|
+
blog: ctx.reverse("blog.index"),
|
|
163
|
+
}));
|
|
164
|
+
|
|
165
|
+
// server — urls.tsx: attach the loader so useLoader has data in context
|
|
166
|
+
const urlpatterns = urls(({ path, loader }) => [
|
|
167
|
+
path("/", HomePage, { name: "home" }, () => [loader(NavLoader)]),
|
|
168
|
+
]);
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
```tsx
|
|
172
|
+
"use client";
|
|
173
|
+
|
|
174
|
+
function Nav() {
|
|
175
|
+
const { data } = useLoader(NavLoader);
|
|
176
|
+
return <Link to={data.home}>Home</Link>;
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
`useLoader()` requires the loader to be attached to an active route. If you need on-demand fetching instead, use `useFetchLoader()`.
|
|
181
|
+
|
|
182
|
+
3. Return from a server action:
|
|
183
|
+
|
|
184
|
+
```tsx
|
|
185
|
+
"use server";
|
|
186
|
+
|
|
187
|
+
export async function getProductUrl(slug: string) {
|
|
188
|
+
const ctx = getRequestContext();
|
|
189
|
+
return ctx.reverse("product", { slug });
|
|
190
|
+
}
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
See `/server-actions` for the full action surface (`getRequestContext()` is the same context middleware and handlers use).
|
|
194
|
+
|
|
195
|
+
For static path strings (not named routes), client components can use `href()` — see below.
|
|
196
|
+
|
|
64
197
|
## Client: href()
|
|
65
198
|
|
|
66
|
-
Plain function for absolute path-based URLs. No hook needed - works anywhere.
|
|
199
|
+
Plain function for absolute path-based URLs. No hook needed - works anywhere in client components. `href()` validates paths at compile time, but does **not** resolve named routes — for named routes, use one of the patterns above.
|
|
67
200
|
|
|
68
201
|
```typescript
|
|
69
202
|
"use client";
|
|
@@ -80,7 +213,9 @@ function GlobalNav() {
|
|
|
80
213
|
}
|
|
81
214
|
```
|
|
82
215
|
|
|
83
|
-
`href()`
|
|
216
|
+
`href()` provides compile-time validation via `ValidPaths` type. Paths are validated against registered route patterns using `PatternToPath`.
|
|
217
|
+
|
|
218
|
+
`href()` is a raw path helper — it is **not** basename-aware. It returns the path as-is (or with the include mount prefix via `useHref()`). For basename-aware navigation, use `Link`, `useRouter().push()`, or `reverse()`, which auto-prefix root-relative paths with the router's basename.
|
|
84
219
|
|
|
85
220
|
## Client: useHref()
|
|
86
221
|
|
|
@@ -124,15 +259,159 @@ function MountInfo() {
|
|
|
124
259
|
|
|
125
260
|
`useMount()` reads from `MountContext`, which is automatically set by `include()` in the segment tree.
|
|
126
261
|
|
|
262
|
+
## Client: useReverse(routes)
|
|
263
|
+
|
|
264
|
+
Hook that returns a typed local reverse function for a `routes` map imported from a generated `.gen.ts` next to a `urls()` module. The route map is the **exposure boundary** — `useReverse` only knows about names in that map, never the full app manifest.
|
|
265
|
+
|
|
266
|
+
```tsx
|
|
267
|
+
"use client";
|
|
268
|
+
import { Link, useReverse } from "@rangojs/router/client";
|
|
269
|
+
import { routes as blogRoutes } from "../urls/blog.gen.js";
|
|
270
|
+
|
|
271
|
+
export function BlogNav() {
|
|
272
|
+
const reverse = useReverse(blogRoutes);
|
|
273
|
+
|
|
274
|
+
return (
|
|
275
|
+
<nav>
|
|
276
|
+
<Link to={reverse(".index")}>Blog</Link>
|
|
277
|
+
<Link to={reverse(".post", { postId: "hello" })}>Post</Link>
|
|
278
|
+
</nav>
|
|
279
|
+
);
|
|
280
|
+
}
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
### How it resolves
|
|
284
|
+
|
|
285
|
+
1. Strips the leading `.` and looks up the name in the imported `routes` map.
|
|
286
|
+
2. Joins the local pattern with the surrounding `useMount()` value — the include's URL pattern.
|
|
287
|
+
3. Substitutes params: explicit params from the call, then auto-filled from `useParams()` for anything still unresolved (mount params like `:tenantId` flow in this way).
|
|
288
|
+
4. Appends a query string if a search object is passed and the route has a `search` schema.
|
|
289
|
+
|
|
290
|
+
### Mount-relativity
|
|
291
|
+
|
|
292
|
+
Patterns in the generated `routes` map are **mount-relative** — they're the patterns as defined inside the `urls()` module, _not_ the full app paths. Mount-joining happens at runtime via `useMount()`, so the same component works under any include:
|
|
293
|
+
|
|
294
|
+
```typescript
|
|
295
|
+
// urls/blog.tsx
|
|
296
|
+
export const blogPatterns = urls(({ path }) => [
|
|
297
|
+
path("/", BlogIndex, { name: "index" }),
|
|
298
|
+
path("/:postId", BlogPost, { name: "post" }),
|
|
299
|
+
]);
|
|
300
|
+
|
|
301
|
+
// Generated urls/blog.gen.ts
|
|
302
|
+
// export const routes = { index: "/", post: "/:postId" } as const;
|
|
303
|
+
|
|
304
|
+
// urls.tsx — same module mounted twice
|
|
305
|
+
include("/news", blogPatterns, { name: "news" }), // <BlogNav> renders /news, /news/hello
|
|
306
|
+
include("/journal", blogPatterns, { name: "diary" }), // <BlogNav> renders /journal, /journal/hello
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
The `/` pattern under a non-root mount collapses cleanly: under `/news`, `reverse(".index")` returns `/news` (no trailing slash), matching `ctx.reverse(".index")` on the server.
|
|
310
|
+
|
|
311
|
+
### Auto-filled params (mount params)
|
|
312
|
+
|
|
313
|
+
When the include itself carries `:params`, those are auto-filled from `useParams()` so the caller doesn't have to thread them through:
|
|
314
|
+
|
|
315
|
+
```typescript
|
|
316
|
+
// urls.tsx
|
|
317
|
+
include("/tenant/:tenantId", clientReversePatterns, { name: "tenant" });
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
```tsx
|
|
321
|
+
// At /tenant/acme/posts/p1, useParams() = { tenantId: "acme", postId: "p1" }
|
|
322
|
+
const reverse = useReverse(clientReverseRoutes);
|
|
323
|
+
|
|
324
|
+
reverse(".index"); // "/tenant/acme"
|
|
325
|
+
reverse(".post", { postId: "p2" }); // "/tenant/acme/posts/p2" (tenantId auto-filled)
|
|
326
|
+
reverse(".post", { tenantId: "other", postId: "p2" }); // "/tenant/other/posts/p2" (explicit override)
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
Auto-fill follows soft navigation — when the matched route changes, `useReverse` re-renders with the new params.
|
|
330
|
+
|
|
331
|
+
### Search schemas
|
|
332
|
+
|
|
333
|
+
Routes declared with a `search` schema accept a typed search object as the third argument:
|
|
334
|
+
|
|
335
|
+
```typescript
|
|
336
|
+
// urls/blog.tsx
|
|
337
|
+
path("/search", SearchPage, {
|
|
338
|
+
name: "search",
|
|
339
|
+
search: { q: "string", page: "number?" },
|
|
340
|
+
}),
|
|
341
|
+
|
|
342
|
+
// Generated as: search: { path: "/search", search: { q: "string", page: "number?" } }
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
```tsx
|
|
346
|
+
const reverse = useReverse(blogRoutes);
|
|
347
|
+
reverse(".search", {}, { q: "hello world", page: 2 });
|
|
348
|
+
// "/news/search?q=hello%20world&page=2"
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
### Errors
|
|
352
|
+
|
|
353
|
+
- Unknown name: throws `Unknown local route: ".not-a-route"`.
|
|
354
|
+
- Missing required param: throws `Missing param "postId" for route ".detail"`.
|
|
355
|
+
|
|
356
|
+
Both happen synchronously during `reverse()` — wrap calls in try/catch (or an ErrorBoundary if the throw happens during render) when you need to surface them as UI.
|
|
357
|
+
|
|
358
|
+
### Names are dot-only on the client
|
|
359
|
+
|
|
360
|
+
`useReverse` accepts only `.name` (and dotted variants like `.nested.index`). There is no global namespace on the client — the import IS the scope. To link into a different module, import that module's `routes`:
|
|
361
|
+
|
|
362
|
+
```tsx
|
|
363
|
+
import { routes as blogRoutes } from "../urls/blog.gen.js";
|
|
364
|
+
import { routes as shopRoutes } from "../urls/shop.gen.js";
|
|
365
|
+
|
|
366
|
+
function CrossNav() {
|
|
367
|
+
const blog = useReverse(blogRoutes);
|
|
368
|
+
const shop = useReverse(shopRoutes);
|
|
369
|
+
return (
|
|
370
|
+
<nav>
|
|
371
|
+
<Link to={blog(".index")}>Blog</Link>
|
|
372
|
+
<Link to={shop(".cart")}>Cart</Link>
|
|
373
|
+
</nav>
|
|
374
|
+
);
|
|
375
|
+
}
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
### Codegen
|
|
379
|
+
|
|
380
|
+
Each `urls()` module gets a sibling `.gen.ts` with the local route names and patterns, produced by `rango generate`:
|
|
381
|
+
|
|
382
|
+
```bash
|
|
383
|
+
pnpm exec rango generate src/urls/blog.tsx
|
|
384
|
+
# or generate everything under a directory:
|
|
385
|
+
pnpm exec rango generate src/urls --static
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
Don't edit the file by hand — re-run codegen when patterns change.
|
|
389
|
+
|
|
390
|
+
**Today the Vite plugin only regenerates the router-level `*.named-routes.gen.ts`.** Per-module `urls/*.gen.ts` files are emitted only by the CLI (or `writePerModuleRouteTypesForFile` programmatically). Commit the generated files and re-run `rango generate` whenever a `urls()` module's `path()`/`include()` shape changes. A common workflow is to wire it into a `predev` script:
|
|
391
|
+
|
|
392
|
+
```jsonc
|
|
393
|
+
// package.json
|
|
394
|
+
{
|
|
395
|
+
"scripts": {
|
|
396
|
+
"predev": "rango generate src",
|
|
397
|
+
"dev": "vite",
|
|
398
|
+
},
|
|
399
|
+
}
|
|
400
|
+
```
|
|
401
|
+
|
|
127
402
|
## When to use what
|
|
128
403
|
|
|
129
|
-
| Context
|
|
130
|
-
|
|
131
|
-
| Server handler
|
|
132
|
-
| Server handler
|
|
133
|
-
| Client component | `
|
|
134
|
-
| Client component |
|
|
135
|
-
| Client component | `
|
|
404
|
+
| Context | API | Resolves | Use for |
|
|
405
|
+
| ---------------- | -------------------------------------------------- | ----------------------------------------- | ---------------------------------------------------------------- |
|
|
406
|
+
| Server handler | `ctx.reverse("name")` | Named routes (local + absolute) | **Default** server-side URL generation |
|
|
407
|
+
| Server handler | `scopedReverse<T>(ctx.reverse)` | Same, with type safety | Type-safe server URLs |
|
|
408
|
+
| Client component | `useReverse(routes)` | Local names from an imported `routes` map | Typed in-module URL generation without round-tripping the server |
|
|
409
|
+
| Client component | (URL passed as prop / loader data / action return) | Named routes | Cross-module URLs or one-off names you don't want to import |
|
|
410
|
+
| Client component | `href("/path")` | Absolute paths (static strings) | Static navigation where no named-route lookup is needed |
|
|
411
|
+
| Client component | `useHref()` | Mount-prefixed paths | Local navigation inside `include()` |
|
|
412
|
+
| Client component | `useMount()` | Raw mount path | Custom mount-aware logic |
|
|
413
|
+
|
|
414
|
+
> `ctx.reverse()` is server-only. On the client, either generate URLs on the server and pass them in, or import the `routes` map and use `useReverse(routes)` for in-module names.
|
|
136
415
|
|
|
137
416
|
## Complete example: mounted module
|
|
138
417
|
|