@rangojs/router 0.0.0-experimental.7 → 0.0.0-experimental.71
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 +942 -4
- package/dist/bin/rango.js +1689 -0
- package/dist/vite/index.js +4951 -930
- package/package.json +70 -60
- package/skills/breadcrumbs/SKILL.md +250 -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 +167 -0
- package/skills/hooks/SKILL.md +334 -72
- package/skills/host-router/SKILL.md +218 -0
- package/skills/intercept/SKILL.md +131 -8
- package/skills/layout/SKILL.md +100 -3
- package/skills/links/SKILL.md +92 -31
- package/skills/loader/SKILL.md +404 -44
- package/skills/middleware/SKILL.md +173 -34
- package/skills/mime-routes/SKILL.md +128 -0
- package/skills/parallel/SKILL.md +204 -1
- package/skills/prerender/SKILL.md +685 -0
- package/skills/rango/SKILL.md +85 -16
- package/skills/response-routes/SKILL.md +411 -0
- package/skills/route/SKILL.md +257 -14
- package/skills/router-setup/SKILL.md +210 -32
- package/skills/tailwind/SKILL.md +129 -0
- package/skills/theme/SKILL.md +9 -8
- package/skills/typesafety/SKILL.md +328 -89
- package/skills/use-cache/SKILL.md +324 -0
- package/src/__internal.ts +102 -4
- package/src/bin/rango.ts +321 -0
- package/src/browser/action-coordinator.ts +97 -0
- package/src/browser/action-response-classifier.ts +99 -0
- package/src/browser/app-version.ts +14 -0
- package/src/browser/event-controller.ts +92 -64
- 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 +296 -558
- package/src/browser/navigation-client.ts +179 -69
- package/src/browser/navigation-store.ts +73 -55
- package/src/browser/navigation-transaction.ts +297 -0
- package/src/browser/network-error-handler.ts +61 -0
- package/src/browser/partial-update.ts +328 -313
- package/src/browser/prefetch/cache.ts +206 -0
- package/src/browser/prefetch/fetch.ts +150 -0
- package/src/browser/prefetch/observer.ts +65 -0
- package/src/browser/prefetch/policy.ts +48 -0
- package/src/browser/prefetch/queue.ts +160 -0
- package/src/browser/prefetch/resource-ready.ts +77 -0
- package/src/browser/rango-state.ts +112 -0
- package/src/browser/react/Link.tsx +230 -74
- package/src/browser/react/NavigationProvider.tsx +87 -11
- package/src/browser/react/context.ts +11 -0
- package/src/browser/react/filter-segment-order.ts +11 -0
- package/src/browser/react/index.ts +12 -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 -126
- package/src/browser/react/use-href.tsx +2 -2
- package/src/browser/react/use-link-status.ts +6 -5
- package/src/browser/react/use-navigation.ts +22 -63
- package/src/browser/react/use-params.ts +65 -0
- package/src/browser/react/use-pathname.ts +47 -0
- package/src/browser/react/use-router.ts +76 -0
- package/src/browser/react/use-search-params.ts +56 -0
- package/src/browser/react/use-segments.ts +80 -97
- package/src/browser/response-adapter.ts +73 -0
- package/src/browser/rsc-router.tsx +214 -58
- package/src/browser/scroll-restoration.ts +127 -52
- package/src/browser/segment-reconciler.ts +221 -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 +141 -48
- package/src/browser/validate-redirect-origin.ts +29 -0
- package/src/build/generate-manifest.ts +235 -24
- package/src/build/generate-route-types.ts +39 -0
- package/src/build/index.ts +13 -0
- package/src/build/route-trie.ts +265 -0
- 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 -309
- package/src/cache/cf/cf-cache-store.ts +571 -17
- 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 +3 -1
- package/src/client.tsx +105 -179
- 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 +108 -2
- package/src/handle.ts +55 -29
- 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 +119 -29
- package/src/index.rsc.ts +155 -19
- package/src/index.ts +223 -30
- package/src/internal-debug.ts +11 -0
- package/src/loader.rsc.ts +26 -157
- package/src/loader.ts +27 -10
- package/src/network-error-thrower.tsx +3 -1
- package/src/outlet-provider.tsx +45 -0
- package/src/prerender/param-hash.ts +37 -0
- package/src/prerender/store.ts +186 -0
- package/src/prerender.ts +524 -0
- package/src/reverse.ts +351 -0
- package/src/root-error-boundary.tsx +41 -29
- package/src/route-content-wrapper.tsx +7 -4
- package/src/route-definition/dsl-helpers.ts +982 -0
- package/src/route-definition/helper-factories.ts +200 -0
- package/src/route-definition/helpers-types.ts +434 -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 +149 -0
- package/src/route-definition.ts +1 -1428
- package/src/route-map-builder.ts +217 -123
- package/src/route-name.ts +53 -0
- package/src/route-types.ts +70 -8
- 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 +435 -86
- package/src/router/intercept-resolution.ts +402 -0
- package/src/router/lazy-includes.ts +237 -0
- package/src/router/loader-resolution.ts +356 -128
- package/src/router/logging.ts +251 -0
- package/src/router/manifest.ts +154 -35
- package/src/router/match-api.ts +555 -0
- package/src/router/match-context.ts +5 -3
- package/src/router/match-handlers.ts +440 -0
- package/src/router/match-middleware/background-revalidation.ts +108 -93
- package/src/router/match-middleware/cache-lookup.ts +459 -10
- package/src/router/match-middleware/cache-store.ts +98 -26
- package/src/router/match-middleware/intercept-resolution.ts +57 -17
- package/src/router/match-middleware/segment-resolution.ts +80 -6
- package/src/router/match-pipelines.ts +10 -45
- package/src/router/match-result.ts +135 -35
- package/src/router/metrics.ts +240 -15
- package/src/router/middleware-cookies.ts +55 -0
- package/src/router/middleware-types.ts +220 -0
- package/src/router/middleware.ts +324 -369
- package/src/router/navigation-snapshot.ts +182 -0
- package/src/router/pattern-matching.ts +211 -43
- 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 +137 -38
- 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 +748 -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 +1379 -0
- package/src/router/segment-resolution/static-store.ts +67 -0
- package/src/router/segment-resolution.ts +21 -0
- package/src/router/segment-wrappers.ts +291 -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 +239 -0
- package/src/router/types.ts +78 -3
- package/src/router.ts +740 -4252
- package/src/rsc/handler-context.ts +45 -0
- package/src/rsc/handler.ts +907 -797
- package/src/rsc/helpers.ts +140 -6
- 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 +391 -0
- package/src/rsc/response-error.ts +37 -0
- package/src/rsc/response-route-handler.ts +347 -0
- package/src/rsc/rsc-rendering.ts +246 -0
- package/src/rsc/runtime-warnings.ts +42 -0
- package/src/rsc/server-action.ts +356 -0
- package/src/rsc/ssr-setup.ts +128 -0
- package/src/rsc/types.ts +46 -11
- package/src/search-params.ts +230 -0
- package/src/segment-system.tsx +165 -17
- package/src/server/context.ts +315 -58
- 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 +607 -81
- package/src/server.ts +35 -130
- package/src/ssr/index.tsx +103 -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 +791 -0
- package/src/types/index.ts +88 -0
- package/src/types/loader-types.ts +210 -0
- package/src/types/route-config.ts +170 -0
- package/src/types/route-entry.ts +109 -0
- package/src/types/segments.ts +151 -0
- package/src/types.ts +1 -1623
- package/src/urls/include-helper.ts +197 -0
- package/src/urls/index.ts +53 -0
- package/src/urls/path-helper-types.ts +346 -0
- package/src/urls/path-helper.ts +364 -0
- package/src/urls/pattern-types.ts +107 -0
- package/src/urls/response-types.ts +116 -0
- package/src/urls/type-extraction.ts +372 -0
- package/src/urls/urls-function.ts +98 -0
- package/src/urls.ts +1 -802
- package/src/use-loader.tsx +161 -81
- package/src/vite/discovery/bundle-postprocess.ts +181 -0
- package/src/vite/discovery/discover-routers.ts +348 -0
- package/src/vite/discovery/prerender-collection.ts +439 -0
- package/src/vite/discovery/route-types-writer.ts +258 -0
- package/src/vite/discovery/self-gen-tracking.ts +47 -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 -1129
- package/src/vite/plugin-types.ts +103 -0
- package/src/vite/plugins/cjs-to-esm.ts +93 -0
- package/src/vite/plugins/client-ref-dedup.ts +115 -0
- package/src/vite/plugins/client-ref-hashing.ts +105 -0
- package/src/vite/{expose-action-id.ts → plugins/expose-action-id.ts} +72 -53
- 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 +110 -0
- package/src/vite/plugins/expose-ids/types.ts +45 -0
- package/src/vite/plugins/expose-internal-ids.ts +786 -0
- package/src/vite/plugins/performance-tracks.ts +88 -0
- package/src/vite/plugins/refresh-cmd.ts +127 -0
- package/src/vite/plugins/use-cache-transform.ts +323 -0
- package/src/vite/plugins/version-injector.ts +83 -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 +462 -0
- package/src/vite/router-discovery.ts +918 -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/{package-resolution.ts → utils/package-resolution.ts} +25 -29
- package/src/vite/utils/prerender-utils.ts +207 -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/href.ts +0 -255
- package/src/server/route-manifest-cache.ts +0 -173
- 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/{version.d.ts → plugins/version.d.ts} +0 -0
package/src/client.tsx
CHANGED
|
@@ -13,13 +13,14 @@ import {
|
|
|
13
13
|
type ClientErrorBoundaryFallbackProps,
|
|
14
14
|
type ErrorInfo,
|
|
15
15
|
type LoaderDefinition,
|
|
16
|
-
type LoaderFn,
|
|
17
16
|
type ResolvedSegment,
|
|
18
17
|
} from "./types";
|
|
19
18
|
import {
|
|
20
19
|
RouteContentWrapper,
|
|
21
20
|
LoaderBoundary,
|
|
22
21
|
} from "./route-content-wrapper.js";
|
|
22
|
+
import { OutletProvider } from "./outlet-provider.js";
|
|
23
|
+
import { MountContextProvider } from "./browser/react/mount-context.js";
|
|
23
24
|
|
|
24
25
|
/**
|
|
25
26
|
* Outlet component - renders child content in layouts
|
|
@@ -86,6 +87,8 @@ export function Outlet({ name }: { name?: `@${string}` } = {}): ReactNode {
|
|
|
86
87
|
content = segment.component ?? null;
|
|
87
88
|
}
|
|
88
89
|
|
|
90
|
+
let result: ReactNode;
|
|
91
|
+
|
|
89
92
|
// If segment has a layout, wrap appropriately
|
|
90
93
|
if (segment.layout) {
|
|
91
94
|
// Check if this segment has loaders that need streaming
|
|
@@ -105,25 +108,23 @@ export function Outlet({ name }: { name?: `@${string}` } = {}): ReactNode {
|
|
|
105
108
|
</LoaderBoundary>
|
|
106
109
|
);
|
|
107
110
|
|
|
108
|
-
|
|
111
|
+
result = (
|
|
109
112
|
<OutletProvider content={loaderAwareContent} segment={segment}>
|
|
110
113
|
{segment.layout}
|
|
111
114
|
</OutletProvider>
|
|
112
115
|
);
|
|
116
|
+
} else {
|
|
117
|
+
// No loaders - wrap in OutletProvider so layout can use <Outlet />
|
|
118
|
+
result = (
|
|
119
|
+
<OutletProvider content={content} segment={segment}>
|
|
120
|
+
{segment.layout}
|
|
121
|
+
</OutletProvider>
|
|
122
|
+
);
|
|
113
123
|
}
|
|
114
|
-
|
|
115
|
-
// No loaders - wrap
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
{segment.layout}
|
|
119
|
-
</OutletProvider>
|
|
120
|
-
);
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
// No layout but has loaders - wrap content with LoaderBoundary for useLoader context
|
|
124
|
-
// This is common for intercept routes that use useLoader without a custom layout
|
|
125
|
-
if (segment.loaderDataPromise && segment.loaderIds) {
|
|
126
|
-
return (
|
|
124
|
+
} else if (segment.loaderDataPromise && segment.loaderIds) {
|
|
125
|
+
// No layout but has loaders - wrap content with LoaderBoundary for useLoader context
|
|
126
|
+
// This is common for intercept routes that use useLoader without a custom layout
|
|
127
|
+
result = (
|
|
127
128
|
<LoaderBoundary
|
|
128
129
|
loaderDataPromise={segment.loaderDataPromise}
|
|
129
130
|
loaderIds={segment.loaderIds}
|
|
@@ -135,9 +136,20 @@ export function Outlet({ name }: { name?: `@${string}` } = {}): ReactNode {
|
|
|
135
136
|
{content}
|
|
136
137
|
</LoaderBoundary>
|
|
137
138
|
);
|
|
139
|
+
} else {
|
|
140
|
+
result = content;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Wrap with MountContextProvider for include() scoped parallel/intercept slots
|
|
144
|
+
if (segment.mountPath) {
|
|
145
|
+
return (
|
|
146
|
+
<MountContextProvider value={segment.mountPath}>
|
|
147
|
+
{result}
|
|
148
|
+
</MountContextProvider>
|
|
149
|
+
);
|
|
138
150
|
}
|
|
139
151
|
|
|
140
|
-
return
|
|
152
|
+
return result;
|
|
141
153
|
}
|
|
142
154
|
|
|
143
155
|
// Default: render child content
|
|
@@ -201,6 +213,8 @@ export function ParallelOutlet({ name }: { name: `@${string}` }): ReactNode {
|
|
|
201
213
|
content = segment.component ?? null;
|
|
202
214
|
}
|
|
203
215
|
|
|
216
|
+
let result: ReactNode;
|
|
217
|
+
|
|
204
218
|
// If segment has a layout, wrap appropriately
|
|
205
219
|
if (segment.layout) {
|
|
206
220
|
// Check if this segment has loaders that need streaming
|
|
@@ -219,25 +233,23 @@ export function ParallelOutlet({ name }: { name: `@${string}` }): ReactNode {
|
|
|
219
233
|
</LoaderBoundary>
|
|
220
234
|
);
|
|
221
235
|
|
|
222
|
-
|
|
236
|
+
result = (
|
|
223
237
|
<OutletProvider content={loaderAwareContent} segment={segment}>
|
|
224
238
|
{segment.layout}
|
|
225
239
|
</OutletProvider>
|
|
226
240
|
);
|
|
241
|
+
} else {
|
|
242
|
+
// No loaders - wrap in OutletProvider so layout can use <Outlet />
|
|
243
|
+
result = (
|
|
244
|
+
<OutletProvider content={content} segment={segment}>
|
|
245
|
+
{segment.layout}
|
|
246
|
+
</OutletProvider>
|
|
247
|
+
);
|
|
227
248
|
}
|
|
228
|
-
|
|
229
|
-
// No loaders - wrap
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
{segment.layout}
|
|
233
|
-
</OutletProvider>
|
|
234
|
-
);
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
// No layout but has loaders - wrap content with LoaderBoundary for useLoader context
|
|
238
|
-
// This is common for intercept routes that use useLoader without a custom layout
|
|
239
|
-
if (segment.loaderDataPromise && segment.loaderIds) {
|
|
240
|
-
return (
|
|
249
|
+
} else if (segment.loaderDataPromise && segment.loaderIds) {
|
|
250
|
+
// No layout but has loaders - wrap content with LoaderBoundary for useLoader context
|
|
251
|
+
// This is common for intercept routes that use useLoader without a custom layout
|
|
252
|
+
result = (
|
|
241
253
|
<LoaderBoundary
|
|
242
254
|
loaderDataPromise={segment.loaderDataPromise}
|
|
243
255
|
loaderIds={segment.loaderIds}
|
|
@@ -249,51 +261,28 @@ export function ParallelOutlet({ name }: { name: `@${string}` }): ReactNode {
|
|
|
249
261
|
{content}
|
|
250
262
|
</LoaderBoundary>
|
|
251
263
|
);
|
|
264
|
+
} else {
|
|
265
|
+
result = content;
|
|
252
266
|
}
|
|
253
267
|
|
|
254
|
-
|
|
255
|
-
|
|
268
|
+
// Wrap with MountContextProvider for include() scoped parallel/intercept slots
|
|
269
|
+
if (segment.mountPath) {
|
|
270
|
+
return (
|
|
271
|
+
<MountContextProvider value={segment.mountPath}>
|
|
272
|
+
{result}
|
|
273
|
+
</MountContextProvider>
|
|
274
|
+
);
|
|
275
|
+
}
|
|
256
276
|
|
|
257
|
-
|
|
258
|
-
* Provider for outlet content - used internally by renderSegments
|
|
259
|
-
*
|
|
260
|
-
* Stores a reference to parent context so useLoader can walk up the chain
|
|
261
|
-
* to find loader data from parent layouts. If this segment defines a loading
|
|
262
|
-
* component, Outlet will wrap content with Suspense using that as fallback.
|
|
263
|
-
*/
|
|
264
|
-
export function OutletProvider({
|
|
265
|
-
content,
|
|
266
|
-
parallel,
|
|
267
|
-
segment,
|
|
268
|
-
loaderData,
|
|
269
|
-
children,
|
|
270
|
-
}: {
|
|
271
|
-
content: ReactNode;
|
|
272
|
-
parallel?: ResolvedSegment[];
|
|
273
|
-
segment?: ResolvedSegment;
|
|
274
|
-
loaderData?: Record<string, any>;
|
|
275
|
-
children: ReactNode;
|
|
276
|
-
}): ReactNode {
|
|
277
|
-
// Get parent context to enable walking up the chain for loader lookups
|
|
278
|
-
const parentContext = useContext(OutletContext);
|
|
279
|
-
|
|
280
|
-
const value = useMemo(
|
|
281
|
-
() => ({
|
|
282
|
-
content,
|
|
283
|
-
parallel,
|
|
284
|
-
segment,
|
|
285
|
-
loaderData,
|
|
286
|
-
parent: parentContext,
|
|
287
|
-
loading: segment?.loading,
|
|
288
|
-
}),
|
|
289
|
-
[content, parallel, segment, loaderData, parentContext]
|
|
290
|
-
);
|
|
291
|
-
|
|
292
|
-
return (
|
|
293
|
-
<OutletContext.Provider value={value}>{children}</OutletContext.Provider>
|
|
294
|
-
);
|
|
277
|
+
return result;
|
|
295
278
|
}
|
|
296
279
|
|
|
280
|
+
// OutletProvider is defined in outlet-provider.tsx to break a circular
|
|
281
|
+
// dependency between client.tsx and route-content-wrapper.tsx.
|
|
282
|
+
// Imported at the top of this file for local use in Outlet/ParallelOutlet,
|
|
283
|
+
// and re-exported here for backwards compatibility.
|
|
284
|
+
export { OutletProvider };
|
|
285
|
+
|
|
297
286
|
/**
|
|
298
287
|
* Hook to access outlet content programmatically
|
|
299
288
|
*
|
|
@@ -323,103 +312,6 @@ export {
|
|
|
323
312
|
type UseLoaderOptions,
|
|
324
313
|
} from "./use-loader.js";
|
|
325
314
|
|
|
326
|
-
/**
|
|
327
|
-
* Hook to access all loader data in the current context
|
|
328
|
-
*
|
|
329
|
-
* Returns a record of all loader data available in the current outlet context
|
|
330
|
-
* and all parent contexts. Useful for debugging or when you need access to
|
|
331
|
-
* multiple loaders.
|
|
332
|
-
*
|
|
333
|
-
* @returns Record of loader name to data, or empty object if no loaders
|
|
334
|
-
*
|
|
335
|
-
* @example
|
|
336
|
-
* ```tsx
|
|
337
|
-
* "use client";
|
|
338
|
-
* import { useLoaderData } from "rsc-router/client";
|
|
339
|
-
*
|
|
340
|
-
* export function DebugPanel() {
|
|
341
|
-
* const loaderData = useLoaderData();
|
|
342
|
-
* return <pre>{JSON.stringify(loaderData, null, 2)}</pre>;
|
|
343
|
-
* }
|
|
344
|
-
* ```
|
|
345
|
-
*/
|
|
346
|
-
export function useLoaderData(): Record<string, any> {
|
|
347
|
-
const context = useContext(OutletContext);
|
|
348
|
-
|
|
349
|
-
// Collect all loader data from the context chain
|
|
350
|
-
// Child loaders override parent loaders with the same name
|
|
351
|
-
const result: Record<string, any> = {};
|
|
352
|
-
const stack: OutletContextValue[] = [];
|
|
353
|
-
|
|
354
|
-
// Build stack from current to root
|
|
355
|
-
let current: OutletContextValue | null | undefined = context;
|
|
356
|
-
while (current) {
|
|
357
|
-
stack.push(current);
|
|
358
|
-
current = current.parent;
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
// Apply from root to current (so children override parents)
|
|
362
|
-
for (let i = stack.length - 1; i >= 0; i--) {
|
|
363
|
-
const ctx = stack[i];
|
|
364
|
-
if (ctx.loaderData) {
|
|
365
|
-
Object.assign(result, ctx.loaderData);
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
return result;
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
/**
|
|
373
|
-
* Client-safe createLoader factory
|
|
374
|
-
*
|
|
375
|
-
* Creates a loader definition that can be used with useLoader().
|
|
376
|
-
* This is the client-side version that only stores the $$id - the function
|
|
377
|
-
* is ignored since loaders only execute on the server.
|
|
378
|
-
*
|
|
379
|
-
* The $$id is injected by the exposeLoaderId Vite plugin. In most cases,
|
|
380
|
-
* you should import the loader directly from the server file rather than
|
|
381
|
-
* creating a reference manually.
|
|
382
|
-
*
|
|
383
|
-
* @param fn - Loader function (ignored on client, kept for API compatibility)
|
|
384
|
-
* @param _fetchable - Optional fetchable flag (ignored on client)
|
|
385
|
-
* @param __injectedId - $$id injected by Vite plugin
|
|
386
|
-
*
|
|
387
|
-
* @example
|
|
388
|
-
* ```tsx
|
|
389
|
-
* "use client";
|
|
390
|
-
* import { useLoader } from "rsc-router/client";
|
|
391
|
-
* import { CartLoader } from "../loaders/cart"; // Import from server file
|
|
392
|
-
*
|
|
393
|
-
* export function CartIcon() {
|
|
394
|
-
* const cart = useLoader(CartLoader);
|
|
395
|
-
* return <span>Cart ({cart?.items.length ?? 0})</span>;
|
|
396
|
-
* }
|
|
397
|
-
* ```
|
|
398
|
-
*/
|
|
399
|
-
// Overload 1: With function only (not fetchable)
|
|
400
|
-
export function createLoader<T>(
|
|
401
|
-
fn: LoaderFn<T, Record<string, string | undefined>, any>
|
|
402
|
-
): LoaderDefinition<Awaited<T>, Record<string, string | undefined>>;
|
|
403
|
-
|
|
404
|
-
// Overload 2: With function and fetchable flag
|
|
405
|
-
export function createLoader<T>(
|
|
406
|
-
fn: LoaderFn<T, Record<string, string | undefined>, any>,
|
|
407
|
-
fetchable: true
|
|
408
|
-
): LoaderDefinition<Awaited<T>, Record<string, string | undefined>>;
|
|
409
|
-
|
|
410
|
-
// Implementation - function is ignored at runtime on client
|
|
411
|
-
// The $$id is injected by Vite plugin as hidden third parameter
|
|
412
|
-
export function createLoader(
|
|
413
|
-
_fn: LoaderFn<any, Record<string, string | undefined>, any>,
|
|
414
|
-
_fetchable?: true,
|
|
415
|
-
__injectedId?: string
|
|
416
|
-
): LoaderDefinition<any, Record<string, string | undefined>> {
|
|
417
|
-
return {
|
|
418
|
-
__brand: "loader",
|
|
419
|
-
$$id: __injectedId || "",
|
|
420
|
-
};
|
|
421
|
-
}
|
|
422
|
-
|
|
423
315
|
/**
|
|
424
316
|
* Props for the ErrorBoundary component
|
|
425
317
|
*/
|
|
@@ -534,11 +426,16 @@ export class ErrorBoundary extends Component<
|
|
|
534
426
|
// ============================================================================
|
|
535
427
|
|
|
536
428
|
// Navigation hooks
|
|
537
|
-
export {
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
} from "./browser/react/use-
|
|
429
|
+
export { useNavigation } from "./browser/react/use-navigation.js";
|
|
430
|
+
export { useRouter } from "./browser/react/use-router.js";
|
|
431
|
+
export { usePathname } from "./browser/react/use-pathname.js";
|
|
432
|
+
export { useSearchParams } from "./browser/react/use-search-params.js";
|
|
433
|
+
export { useParams } from "./browser/react/use-params.js";
|
|
434
|
+
export type {
|
|
435
|
+
RouterInstance,
|
|
436
|
+
RouterNavigateOptions,
|
|
437
|
+
ReadonlyURLSearchParams,
|
|
438
|
+
} from "./browser/types.js";
|
|
542
439
|
|
|
543
440
|
// Action state tracking hook
|
|
544
441
|
export {
|
|
@@ -585,16 +482,15 @@ export {
|
|
|
585
482
|
type ScrollRestorationProps,
|
|
586
483
|
} from "./browser/react/ScrollRestoration.js";
|
|
587
484
|
|
|
588
|
-
// Handle
|
|
589
|
-
export {
|
|
590
|
-
|
|
591
|
-
// Handle data hook
|
|
485
|
+
// Handle data hook (client-side only — createHandle/isHandle are server APIs from the root export)
|
|
486
|
+
export { type Handle } from "./handle.js";
|
|
592
487
|
export { useHandle } from "./browser/react/use-handle.js";
|
|
593
488
|
|
|
594
489
|
// Built-in handles
|
|
595
490
|
export { Meta } from "./handles/meta.js";
|
|
596
491
|
export { MetaTags } from "./handles/MetaTags.js";
|
|
597
492
|
export type { MetaDescriptor, MetaDescriptorBase } from "./router/types.js";
|
|
493
|
+
export { Breadcrumbs, type BreadcrumbItem } from "./handles/breadcrumbs.js";
|
|
598
494
|
|
|
599
495
|
// Location state - type-safe navigation state
|
|
600
496
|
export {
|
|
@@ -602,10 +498,40 @@ export {
|
|
|
602
498
|
useLocationState,
|
|
603
499
|
type LocationStateDefinition,
|
|
604
500
|
type LocationStateEntry,
|
|
501
|
+
type LocationStateOptions,
|
|
605
502
|
} from "./browser/react/location-state.js";
|
|
606
503
|
|
|
607
504
|
// Type-safe href for client-side path validation
|
|
608
|
-
export {
|
|
505
|
+
export {
|
|
506
|
+
href,
|
|
507
|
+
type ValidPaths,
|
|
508
|
+
type PatternToPath,
|
|
509
|
+
type PathResponse,
|
|
510
|
+
} from "./href-client.js";
|
|
511
|
+
|
|
512
|
+
// Response envelope types for consuming JSON response routes
|
|
513
|
+
export type { ResponseEnvelope, ResponseError } from "./urls.js";
|
|
514
|
+
|
|
515
|
+
/**
|
|
516
|
+
* Type guard for checking if a response envelope contains an error.
|
|
517
|
+
*
|
|
518
|
+
* @example
|
|
519
|
+
* ```typescript
|
|
520
|
+
* const result: ResponseEnvelope<Product> = await fetch(url).then(r => r.json());
|
|
521
|
+
* if (isResponseError(result)) {
|
|
522
|
+
* console.log(result.error.message, result.error.code);
|
|
523
|
+
* return;
|
|
524
|
+
* }
|
|
525
|
+
* result.data // fully typed as Product
|
|
526
|
+
* ```
|
|
527
|
+
*/
|
|
528
|
+
export function isResponseError<T>(
|
|
529
|
+
result: import("./urls.js").ResponseEnvelope<T>,
|
|
530
|
+
): result is import("./urls.js").ResponseEnvelope<T> & {
|
|
531
|
+
error: import("./urls.js").ResponseError;
|
|
532
|
+
} {
|
|
533
|
+
return result.error !== undefined;
|
|
534
|
+
}
|
|
609
535
|
|
|
610
536
|
// Mount context for include() scoped components
|
|
611
537
|
export { useMount } from "./browser/react/use-mount.js";
|
|
@@ -614,8 +540,8 @@ export { MountContext } from "./browser/react/mount-context.js";
|
|
|
614
540
|
// Mount-aware href hook - auto-prefixes paths with include() mount
|
|
615
541
|
export { useHref } from "./browser/react/use-href.js";
|
|
616
542
|
|
|
617
|
-
// Type-safe scoped
|
|
618
|
-
export type {
|
|
543
|
+
// Type-safe scoped reverse function for scopedReverse<typeof patterns>()
|
|
544
|
+
export type { ScopedReverseFunction } from "./reverse.js";
|
|
619
545
|
|
|
620
546
|
// Loader definition type - for typing loader props in client components
|
|
621
547
|
export type { LoaderDefinition } from "./types.js";
|
package/src/component-utils.ts
CHANGED
|
@@ -33,7 +33,7 @@ const CLIENT_REFERENCE = Symbol.for("react.client.reference");
|
|
|
33
33
|
* ```
|
|
34
34
|
*/
|
|
35
35
|
export function isClientComponent(
|
|
36
|
-
component: ComponentType<unknown> | unknown
|
|
36
|
+
component: ComponentType<unknown> | unknown,
|
|
37
37
|
): boolean {
|
|
38
38
|
if (typeof component !== "function") {
|
|
39
39
|
return false;
|
|
@@ -52,13 +52,13 @@ export function isClientComponent(
|
|
|
52
52
|
*/
|
|
53
53
|
export function assertClientComponent(
|
|
54
54
|
component: ComponentType<unknown> | unknown,
|
|
55
|
-
name: string
|
|
55
|
+
name: string,
|
|
56
56
|
): asserts component is ComponentType<unknown> {
|
|
57
57
|
if (typeof component !== "function") {
|
|
58
58
|
throw new Error(
|
|
59
59
|
`${name} must be a client component function with "use client" directive. ` +
|
|
60
60
|
`Make sure to pass the component itself, not a JSX element: ` +
|
|
61
|
-
`${name}: My${capitalize(name)} (correct) vs ${name}: <My${capitalize(name)} /> (incorrect)
|
|
61
|
+
`${name}: My${capitalize(name)} (correct) vs ${name}: <My${capitalize(name)} /> (incorrect)`,
|
|
62
62
|
);
|
|
63
63
|
}
|
|
64
64
|
|
|
@@ -66,7 +66,7 @@ export function assertClientComponent(
|
|
|
66
66
|
throw new Error(
|
|
67
67
|
`${name} must be a client component with "use client" directive at the top of the file. ` +
|
|
68
68
|
`Server components cannot be used as the ${name} because their function reference ` +
|
|
69
|
-
`cannot be serialized in the RSC payload. Add "use client" to your ${name} file
|
|
69
|
+
`cannot be serialized in the RSC payload. Add "use client" to your ${name} file.`,
|
|
70
70
|
);
|
|
71
71
|
}
|
|
72
72
|
}
|
|
@@ -11,7 +11,11 @@ import { MetaTags } from "../handles/MetaTags.js";
|
|
|
11
11
|
* Uses suppressHydrationWarning on <html> because the theme script
|
|
12
12
|
* may modify class/style attributes before React hydrates.
|
|
13
13
|
*/
|
|
14
|
-
export function DefaultDocument({
|
|
14
|
+
export function DefaultDocument({
|
|
15
|
+
children,
|
|
16
|
+
}: {
|
|
17
|
+
children: ReactNode;
|
|
18
|
+
}): ReactElement {
|
|
15
19
|
return (
|
|
16
20
|
<html lang="en" suppressHydrationWarning>
|
|
17
21
|
<head>
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed context variables for ctx.set() / ctx.get().
|
|
3
|
+
*
|
|
4
|
+
* createVar<T>() produces a typed token that handlers set and layouts/middleware
|
|
5
|
+
* read. The token carries a unique Symbol used as the property key on the
|
|
6
|
+
* per-request variables object — no build-time processing, no IDs.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```ts
|
|
10
|
+
* import { createVar } from "@rangojs/router";
|
|
11
|
+
*
|
|
12
|
+
* interface PaginationData { current: number; total: number }
|
|
13
|
+
* export const Pagination = createVar<PaginationData>();
|
|
14
|
+
*
|
|
15
|
+
* // Non-cacheable var — throws if set/get inside cache() or "use cache"
|
|
16
|
+
* export const User = createVar<UserData>({ cache: false });
|
|
17
|
+
*
|
|
18
|
+
* // handler
|
|
19
|
+
* ctx.set(Pagination, { current: 1, total: 4 });
|
|
20
|
+
*
|
|
21
|
+
* // layout
|
|
22
|
+
* const pg = ctx.get(Pagination); // PaginationData | undefined
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
export interface ContextVar<T> {
|
|
27
|
+
readonly __brand: "context-var";
|
|
28
|
+
readonly key: symbol;
|
|
29
|
+
/** When false, the var is non-cacheable — throws inside cache() / "use cache" */
|
|
30
|
+
readonly cache: boolean;
|
|
31
|
+
/** Phantom field to carry the type parameter. Never set at runtime. */
|
|
32
|
+
readonly __type?: T;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface ContextVarOptions {
|
|
36
|
+
/**
|
|
37
|
+
* When false, marks this variable as non-cacheable.
|
|
38
|
+
* Setting or getting this var inside a cache() boundary or "use cache"
|
|
39
|
+
* function will throw. Use for inherently request-specific data (user
|
|
40
|
+
* sessions, auth tokens, etc.) that must never be baked into cached segments.
|
|
41
|
+
*
|
|
42
|
+
* @default true
|
|
43
|
+
*/
|
|
44
|
+
cache?: boolean;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Create a typed context variable token.
|
|
49
|
+
*
|
|
50
|
+
* The returned object is used with ctx.set(token, value) and ctx.get(token)
|
|
51
|
+
* for compile-time-checked data flow between handlers, layouts, and middleware.
|
|
52
|
+
*/
|
|
53
|
+
export function createVar<T>(options?: ContextVarOptions): ContextVar<T> {
|
|
54
|
+
return {
|
|
55
|
+
__brand: "context-var" as const,
|
|
56
|
+
key: Symbol(),
|
|
57
|
+
cache: options?.cache !== false,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Type guard: is the value a ContextVar token?
|
|
63
|
+
*/
|
|
64
|
+
export function isContextVar(value: unknown): value is ContextVar<unknown> {
|
|
65
|
+
return (
|
|
66
|
+
typeof value === "object" &&
|
|
67
|
+
value !== null &&
|
|
68
|
+
"__brand" in value &&
|
|
69
|
+
(value as { __brand: unknown }).__brand === "context-var"
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Symbol used as a Set stored on the variables object to track
|
|
75
|
+
* which keys hold non-cacheable values (from write-level { cache: false }).
|
|
76
|
+
*/
|
|
77
|
+
const NON_CACHEABLE_KEYS: unique symbol = Symbol.for(
|
|
78
|
+
"rango:non-cacheable-keys",
|
|
79
|
+
) as any;
|
|
80
|
+
|
|
81
|
+
function getNonCacheableKeys(variables: any): Set<string | symbol> {
|
|
82
|
+
if (!variables[NON_CACHEABLE_KEYS]) {
|
|
83
|
+
variables[NON_CACHEABLE_KEYS] = new Set();
|
|
84
|
+
}
|
|
85
|
+
return variables[NON_CACHEABLE_KEYS];
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Check if a variable value is non-cacheable (either var-level or write-level).
|
|
90
|
+
*/
|
|
91
|
+
export function isNonCacheable(
|
|
92
|
+
variables: any,
|
|
93
|
+
keyOrVar: string | ContextVar<any>,
|
|
94
|
+
): boolean {
|
|
95
|
+
if (typeof keyOrVar !== "string" && !keyOrVar.cache) {
|
|
96
|
+
return true; // var-level policy
|
|
97
|
+
}
|
|
98
|
+
const key = typeof keyOrVar === "string" ? keyOrVar : keyOrVar.key;
|
|
99
|
+
const set = variables[NON_CACHEABLE_KEYS] as Set<string | symbol> | undefined;
|
|
100
|
+
return set?.has(key) ?? false; // write-level policy
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Read a variable from the variables store.
|
|
105
|
+
* Accepts either a string key (legacy) or a ContextVar token (typed).
|
|
106
|
+
*/
|
|
107
|
+
export function contextGet(
|
|
108
|
+
variables: any,
|
|
109
|
+
keyOrVar: string | ContextVar<any>,
|
|
110
|
+
): any {
|
|
111
|
+
if (typeof keyOrVar === "string") return variables[keyOrVar];
|
|
112
|
+
return variables[keyOrVar.key];
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** Keys that must never be used as string variable names */
|
|
116
|
+
const FORBIDDEN_KEYS = new Set(["__proto__", "constructor", "prototype"]);
|
|
117
|
+
|
|
118
|
+
export interface ContextSetOptions {
|
|
119
|
+
/**
|
|
120
|
+
* When false, marks this specific write as non-cacheable.
|
|
121
|
+
* "Least cacheable wins" — if either the var definition or this option
|
|
122
|
+
* says cache: false, the value is non-cacheable.
|
|
123
|
+
*
|
|
124
|
+
* @default true (inherits from createVar)
|
|
125
|
+
*/
|
|
126
|
+
cache?: boolean;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Write a variable to the variables store.
|
|
131
|
+
* Accepts either a string key (legacy) or a ContextVar token (typed).
|
|
132
|
+
*/
|
|
133
|
+
export function contextSet(
|
|
134
|
+
variables: any,
|
|
135
|
+
keyOrVar: string | ContextVar<any>,
|
|
136
|
+
value: any,
|
|
137
|
+
options?: ContextSetOptions,
|
|
138
|
+
): void {
|
|
139
|
+
if (typeof keyOrVar === "string") {
|
|
140
|
+
if (FORBIDDEN_KEYS.has(keyOrVar)) {
|
|
141
|
+
throw new Error(
|
|
142
|
+
`ctx.set(): "${keyOrVar}" is a reserved key and cannot be used as a variable name.`,
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
variables[keyOrVar] = value;
|
|
146
|
+
if (options?.cache === false) {
|
|
147
|
+
getNonCacheableKeys(variables).add(keyOrVar);
|
|
148
|
+
}
|
|
149
|
+
} else {
|
|
150
|
+
variables[keyOrVar.key] = value;
|
|
151
|
+
// Track write-level non-cacheable (var-level is checked via keyOrVar.cache)
|
|
152
|
+
if (options?.cache === false) {
|
|
153
|
+
getNonCacheableKeys(variables).add(keyOrVar.key);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
package/src/debug.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Debug utilities for manifest inspection and comparison
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import type
|
|
5
|
+
import { getParallelSlotCount, type EntryData } from "./server/context";
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Serialized entry for debug output
|
|
@@ -34,7 +34,7 @@ export interface SerializedManifest {
|
|
|
34
34
|
* Serialize a manifest Map into a JSON-friendly structure
|
|
35
35
|
*/
|
|
36
36
|
export function serializeManifest(
|
|
37
|
-
manifest: Map<string, EntryData
|
|
37
|
+
manifest: Map<string, EntryData>,
|
|
38
38
|
): SerializedManifest {
|
|
39
39
|
const routes: Record<string, SerializedEntry> = {};
|
|
40
40
|
const layouts: Record<string, SerializedEntry> = {};
|
|
@@ -64,7 +64,7 @@ export function serializeManifest(
|
|
|
64
64
|
hasLoader: entry.loader?.length > 0,
|
|
65
65
|
hasMiddleware: entry.middleware?.length > 0,
|
|
66
66
|
hasErrorBoundary: entry.errorBoundary?.length > 0,
|
|
67
|
-
parallelCount: entry.parallel
|
|
67
|
+
parallelCount: getParallelSlotCount(entry.parallel),
|
|
68
68
|
interceptCount: entry.intercept?.length ?? 0,
|
|
69
69
|
};
|
|
70
70
|
|
|
@@ -92,7 +92,7 @@ export function serializeManifest(
|
|
|
92
92
|
*/
|
|
93
93
|
export function compareManifests(
|
|
94
94
|
oldManifest: SerializedManifest,
|
|
95
|
-
newManifest: SerializedManifest
|
|
95
|
+
newManifest: SerializedManifest,
|
|
96
96
|
): {
|
|
97
97
|
addedRoutes: string[];
|
|
98
98
|
removedRoutes: string[];
|
|
@@ -113,10 +113,20 @@ export function compareManifests(
|
|
|
113
113
|
} {
|
|
114
114
|
const addedRoutes: string[] = [];
|
|
115
115
|
const removedRoutes: string[] = [];
|
|
116
|
-
const changedRoutes: Array<{
|
|
116
|
+
const changedRoutes: Array<{
|
|
117
|
+
key: string;
|
|
118
|
+
field: string;
|
|
119
|
+
old: any;
|
|
120
|
+
new: any;
|
|
121
|
+
}> = [];
|
|
117
122
|
const addedLayouts: string[] = [];
|
|
118
123
|
const removedLayouts: string[] = [];
|
|
119
|
-
const changedLayouts: Array<{
|
|
124
|
+
const changedLayouts: Array<{
|
|
125
|
+
key: string;
|
|
126
|
+
field: string;
|
|
127
|
+
old: any;
|
|
128
|
+
new: any;
|
|
129
|
+
}> = [];
|
|
120
130
|
|
|
121
131
|
// Compare routes
|
|
122
132
|
const oldRouteKeys = new Set(Object.keys(oldManifest.routes));
|
|
@@ -191,7 +201,7 @@ export function compareManifests(
|
|
|
191
201
|
* Format manifest diff as a human-readable string
|
|
192
202
|
*/
|
|
193
203
|
export function formatManifestDiff(
|
|
194
|
-
diff: ReturnType<typeof compareManifests
|
|
204
|
+
diff: ReturnType<typeof compareManifests>,
|
|
195
205
|
): string {
|
|
196
206
|
const lines: string[] = [];
|
|
197
207
|
|
|
@@ -208,7 +218,7 @@ export function formatManifestDiff(
|
|
|
208
218
|
if (diff.changedRoutes.length > 0) {
|
|
209
219
|
lines.push("Changed routes:");
|
|
210
220
|
diff.changedRoutes.forEach((c) =>
|
|
211
|
-
lines.push(` ~ ${c.key}.${c.field}: ${c.old} -> ${c.new}`)
|
|
221
|
+
lines.push(` ~ ${c.key}.${c.field}: ${c.old} -> ${c.new}`),
|
|
212
222
|
);
|
|
213
223
|
}
|
|
214
224
|
|
|
@@ -225,7 +235,7 @@ export function formatManifestDiff(
|
|
|
225
235
|
if (diff.changedLayouts.length > 0) {
|
|
226
236
|
lines.push("Changed layouts:");
|
|
227
237
|
diff.changedLayouts.forEach((c) =>
|
|
228
|
-
lines.push(` ~ ${c.key}.${c.field}: ${c.old} -> ${c.new}`)
|
|
238
|
+
lines.push(` ~ ${c.key}.${c.field}: ${c.old} -> ${c.new}`),
|
|
229
239
|
);
|
|
230
240
|
}
|
|
231
241
|
|