@rangojs/router 0.0.0-experimental.eb0645d3 → 0.0.0-experimental.f1468e3c
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 +8 -0
- package/README.md +126 -16
- package/dist/bin/rango.js +319 -95
- package/dist/testing/vitest.js +82 -0
- package/dist/vite/index.js +2724 -1053
- package/package.json +68 -14
- package/skills/api-client/SKILL.md +211 -0
- package/skills/breadcrumbs/SKILL.md +64 -2
- package/skills/bundle-analysis/SKILL.md +159 -0
- package/skills/cache-guide/SKILL.md +224 -32
- package/skills/caching/SKILL.md +279 -17
- package/skills/composability/SKILL.md +27 -3
- package/skills/css/SKILL.md +76 -0
- package/skills/debug-manifest/SKILL.md +4 -2
- package/skills/document-cache/SKILL.md +78 -55
- package/skills/handler-use/SKILL.md +11 -9
- package/skills/hooks/SKILL.md +243 -29
- package/skills/host-router/SKILL.md +83 -23
- package/skills/i18n/SKILL.md +276 -0
- package/skills/intercept/SKILL.md +68 -19
- package/skills/layout/SKILL.md +13 -9
- package/skills/links/SKILL.md +190 -23
- package/skills/loader/SKILL.md +235 -9
- package/skills/middleware/SKILL.md +18 -10
- package/skills/migrate-nextjs/SKILL.md +43 -19
- package/skills/migrate-react-router/SKILL.md +8 -2
- package/skills/mime-routes/SKILL.md +28 -1
- package/skills/observability/SKILL.md +172 -0
- package/skills/parallel/SKILL.md +18 -7
- package/skills/prerender/SKILL.md +65 -60
- package/skills/rango/SKILL.md +251 -24
- package/skills/react-compiler/SKILL.md +168 -0
- package/skills/response-routes/SKILL.md +115 -48
- package/skills/route/SKILL.md +46 -5
- package/skills/router-setup/SKILL.md +30 -8
- package/skills/scripts/SKILL.md +179 -0
- package/skills/server-actions/SKILL.md +775 -0
- package/skills/tailwind/SKILL.md +27 -3
- package/skills/testing/SKILL.md +130 -0
- package/skills/testing/bindings.md +103 -0
- package/skills/testing/cache-prerender.md +127 -0
- package/skills/testing/client-components.md +124 -0
- package/skills/testing/e2e-parity.md +125 -0
- package/skills/testing/flight.md +91 -0
- package/skills/testing/handles.md +129 -0
- package/skills/testing/loader.md +128 -0
- package/skills/testing/middleware.md +99 -0
- package/skills/testing/render-handler.md +122 -0
- package/skills/testing/response-routes.md +95 -0
- package/skills/testing/reverse-and-types.md +84 -0
- package/skills/testing/server-actions.md +107 -0
- package/skills/testing/server-tree.md +128 -0
- package/skills/testing/setup.md +123 -0
- package/skills/typesafety/SKILL.md +322 -29
- package/skills/use-cache/SKILL.md +57 -14
- package/skills/view-transitions/SKILL.md +337 -0
- package/src/__augment-tests__/augment.ts +81 -0
- package/src/__augment-tests__/augmented.check.ts +116 -0
- package/src/__internal.ts +0 -65
- package/src/browser/action-coordinator.ts +53 -36
- package/src/browser/action-fence.ts +47 -0
- package/src/browser/app-shell.ts +39 -0
- package/src/browser/connection-warmup.ts +134 -0
- package/src/browser/cookie-name.ts +140 -0
- package/src/browser/event-controller.ts +192 -150
- package/src/browser/history-state.ts +21 -0
- package/src/browser/index.ts +3 -3
- package/src/browser/invalidate-client-cache.ts +52 -0
- package/src/browser/navigation-bridge.ts +94 -25
- package/src/browser/navigation-client.ts +121 -84
- package/src/browser/navigation-store-handle.ts +38 -0
- package/src/browser/navigation-store.ts +115 -67
- package/src/browser/navigation-transaction.ts +9 -59
- package/src/browser/network-error-handler.ts +34 -7
- package/src/browser/partial-update.ts +147 -128
- package/src/browser/prefetch/cache.ts +107 -56
- package/src/browser/prefetch/fetch.ts +204 -34
- package/src/browser/prefetch/queue.ts +6 -3
- package/src/browser/rango-state.ts +158 -76
- package/src/browser/react/Link.tsx +30 -7
- package/src/browser/react/NavigationProvider.tsx +283 -118
- package/src/browser/react/ScrollRestoration.tsx +10 -6
- package/src/browser/react/deferred-handle-resolution.ts +75 -0
- package/src/browser/react/filter-segment-order.ts +66 -7
- package/src/browser/react/index.ts +0 -48
- package/src/browser/react/location-state-shared.ts +178 -8
- package/src/browser/react/location-state.ts +39 -14
- package/src/browser/react/use-action.ts +6 -15
- package/src/browser/react/use-handle.ts +17 -14
- package/src/browser/react/use-href.tsx +8 -1
- package/src/browser/react/use-link-status.ts +33 -8
- package/src/browser/react/use-navigation.ts +10 -5
- package/src/browser/react/use-params.ts +11 -11
- package/src/browser/react/use-reverse.ts +106 -0
- package/src/browser/react/use-router.ts +25 -3
- package/src/browser/react/use-search-params.ts +0 -5
- package/src/browser/react/use-segments.ts +11 -21
- package/src/browser/response-adapter.ts +99 -8
- package/src/browser/rsc-router.tsx +91 -24
- package/src/browser/scroll-restoration.ts +30 -17
- package/src/browser/segment-structure-assert.ts +2 -2
- package/src/browser/server-action-bridge.ts +214 -55
- package/src/browser/types.ts +80 -9
- package/src/browser/validate-redirect-origin.ts +43 -16
- package/src/build/collect-fallback-refs.ts +107 -0
- package/src/build/generate-manifest.ts +60 -35
- package/src/build/generate-route-types.ts +2 -1
- package/src/build/index.ts +8 -2
- package/src/build/prefix-tree-utils.ts +123 -0
- package/src/build/route-trie.ts +117 -14
- package/src/build/route-types/ast-route-extraction.ts +15 -8
- package/src/build/route-types/codegen.ts +16 -5
- package/src/build/route-types/include-resolution.ts +117 -23
- package/src/build/route-types/param-extraction.ts +6 -3
- package/src/build/route-types/per-module-writer.ts +22 -6
- package/src/build/route-types/router-processing.ts +55 -28
- package/src/build/route-types/scan-filter.ts +1 -1
- package/src/build/route-types/source-scan.ts +216 -0
- package/src/build/runtime-discovery.ts +9 -20
- package/src/cache/cache-error.ts +104 -0
- package/src/cache/cache-key-utils.ts +29 -13
- package/src/cache/cache-policy.ts +108 -34
- package/src/cache/cache-runtime.ts +224 -41
- package/src/cache/cache-scope.ts +188 -82
- package/src/cache/cache-tag.ts +103 -0
- package/src/cache/cf/cf-base64.ts +33 -0
- package/src/cache/cf/cf-cache-constants.ts +127 -0
- package/src/cache/cf/cf-cache-store.ts +1989 -378
- package/src/cache/cf/cf-cache-types.ts +349 -0
- package/src/cache/cf/cf-kv-utils.ts +46 -0
- package/src/cache/cf/cf-tag-marker-memo.ts +105 -0
- package/src/cache/cf/index.ts +6 -16
- package/src/cache/document-cache.ts +89 -21
- package/src/cache/handle-snapshot.ts +70 -0
- package/src/cache/index.ts +10 -20
- package/src/cache/memory-segment-store.ts +136 -37
- package/src/cache/profile-registry.ts +46 -31
- package/src/cache/read-through-swr.ts +56 -12
- package/src/cache/segment-codec.ts +9 -17
- package/src/cache/tag-invalidation.ts +230 -0
- package/src/cache/types.ts +37 -100
- package/src/client.rsc.tsx +44 -21
- package/src/client.tsx +36 -61
- package/src/cloudflare/index.ts +11 -0
- package/src/cloudflare/tracing.ts +109 -0
- package/src/component-utils.ts +19 -0
- package/src/components/DefaultDocument.tsx +8 -2
- package/src/context-var.ts +18 -6
- package/src/decode-loader-results.ts +52 -0
- package/src/defer.ts +196 -0
- package/src/deps/ssr.ts +0 -1
- package/src/encode-kv.ts +49 -0
- package/src/errors.ts +30 -4
- package/src/escape-script.ts +52 -0
- package/src/handle.ts +31 -23
- package/src/handles/MetaTags.tsx +62 -19
- package/src/handles/Scripts.tsx +183 -0
- package/src/handles/breadcrumbs.ts +37 -8
- package/src/handles/is-thenable.ts +19 -0
- package/src/handles/meta.ts +51 -40
- package/src/handles/script.ts +244 -0
- package/src/host/cookie-handler.ts +9 -60
- package/src/host/errors.ts +0 -24
- package/src/host/index.ts +8 -2
- package/src/host/pattern-matcher.ts +23 -52
- package/src/host/router.ts +107 -99
- package/src/host/testing.ts +40 -27
- package/src/host/types.ts +37 -4
- package/src/host/utils.ts +1 -1
- package/src/href-client.ts +137 -22
- package/src/index.rsc.ts +96 -12
- package/src/index.ts +94 -14
- package/src/internal-debug.ts +11 -10
- package/src/loader-store.ts +500 -0
- package/src/loader.rsc.ts +20 -13
- package/src/loader.ts +12 -11
- package/src/missing-id-error.ts +68 -0
- package/src/outlet-context.ts +1 -1
- package/src/outlet-provider.tsx +1 -5
- package/src/prerender/param-hash.ts +16 -16
- package/src/prerender/store.ts +32 -37
- package/src/prerender.ts +61 -6
- package/src/redirect-origin.ts +100 -0
- package/src/regex-escape.ts +8 -0
- package/src/render-error-thrower.tsx +20 -0
- package/src/response-utils.ts +34 -0
- package/src/reverse.ts +65 -40
- package/src/root-error-boundary.tsx +1 -19
- package/src/route-content-wrapper.tsx +19 -77
- package/src/route-definition/dsl-helpers.ts +304 -309
- package/src/route-definition/helper-factories.ts +28 -140
- package/src/route-definition/helpers-types.ts +82 -55
- package/src/route-definition/index.ts +1 -2
- package/src/route-definition/redirect.ts +44 -11
- package/src/route-definition/resolve-handler-use.ts +12 -1
- package/src/route-definition/use-item-types.ts +29 -0
- package/src/route-map-builder.ts +0 -16
- package/src/route-types.ts +19 -46
- package/src/router/basename.ts +14 -0
- package/src/router/content-negotiation.ts +73 -25
- package/src/router/error-handling.ts +45 -18
- package/src/router/find-match.ts +44 -23
- package/src/router/handler-context.ts +27 -43
- package/src/router/instrument.ts +350 -0
- package/src/router/intercept-resolution.ts +39 -20
- package/src/router/lazy-includes.ts +10 -47
- package/src/router/loader-resolution.ts +155 -72
- package/src/router/logging.ts +0 -6
- package/src/router/manifest.ts +18 -29
- package/src/router/match-api.ts +9 -24
- package/src/router/match-context.ts +0 -22
- package/src/router/match-handlers.ts +58 -58
- package/src/router/match-middleware/background-revalidation.ts +40 -24
- package/src/router/match-middleware/cache-lookup.ts +159 -285
- package/src/router/match-middleware/cache-store.ts +64 -52
- package/src/router/match-middleware/intercept-resolution.ts +0 -22
- package/src/router/match-middleware/segment-resolution.ts +0 -22
- package/src/router/match-pipelines.ts +1 -42
- package/src/router/match-result.ts +44 -74
- package/src/router/metrics.ts +0 -34
- package/src/router/middleware-types.ts +7 -134
- package/src/router/middleware.ts +247 -166
- package/src/router/navigation-snapshot.ts +0 -51
- package/src/router/params-util.ts +23 -0
- package/src/router/pattern-matching.ts +85 -94
- package/src/router/prefetch-cache-ttl.ts +51 -0
- package/src/router/prerender-match.ts +104 -65
- package/src/router/preview-match.ts +3 -1
- package/src/router/request-classification.ts +28 -62
- package/src/router/revalidation.ts +123 -73
- package/src/router/route-snapshot.ts +0 -1
- package/src/router/router-context.ts +3 -28
- package/src/router/router-interfaces.ts +83 -35
- package/src/router/router-options.ts +136 -5
- package/src/router/router-registry.ts +2 -5
- package/src/router/segment-resolution/fresh.ts +97 -84
- package/src/router/segment-resolution/helpers.ts +86 -6
- package/src/router/segment-resolution/loader-cache.ts +76 -39
- package/src/router/segment-resolution/revalidation.ts +272 -320
- package/src/router/segment-resolution/static-store.ts +19 -5
- package/src/router/segment-resolution/streamed-handler-telemetry.ts +52 -0
- package/src/router/segment-resolution/view-transition-default.ts +56 -0
- package/src/router/segment-resolution.ts +5 -1
- package/src/router/segment-wrappers.ts +6 -5
- package/src/router/state-cookie-name.ts +33 -0
- package/src/router/substitute-pattern-params.ts +56 -0
- package/src/router/telemetry-otel.ts +161 -199
- package/src/router/telemetry.ts +96 -19
- package/src/router/timeout.ts +0 -20
- package/src/router/tracing.ts +206 -0
- package/src/router/trie-matching.ts +162 -64
- package/src/router/types.ts +9 -63
- package/src/router/url-params.ts +0 -5
- package/src/router.ts +110 -55
- package/src/rsc/handler-context.ts +3 -2
- package/src/rsc/handler.ts +264 -220
- package/src/rsc/helpers.ts +100 -6
- package/src/rsc/index.ts +2 -5
- package/src/rsc/json-route-result.ts +38 -0
- package/src/rsc/loader-fetch.ts +114 -38
- package/src/rsc/manifest-init.ts +28 -41
- package/src/rsc/origin-guard.ts +39 -25
- package/src/rsc/progressive-enhancement.ts +117 -11
- package/src/rsc/redirect-guard.ts +99 -0
- package/src/rsc/response-cache-serve.ts +238 -0
- package/src/rsc/response-error.ts +79 -12
- package/src/rsc/response-route-handler.ts +88 -188
- package/src/rsc/rsc-rendering.ts +98 -76
- package/src/rsc/runtime-warnings.ts +23 -10
- package/src/rsc/server-action.ts +281 -117
- package/src/rsc/ssr-setup.ts +16 -0
- package/src/rsc/transition-gate.ts +89 -0
- package/src/rsc/types.ts +23 -5
- package/src/runtime-env.ts +18 -0
- package/src/search-params.ts +35 -30
- package/src/segment-loader-promise.ts +31 -4
- package/src/segment-system.tsx +254 -143
- package/src/serialize.ts +243 -0
- package/src/server/context.ts +163 -51
- package/src/server/cookie-parse.ts +32 -0
- package/src/server/cookie-store.ts +80 -5
- package/src/server/handle-store.ts +21 -38
- package/src/server/loader-registry.ts +33 -42
- package/src/server/request-context.ts +287 -178
- package/src/ssr/index.tsx +21 -16
- package/src/static-handler.ts +10 -13
- package/src/testing/cache-status.ts +162 -0
- package/src/testing/collect-handle.ts +40 -0
- package/src/testing/dispatch.ts +701 -0
- package/src/testing/dom.entry.ts +22 -0
- package/src/testing/e2e/fixture.ts +188 -0
- package/src/testing/e2e/index.ts +128 -0
- package/src/testing/e2e/matchers.ts +35 -0
- package/src/testing/e2e/page-helpers.ts +272 -0
- package/src/testing/e2e/parity.ts +387 -0
- package/src/testing/e2e/server.ts +195 -0
- package/src/testing/flight-matchers.ts +97 -0
- package/src/testing/flight-normalize.ts +11 -0
- package/src/testing/flight-runtime.d.ts +57 -0
- package/src/testing/flight-tree.ts +682 -0
- package/src/testing/flight.entry.ts +52 -0
- package/src/testing/flight.ts +257 -0
- package/src/testing/generated-routes.ts +183 -0
- package/src/testing/index.ts +105 -0
- package/src/testing/internal/context.ts +371 -0
- package/src/testing/internal/flight-client-globals.ts +30 -0
- package/src/testing/internal/seed-vars.ts +54 -0
- package/src/testing/render-handler.ts +357 -0
- package/src/testing/render-route.tsx +581 -0
- package/src/testing/run-loader.ts +385 -0
- package/src/testing/run-middleware.ts +205 -0
- package/src/testing/run-transition-when.ts +164 -0
- package/src/testing/vitest-stubs/cloudflare-email.ts +9 -0
- package/src/testing/vitest-stubs/cloudflare-workers.ts +21 -0
- package/src/testing/vitest-stubs/plugin-rsc.ts +16 -0
- package/src/testing/vitest-stubs/version.ts +5 -0
- package/src/testing/vitest.ts +305 -0
- package/src/theme/ThemeProvider.tsx +20 -58
- package/src/theme/ThemeScript.tsx +7 -9
- package/src/theme/constants.ts +52 -13
- package/src/theme/index.ts +0 -7
- package/src/theme/theme-context.ts +1 -5
- package/src/theme/theme-script.ts +22 -21
- package/src/theme/use-theme.ts +0 -3
- package/src/types/boundaries.ts +0 -35
- package/src/types/cache-types.ts +13 -4
- package/src/types/error-types.ts +30 -90
- package/src/types/global-namespace.ts +54 -41
- package/src/types/handler-context.ts +110 -62
- package/src/types/index.ts +3 -10
- package/src/types/loader-types.ts +11 -9
- package/src/types/request-scope.ts +112 -0
- package/src/types/route-config.ts +6 -50
- package/src/types/route-entry.ts +0 -6
- package/src/types/segments.ts +135 -14
- package/src/urls/include-helper.ts +9 -56
- package/src/urls/index.ts +1 -11
- package/src/urls/path-helper-types.ts +29 -12
- package/src/urls/path-helper.ts +17 -106
- package/src/urls/pattern-types.ts +36 -19
- package/src/urls/response-types.ts +22 -29
- package/src/urls/type-extraction.ts +58 -139
- package/src/urls/urls-function.ts +1 -19
- package/src/use-loader.tsx +292 -107
- package/src/vite/debug.ts +185 -0
- package/src/vite/discovery/bundle-postprocess.ts +8 -7
- package/src/vite/discovery/discover-routers.ts +126 -85
- package/src/vite/discovery/discovery-errors.ts +194 -0
- package/src/vite/discovery/gate-state.ts +171 -0
- package/src/vite/discovery/prerender-collection.ts +96 -68
- package/src/vite/discovery/route-types-writer.ts +40 -84
- package/src/vite/discovery/self-gen-tracking.ts +27 -1
- package/src/vite/discovery/state.ts +44 -0
- package/src/vite/discovery/virtual-module-codegen.ts +14 -34
- package/src/vite/index.ts +2 -0
- package/src/vite/inject-client-debug.ts +36 -0
- package/src/vite/plugin-types.ts +126 -8
- package/src/vite/plugins/cjs-to-esm.ts +16 -19
- package/src/vite/plugins/client-ref-dedup.ts +16 -11
- package/src/vite/plugins/client-ref-hashing.ts +28 -15
- package/src/vite/plugins/cloudflare-protocol-stub.ts +1 -21
- package/src/vite/plugins/expose-action-id.ts +48 -95
- package/src/vite/plugins/expose-id-utils.ts +88 -55
- package/src/vite/plugins/expose-ids/export-analysis.ts +101 -34
- package/src/vite/plugins/expose-ids/handler-transform.ts +11 -90
- package/src/vite/plugins/expose-ids/loader-transform.ts +14 -24
- package/src/vite/plugins/expose-ids/router-transform.ts +118 -29
- package/src/vite/plugins/expose-internal-ids.ts +505 -486
- package/src/vite/plugins/performance-tracks.ts +26 -25
- package/src/vite/plugins/refresh-cmd.ts +1 -1
- package/src/vite/plugins/use-cache-transform.ts +73 -83
- package/src/vite/plugins/version-injector.ts +40 -29
- package/src/vite/plugins/version-plugin.ts +37 -40
- package/src/vite/plugins/virtual-entries.ts +39 -25
- package/src/vite/rango.ts +109 -118
- package/src/vite/router-discovery.ts +718 -119
- package/src/vite/utils/ast-handler-extract.ts +26 -35
- package/src/vite/utils/banner.ts +1 -1
- package/src/vite/utils/bundle-analysis.ts +10 -15
- package/src/vite/utils/client-chunks.ts +184 -0
- package/src/vite/utils/directive-prologue.ts +40 -0
- package/src/vite/utils/forward-user-plugins.ts +171 -0
- package/src/vite/utils/manifest-utils.ts +4 -59
- package/src/vite/utils/package-resolution.ts +20 -52
- package/src/vite/utils/prerender-utils.ts +54 -39
- package/src/vite/utils/shared-utils.ts +90 -41
- package/src/browser/action-response-classifier.ts +0 -99
- package/src/browser/react/use-client-cache.ts +0 -58
- package/src/browser/shallow.ts +0 -40
- package/src/handles/index.ts +0 -7
- package/src/network-error-thrower.tsx +0 -23
- package/src/router/middleware-cookies.ts +0 -55
package/skills/layout/SKILL.md
CHANGED
|
@@ -8,9 +8,6 @@ argument-hint: [component]
|
|
|
8
8
|
|
|
9
9
|
Layouts wrap child routes and persist during navigation within their scope.
|
|
10
10
|
|
|
11
|
-
Canonical semantics reference:
|
|
12
|
-
[docs/execution-model.md](../../docs/internal/execution-model.md)
|
|
13
|
-
|
|
14
11
|
## Basic Layout
|
|
15
12
|
|
|
16
13
|
```typescript
|
|
@@ -118,6 +115,8 @@ function ShopLayout() {
|
|
|
118
115
|
}
|
|
119
116
|
```
|
|
120
117
|
|
|
118
|
+
A layout's `transition()` config wraps the content that flows through `<Outlet />` — not the layout chrome itself, and not sibling `<ParallelOutlet />` slots. Stacking transitions across nested layouts collapses around the deepest default outlet content. See [skills/view-transitions](../view-transitions/SKILL.md) for the full wrap rules and intercept-modal interaction.
|
|
119
|
+
|
|
121
120
|
## Named Outlets
|
|
122
121
|
|
|
123
122
|
For parallel routes, use named outlets:
|
|
@@ -203,8 +202,10 @@ layout(<ShopLayout />, () => [
|
|
|
203
202
|
])
|
|
204
203
|
|
|
205
204
|
// Or revalidate based on conditions
|
|
205
|
+
import * as CartActions from "./actions/cart";
|
|
206
|
+
|
|
206
207
|
layout(<CartLayout />, () => [
|
|
207
|
-
revalidate((
|
|
208
|
+
revalidate((ctx) => ctx.isAction(CartActions) || undefined),
|
|
208
209
|
|
|
209
210
|
path("/cart", CartPage, { name: "cart" }),
|
|
210
211
|
])
|
|
@@ -222,8 +223,9 @@ them on both producer and consumer segments:
|
|
|
222
223
|
|
|
223
224
|
```typescript
|
|
224
225
|
// revalidation-contracts.ts
|
|
225
|
-
|
|
226
|
-
|
|
226
|
+
import { addToCart } from "./actions/cart";
|
|
227
|
+
|
|
228
|
+
export const revalidateCartData = (ctx) => ctx.isAction(addToCart) || undefined;
|
|
227
229
|
```
|
|
228
230
|
|
|
229
231
|
```typescript
|
|
@@ -243,9 +245,10 @@ You can also package them as importable handoff helpers:
|
|
|
243
245
|
```typescript
|
|
244
246
|
// revalidation-contracts.ts
|
|
245
247
|
import { revalidate } from "@rangojs/router";
|
|
248
|
+
import * as AuthActions from "./actions/auth";
|
|
246
249
|
|
|
247
|
-
export const revalidateAuthData = (
|
|
248
|
-
|
|
250
|
+
export const revalidateAuthData = (ctx) =>
|
|
251
|
+
ctx.isAction(AuthActions) || undefined;
|
|
249
252
|
export const revalidateAuth = () => [revalidate(revalidateAuthData)];
|
|
250
253
|
```
|
|
251
254
|
|
|
@@ -263,6 +266,7 @@ layout(<ShellLayout />, () => [
|
|
|
263
266
|
```typescript
|
|
264
267
|
import { urls } from "@rangojs/router";
|
|
265
268
|
import { Outlet, ParallelOutlet } from "@rangojs/router/client";
|
|
269
|
+
import * as CartActions from "./actions/cart";
|
|
266
270
|
|
|
267
271
|
function ShopLayout() {
|
|
268
272
|
return (
|
|
@@ -292,7 +296,7 @@ export const shopPatterns = urls(({ path, layout, parallel, loader, revalidate }
|
|
|
292
296
|
}, () => [
|
|
293
297
|
// Layout loaders
|
|
294
298
|
loader(CartLoader, () => [
|
|
295
|
-
revalidate((
|
|
299
|
+
revalidate((ctx) => ctx.isAction(CartActions) || undefined),
|
|
296
300
|
]),
|
|
297
301
|
|
|
298
302
|
// Parallel routes
|
package/skills/links/SKILL.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: links
|
|
3
|
-
description: URL generation with ctx.reverse (server default), href (client), useHref (mounted), useMount, and scopedReverse
|
|
4
|
-
argument-hint: [ctx.reverse|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
|
|
@@ -10,7 +10,12 @@ argument-hint: [ctx.reverse|href|useHref|useMount|scopedReverse]
|
|
|
10
10
|
|
|
11
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
12
|
|
|
13
|
-
|
|
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?)` (the leading dot is optional). 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.
|
|
14
19
|
|
|
15
20
|
## Server: ctx.reverse()
|
|
16
21
|
|
|
@@ -127,26 +132,30 @@ path("/product/:slug", (ctx) => {
|
|
|
127
132
|
|
|
128
133
|
## Client components: receive URLs as props
|
|
129
134
|
|
|
130
|
-
`reverse()` is not available inside `"use client"` modules — there is no handler context
|
|
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:
|
|
131
136
|
|
|
132
|
-
|
|
137
|
+
1. Pass as a prop from a server component:
|
|
133
138
|
|
|
134
139
|
```tsx
|
|
135
|
-
// 1. Pass as a prop from a server component
|
|
136
140
|
// server
|
|
137
141
|
function BlogPostPage(ctx: HandlerContext) {
|
|
138
142
|
return <ShareButton url={ctx.reverse(".post", { slug: ctx.params.slug })} />;
|
|
139
143
|
}
|
|
144
|
+
```
|
|
140
145
|
|
|
141
|
-
|
|
146
|
+
```tsx
|
|
142
147
|
"use client";
|
|
148
|
+
|
|
143
149
|
export function ShareButton({ url }: { url: string }) {
|
|
144
|
-
return
|
|
150
|
+
return (
|
|
151
|
+
<button onClick={() => navigator.clipboard.writeText(url)}>Share</button>
|
|
152
|
+
);
|
|
145
153
|
}
|
|
146
154
|
```
|
|
147
155
|
|
|
156
|
+
2. Return from a loader (attached to the route via the DSL):
|
|
157
|
+
|
|
148
158
|
```tsx
|
|
149
|
-
// 2. Return from a loader (attached to the route via the DSL)
|
|
150
159
|
// server — loaders/nav.ts
|
|
151
160
|
export const NavLoader = createLoader((ctx) => ({
|
|
152
161
|
home: ctx.reverse("home"),
|
|
@@ -157,9 +166,11 @@ export const NavLoader = createLoader((ctx) => ({
|
|
|
157
166
|
const urlpatterns = urls(({ path, loader }) => [
|
|
158
167
|
path("/", HomePage, { name: "home" }, () => [loader(NavLoader)]),
|
|
159
168
|
]);
|
|
169
|
+
```
|
|
160
170
|
|
|
161
|
-
|
|
171
|
+
```tsx
|
|
162
172
|
"use client";
|
|
173
|
+
|
|
163
174
|
function Nav() {
|
|
164
175
|
const { data } = useLoader(NavLoader);
|
|
165
176
|
return <Link to={data.home}>Home</Link>;
|
|
@@ -168,16 +179,19 @@ function Nav() {
|
|
|
168
179
|
|
|
169
180
|
`useLoader()` requires the loader to be attached to an active route. If you need on-demand fetching instead, use `useFetchLoader()`.
|
|
170
181
|
|
|
182
|
+
3. Return from a server action:
|
|
183
|
+
|
|
171
184
|
```tsx
|
|
172
|
-
// 3. Return from a server action
|
|
173
|
-
// server
|
|
174
185
|
"use server";
|
|
186
|
+
|
|
175
187
|
export async function getProductUrl(slug: string) {
|
|
176
188
|
const ctx = getRequestContext();
|
|
177
189
|
return ctx.reverse("product", { slug });
|
|
178
190
|
}
|
|
179
191
|
```
|
|
180
192
|
|
|
193
|
+
See `/server-actions` for the full action surface (`getRequestContext()` is the same context middleware and handlers use).
|
|
194
|
+
|
|
181
195
|
For static path strings (not named routes), client components can use `href()` — see below.
|
|
182
196
|
|
|
183
197
|
## Client: href()
|
|
@@ -199,7 +213,17 @@ function GlobalNav() {
|
|
|
199
213
|
}
|
|
200
214
|
```
|
|
201
215
|
|
|
202
|
-
`href()` provides compile-time validation via `
|
|
216
|
+
`href()` provides compile-time validation via the `Rango.Path` type. Paths are validated against registered route patterns using `PatternToPath`.
|
|
217
|
+
|
|
218
|
+
When wrapping `href()`, type the wrapper's path parameter as `Rango.Path` so it
|
|
219
|
+
keeps the same generated-route validation. `Rango.Path` is ambient — no import,
|
|
220
|
+
just like `Rango.Env` / `Rango.Vars`:
|
|
221
|
+
|
|
222
|
+
```typescript
|
|
223
|
+
import { href } from "@rangojs/router/client";
|
|
224
|
+
|
|
225
|
+
export const appHref = (path: Rango.Path): string => href(path);
|
|
226
|
+
```
|
|
203
227
|
|
|
204
228
|
`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.
|
|
205
229
|
|
|
@@ -245,18 +269,161 @@ function MountInfo() {
|
|
|
245
269
|
|
|
246
270
|
`useMount()` reads from `MountContext`, which is automatically set by `include()` in the segment tree.
|
|
247
271
|
|
|
248
|
-
##
|
|
272
|
+
## Client: useReverse(routes)
|
|
273
|
+
|
|
274
|
+
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.
|
|
275
|
+
|
|
276
|
+
> **Which map?** `useReverse` accepts any routes map. Prefer the per-module `routes` (e.g. `urls/blog.gen.ts`): it gives **mount-aware** local `.name` reverse (auto-prefixes the `include()` mount) and only that module's names enter the client bundle. You _can_ instead pass `router.named-routes.gen.ts` (`NamedRoutes`) for **global** names (`blog.post`; the leading dot is optional) — it is a plain importable map and works on the client (it is **not** server-only) — but its paths are **absolute** while `useReverse` mount-prefixes, so it is correct only at the root mount (under a non-root mount it double-prefixes), and importing it pulls every route name and pattern in the app into the client bundle (a small names-to-paths map — not components or loaders), versus the per-module map which exposes only one module's names. So the per-module map is preferred for in-module links; the named-routes map is the escape hatch for global names.
|
|
277
|
+
|
|
278
|
+
```tsx
|
|
279
|
+
"use client";
|
|
280
|
+
import { Link, useReverse } from "@rangojs/router/client";
|
|
281
|
+
import { routes as blogRoutes } from "../urls/blog.gen.js";
|
|
282
|
+
|
|
283
|
+
export function BlogNav() {
|
|
284
|
+
const reverse = useReverse(blogRoutes);
|
|
285
|
+
|
|
286
|
+
return (
|
|
287
|
+
<nav>
|
|
288
|
+
<Link to={reverse("index")}>Blog</Link>
|
|
289
|
+
<Link to={reverse("post", { postId: "hello" })}>Post</Link>
|
|
290
|
+
</nav>
|
|
291
|
+
);
|
|
292
|
+
}
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
### How it resolves
|
|
296
|
+
|
|
297
|
+
1. Strips an optional leading `.` and looks up the name in the imported `routes` map.
|
|
298
|
+
2. Joins the local pattern with the surrounding `useMount()` value — the include's URL pattern.
|
|
299
|
+
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).
|
|
300
|
+
4. Appends a query string if a search object is passed and the route has a `search` schema.
|
|
301
|
+
|
|
302
|
+
### Mount-relativity
|
|
303
|
+
|
|
304
|
+
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:
|
|
305
|
+
|
|
306
|
+
```typescript
|
|
307
|
+
// urls/blog.tsx
|
|
308
|
+
export const blogPatterns = urls(({ path }) => [
|
|
309
|
+
path("/", BlogIndex, { name: "index" }),
|
|
310
|
+
path("/:postId", BlogPost, { name: "post" }),
|
|
311
|
+
]);
|
|
312
|
+
|
|
313
|
+
// Generated urls/blog.gen.ts
|
|
314
|
+
// export const routes = { index: "/", post: "/:postId" } as const;
|
|
315
|
+
|
|
316
|
+
// urls.tsx — same module mounted twice
|
|
317
|
+
include("/news", blogPatterns, { name: "news" }), // <BlogNav> renders /news, /news/hello
|
|
318
|
+
include("/journal", blogPatterns, { name: "diary" }), // <BlogNav> renders /journal, /journal/hello
|
|
319
|
+
```
|
|
249
320
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
321
|
+
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.
|
|
322
|
+
|
|
323
|
+
### Auto-filled params (mount params)
|
|
324
|
+
|
|
325
|
+
When the include itself carries `:params`, those are auto-filled from `useParams()` so the caller doesn't have to thread them through:
|
|
326
|
+
|
|
327
|
+
```typescript
|
|
328
|
+
// urls.tsx
|
|
329
|
+
include("/tenant/:tenantId", clientReversePatterns, { name: "tenant" });
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
```tsx
|
|
333
|
+
// At /tenant/acme/posts/p1, useParams() = { tenantId: "acme", postId: "p1" }
|
|
334
|
+
const reverse = useReverse(clientReverseRoutes);
|
|
335
|
+
|
|
336
|
+
reverse(".index"); // "/tenant/acme"
|
|
337
|
+
reverse(".post", { postId: "p2" }); // "/tenant/acme/posts/p2" (tenantId auto-filled)
|
|
338
|
+
reverse(".post", { tenantId: "other", postId: "p2" }); // "/tenant/other/posts/p2" (explicit override)
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
Auto-fill follows soft navigation — when the matched route changes, `useReverse` re-renders with the new params.
|
|
342
|
+
|
|
343
|
+
### Search schemas
|
|
344
|
+
|
|
345
|
+
Routes declared with a `search` schema accept a typed search object as the third argument:
|
|
346
|
+
|
|
347
|
+
```typescript
|
|
348
|
+
// urls/blog.tsx
|
|
349
|
+
path("/search", SearchPage, {
|
|
350
|
+
name: "search",
|
|
351
|
+
search: { q: "string", page: "number?" },
|
|
352
|
+
}),
|
|
353
|
+
|
|
354
|
+
// Generated as: search: { path: "/search", search: { q: "string", page: "number?" } }
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
```tsx
|
|
358
|
+
const reverse = useReverse(blogRoutes);
|
|
359
|
+
reverse(".search", {}, { q: "hello world", page: 2 });
|
|
360
|
+
// "/news/search?q=hello%20world&page=2"
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
### Errors
|
|
364
|
+
|
|
365
|
+
- Unknown name: throws `Unknown route: ".not-a-route"`.
|
|
366
|
+
- Missing required param: throws `Missing param "postId" for route ".detail"`.
|
|
367
|
+
|
|
368
|
+
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.
|
|
369
|
+
|
|
370
|
+
### The leading dot is optional
|
|
371
|
+
|
|
372
|
+
`reverse("post")` and `reverse(".post")` resolve **identically** — the leading dot is cosmetic. The map you import IS the scope, so there is no separate global namespace to disambiguate and the dot carries no meaning; it exists only as a readability convention and for parity with `ctx.reverse(".name")` on the server. To link into a different module, import that module's `routes`:
|
|
373
|
+
|
|
374
|
+
```tsx
|
|
375
|
+
import { routes as blogRoutes } from "../urls/blog.gen.js";
|
|
376
|
+
import { routes as shopRoutes } from "../urls/shop.gen.js";
|
|
377
|
+
|
|
378
|
+
function CrossNav() {
|
|
379
|
+
const blog = useReverse(blogRoutes);
|
|
380
|
+
const shop = useReverse(shopRoutes);
|
|
381
|
+
return (
|
|
382
|
+
<nav>
|
|
383
|
+
<Link to={blog("index")}>Blog</Link>
|
|
384
|
+
<Link to={shop("cart")}>Cart</Link>
|
|
385
|
+
</nav>
|
|
386
|
+
);
|
|
387
|
+
}
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
### Codegen
|
|
391
|
+
|
|
392
|
+
Each `urls()` module gets a sibling `.gen.ts` with the local route names and patterns, produced by `rango generate`:
|
|
393
|
+
|
|
394
|
+
```bash
|
|
395
|
+
pnpm exec rango generate src/urls/blog.tsx
|
|
396
|
+
# or generate everything under a directory:
|
|
397
|
+
pnpm exec rango generate src/urls --static
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
Don't edit the file by hand — re-run codegen when patterns change.
|
|
401
|
+
|
|
402
|
+
**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:
|
|
403
|
+
|
|
404
|
+
```jsonc
|
|
405
|
+
// package.json
|
|
406
|
+
{
|
|
407
|
+
"scripts": {
|
|
408
|
+
"predev": "rango generate src",
|
|
409
|
+
"dev": "vite",
|
|
410
|
+
},
|
|
411
|
+
}
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
## When to use what
|
|
258
415
|
|
|
259
|
-
|
|
416
|
+
| Context | API | Resolves | Use for |
|
|
417
|
+
| ---------------- | -------------------------------------------------- | ----------------------------------------- | ---------------------------------------------------------------- |
|
|
418
|
+
| Server handler | `ctx.reverse("name")` | Named routes (local + absolute) | **Default** server-side URL generation |
|
|
419
|
+
| Server handler | `scopedReverse<T>(ctx.reverse)` | Same, with type safety | Type-safe server URLs |
|
|
420
|
+
| Client component | `useReverse(routes)` | Local names from an imported `routes` map | Typed in-module URL generation without round-tripping the server |
|
|
421
|
+
| 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 |
|
|
422
|
+
| Client component | `href("/path")` | Absolute paths (static strings) | Static navigation where no named-route lookup is needed |
|
|
423
|
+
| Client component | `useHref()` | Mount-prefixed paths | Local navigation inside `include()` |
|
|
424
|
+
| Client component | `useMount()` | Raw mount path | Custom mount-aware logic |
|
|
425
|
+
|
|
426
|
+
> `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.
|
|
260
427
|
|
|
261
428
|
## Complete example: mounted module
|
|
262
429
|
|