@rangojs/router 0.0.0-experimental.97 → 0.0.0-experimental.98914650
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -9
- package/dist/bin/rango.js +157 -63
- package/dist/testing/vitest.js +82 -0
- package/dist/vite/index.js +1584 -639
- package/package.json +71 -21
- package/skills/api-client/SKILL.md +211 -0
- package/skills/breadcrumbs/SKILL.md +60 -0
- package/skills/bundle-analysis/SKILL.md +159 -0
- package/skills/cache-guide/SKILL.md +222 -30
- package/skills/caching/SKILL.md +263 -8
- package/skills/composability/SKILL.md +27 -2
- package/skills/css/SKILL.md +76 -0
- package/skills/document-cache/SKILL.md +78 -55
- package/skills/handler-use/SKILL.md +3 -1
- package/skills/hooks/SKILL.md +235 -28
- package/skills/host-router/SKILL.md +122 -22
- package/skills/i18n/SKILL.md +276 -0
- package/skills/intercept/SKILL.md +29 -5
- package/skills/layout/SKILL.md +13 -9
- package/skills/links/SKILL.md +173 -17
- package/skills/loader/SKILL.md +170 -23
- package/skills/middleware/SKILL.md +16 -10
- package/skills/migrate-nextjs/SKILL.md +38 -16
- package/skills/mime-routes/SKILL.md +27 -0
- package/skills/observability/SKILL.md +137 -0
- package/skills/parallel/SKILL.md +11 -7
- package/skills/prerender/SKILL.md +14 -33
- package/skills/rango/SKILL.md +250 -25
- package/skills/react-compiler/SKILL.md +168 -0
- package/skills/response-routes/SKILL.md +114 -47
- package/skills/route/SKILL.md +42 -5
- package/skills/router-setup/SKILL.md +3 -3
- package/skills/server-actions/SKILL.md +78 -42
- package/skills/tailwind/SKILL.md +27 -3
- package/skills/testing/SKILL.md +129 -0
- package/skills/testing/bindings.md +89 -0
- package/skills/testing/cache-prerender.md +124 -0
- package/skills/testing/client-components.md +122 -0
- package/skills/testing/e2e-parity.md +125 -0
- package/skills/testing/flight.md +92 -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 +121 -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 +120 -0
- package/skills/typesafety/SKILL.md +316 -26
- package/skills/use-cache/SKILL.md +36 -5
- package/skills/vercel/SKILL.md +107 -0
- package/skills/view-transitions/SKILL.md +294 -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 +14 -27
- package/src/browser/cookie-name.ts +140 -0
- package/src/browser/event-controller.ts +37 -143
- 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 +30 -59
- package/src/browser/navigation-client.ts +96 -84
- package/src/browser/navigation-store-handle.ts +38 -0
- package/src/browser/navigation-store.ts +32 -82
- package/src/browser/navigation-transaction.ts +9 -59
- package/src/browser/partial-update.ts +60 -127
- package/src/browser/prefetch/cache.ts +82 -72
- package/src/browser/prefetch/fetch.ts +108 -33
- package/src/browser/prefetch/queue.ts +6 -3
- package/src/browser/rango-state.ts +157 -115
- package/src/browser/react/Link.tsx +0 -2
- package/src/browser/react/NavigationProvider.tsx +41 -48
- package/src/browser/react/ScrollRestoration.tsx +10 -6
- package/src/browser/react/filter-segment-order.ts +0 -2
- package/src/browser/react/index.ts +0 -48
- package/src/browser/react/location-state-shared.ts +166 -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-link-status.ts +0 -4
- package/src/browser/react/use-navigation.ts +0 -3
- 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 +20 -5
- package/src/browser/react/use-search-params.ts +0 -5
- package/src/browser/react/use-segments.ts +0 -13
- package/src/browser/response-adapter.ts +52 -1
- package/src/browser/rsc-router.tsx +70 -34
- package/src/browser/scroll-restoration.ts +22 -14
- package/src/browser/segment-structure-assert.ts +2 -2
- package/src/browser/server-action-bridge.ts +168 -44
- package/src/browser/types.ts +36 -21
- 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 +3 -0
- package/src/build/index.ts +8 -2
- package/src/build/prefix-tree-utils.ts +123 -0
- package/src/build/route-trie.ts +89 -10
- package/src/build/route-types/codegen.ts +4 -4
- package/src/build/route-types/include-resolution.ts +1 -1
- package/src/build/route-types/param-extraction.ts +6 -3
- package/src/build/route-types/per-module-writer.ts +7 -4
- package/src/build/route-types/router-processing.ts +122 -22
- package/src/build/route-types/scan-filter.ts +1 -1
- package/src/build/route-types/source-scan.ts +118 -0
- package/src/build/runtime-discovery.ts +9 -20
- package/src/cache/cache-error.ts +104 -0
- package/src/cache/cache-policy.ts +68 -28
- package/src/cache/cache-runtime.ts +134 -32
- package/src/cache/cache-scope.ts +100 -74
- package/src/cache/cache-tag.ts +98 -0
- package/src/cache/cf/cf-cache-store.ts +2255 -238
- package/src/cache/cf/index.ts +6 -16
- package/src/cache/document-cache.ts +61 -20
- package/src/cache/handle-snapshot.ts +63 -0
- package/src/cache/index.ts +22 -20
- package/src/cache/memory-segment-store.ts +136 -37
- package/src/cache/profile-registry.ts +6 -30
- package/src/cache/read-through-swr.ts +41 -11
- package/src/cache/segment-codec.ts +0 -16
- package/src/cache/tag-invalidation.ts +230 -0
- package/src/cache/types.ts +33 -100
- package/src/cache/vercel/index.ts +11 -0
- package/src/cache/vercel/vercel-cache-store.ts +799 -0
- package/src/client.rsc.tsx +6 -21
- package/src/client.tsx +25 -61
- package/src/component-utils.ts +19 -0
- package/src/context-var.ts +17 -5
- package/src/decode-loader-results.ts +36 -0
- package/src/defer.ts +196 -0
- package/src/deps/ssr.ts +0 -1
- package/src/errors.ts +30 -4
- package/src/handle.ts +31 -23
- package/src/handles/MetaTags.tsx +0 -14
- package/src/handles/breadcrumbs.ts +16 -5
- package/src/handles/meta.ts +0 -39
- package/src/host/cookie-handler.ts +0 -36
- package/src/host/errors.ts +0 -24
- package/src/host/index.ts +8 -2
- package/src/host/pattern-matcher.ts +7 -50
- 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 +63 -9
- package/src/index.ts +64 -9
- package/src/internal-debug.ts +2 -4
- 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/network-error-thrower.tsx +1 -6
- package/src/outlet-provider.tsx +1 -5
- package/src/prerender/param-hash.ts +10 -11
- package/src/prerender/store.ts +32 -37
- package/src/prerender.ts +61 -6
- package/src/redirect-origin.ts +100 -0
- package/src/response-utils.ts +9 -0
- package/src/reverse.ts +65 -40
- package/src/root-error-boundary.tsx +1 -19
- package/src/route-content-wrapper.tsx +7 -72
- package/src/route-definition/dsl-helpers.ts +244 -281
- package/src/route-definition/helper-factories.ts +29 -139
- package/src/route-definition/helpers-types.ts +40 -17
- package/src/route-definition/redirect.ts +43 -9
- package/src/route-definition/resolve-handler-use.ts +6 -0
- package/src/route-definition/use-item-types.ts +32 -0
- package/src/route-map-builder.ts +0 -16
- package/src/route-types.ts +19 -41
- package/src/router/basename.ts +14 -0
- package/src/router/content-negotiation.ts +15 -15
- package/src/router/error-handling.ts +13 -17
- package/src/router/find-match.ts +44 -23
- package/src/router/handler-context.ts +4 -41
- package/src/router/intercept-resolution.ts +14 -19
- package/src/router/lazy-includes.ts +9 -46
- package/src/router/loader-resolution.ts +91 -46
- package/src/router/logging.ts +0 -6
- package/src/router/manifest.ts +18 -29
- package/src/router/match-api.ts +0 -20
- package/src/router/match-context.ts +0 -22
- package/src/router/match-handlers.ts +57 -58
- package/src/router/match-middleware/background-revalidation.ts +0 -7
- package/src/router/match-middleware/cache-lookup.ts +150 -271
- package/src/router/match-middleware/cache-store.ts +3 -33
- 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 +31 -80
- package/src/router/metrics.ts +0 -34
- package/src/router/middleware-types.ts +5 -112
- package/src/router/middleware.ts +118 -133
- package/src/router/navigation-snapshot.ts +0 -51
- package/src/router/params-util.ts +23 -0
- package/src/router/pattern-matching.ts +62 -67
- package/src/router/prerender-match.ts +99 -63
- package/src/router/preview-match.ts +3 -1
- package/src/router/request-classification.ts +28 -62
- package/src/router/revalidation.ts +50 -56
- package/src/router/route-snapshot.ts +0 -1
- package/src/router/router-context.ts +0 -27
- package/src/router/router-interfaces.ts +68 -35
- package/src/router/router-options.ts +55 -1
- package/src/router/router-registry.ts +2 -5
- package/src/router/segment-resolution/fresh.ts +44 -63
- package/src/router/segment-resolution/helpers.ts +34 -0
- package/src/router/segment-resolution/loader-cache.ts +40 -37
- package/src/router/segment-resolution/revalidation.ts +203 -285
- 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 +36 -0
- package/src/router/segment-resolution.ts +4 -1
- package/src/router/segment-wrappers.ts +0 -3
- package/src/router/state-cookie-name.ts +33 -0
- package/src/router/substitute-pattern-params.ts +56 -0
- package/src/router/telemetry-otel.ts +0 -20
- package/src/router/telemetry.ts +96 -19
- package/src/router/timeout.ts +0 -20
- package/src/router/trie-matching.ts +87 -48
- package/src/router/types.ts +9 -63
- package/src/router/url-params.ts +0 -5
- package/src/router.ts +80 -41
- package/src/rsc/handler-context.ts +3 -2
- package/src/rsc/handler.ts +83 -78
- package/src/rsc/helpers.ts +93 -5
- package/src/rsc/index.ts +1 -1
- package/src/rsc/json-route-result.ts +38 -0
- package/src/rsc/manifest-init.ts +28 -41
- package/src/rsc/origin-guard.ts +39 -25
- package/src/rsc/progressive-enhancement.ts +12 -1
- package/src/rsc/redirect-guard.ts +99 -0
- package/src/rsc/response-error.ts +79 -12
- package/src/rsc/response-route-handler.ts +76 -62
- package/src/rsc/rsc-rendering.ts +41 -60
- package/src/rsc/runtime-warnings.ts +23 -10
- package/src/rsc/server-action.ts +62 -67
- package/src/rsc/ssr-setup.ts +16 -0
- package/src/rsc/types.ts +10 -5
- package/src/runtime-env.ts +18 -0
- package/src/search-params.ts +4 -20
- package/src/segment-loader-promise.ts +14 -2
- package/src/segment-system.tsx +199 -142
- package/src/serialize.ts +243 -0
- package/src/server/context.ts +150 -51
- package/src/server/cookie-store.ts +80 -5
- package/src/server/handle-store.ts +7 -24
- package/src/server/loader-registry.ts +5 -24
- package/src/server/request-context.ts +165 -87
- package/src/ssr/index.tsx +14 -14
- 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 +618 -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 +232 -0
- package/src/testing/generated-routes.ts +183 -0
- package/src/testing/index.ts +99 -0
- package/src/testing/internal/context.ts +348 -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 +330 -0
- package/src/testing/render-route.tsx +566 -0
- package/src/testing/run-loader.ts +378 -0
- package/src/testing/run-middleware.ts +205 -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 +0 -52
- package/src/theme/ThemeScript.tsx +0 -6
- package/src/theme/constants.ts +0 -12
- package/src/theme/index.ts +0 -7
- package/src/theme/theme-context.ts +1 -5
- package/src/theme/theme-script.ts +0 -14
- 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 +97 -22
- package/src/types/index.ts +1 -10
- package/src/types/loader-types.ts +6 -3
- package/src/types/request-scope.ts +0 -19
- package/src/types/route-config.ts +6 -50
- package/src/types/route-entry.ts +0 -6
- package/src/types/segments.ts +18 -14
- package/src/urls/include-helper.ts +9 -56
- package/src/urls/index.ts +1 -11
- package/src/urls/path-helper-types.ts +19 -5
- package/src/urls/path-helper.ts +17 -106
- package/src/urls/pattern-types.ts +36 -19
- package/src/urls/response-types.ts +20 -19
- package/src/urls/type-extraction.ts +58 -139
- package/src/urls/urls-function.ts +1 -18
- package/src/use-loader.tsx +292 -107
- package/src/vite/debug.ts +1 -0
- package/src/vite/discovery/bundle-postprocess.ts +8 -7
- package/src/vite/discovery/discover-routers.ts +95 -82
- package/src/vite/discovery/discovery-errors.ts +194 -0
- package/src/vite/discovery/prerender-collection.ts +26 -34
- package/src/vite/discovery/route-types-writer.ts +40 -84
- package/src/vite/discovery/state.ts +39 -1
- package/src/vite/discovery/virtual-module-codegen.ts +14 -34
- package/src/vite/index.ts +4 -0
- package/src/vite/plugin-types.ts +185 -10
- package/src/vite/plugins/cjs-to-esm.ts +3 -18
- package/src/vite/plugins/client-ref-dedup.ts +0 -11
- package/src/vite/plugins/client-ref-hashing.ts +12 -11
- package/src/vite/plugins/cloudflare-protocol-stub.ts +1 -21
- package/src/vite/plugins/expose-action-id.ts +4 -75
- package/src/vite/plugins/expose-id-utils.ts +3 -54
- package/src/vite/plugins/expose-ids/export-analysis.ts +76 -34
- package/src/vite/plugins/expose-ids/handler-transform.ts +6 -74
- package/src/vite/plugins/expose-ids/loader-transform.ts +3 -20
- package/src/vite/plugins/expose-ids/router-transform.ts +0 -13
- package/src/vite/plugins/expose-internal-ids.ts +57 -67
- package/src/vite/plugins/performance-tracks.ts +9 -16
- package/src/vite/plugins/refresh-cmd.ts +1 -1
- package/src/vite/plugins/use-cache-transform.ts +26 -49
- package/src/vite/plugins/vercel-output.ts +258 -0
- package/src/vite/plugins/version-injector.ts +2 -32
- package/src/vite/plugins/version-plugin.ts +32 -23
- package/src/vite/plugins/virtual-entries.ts +35 -17
- package/src/vite/rango.ts +148 -115
- package/src/vite/router-discovery.ts +220 -68
- package/src/vite/utils/ast-handler-extract.ts +15 -31
- package/src/vite/utils/bundle-analysis.ts +10 -15
- package/src/vite/utils/client-chunks.ts +184 -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 +1 -73
- package/src/vite/utils/prerender-utils.ts +0 -34
- package/src/vite/utils/shared-utils.ts +95 -43
- 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/router/middleware-cookies.ts +0 -55
|
@@ -11,6 +11,16 @@ import {
|
|
|
11
11
|
formatNestedRouterConflictError,
|
|
12
12
|
findNestedRouterConflict,
|
|
13
13
|
} from "../../build/generate-route-types.js";
|
|
14
|
+
// Pure data transforms over generateManifestFull's output. Imported directly
|
|
15
|
+
// from source (not the public ./build barrel, and not the runner) because they
|
|
16
|
+
// are realm-independent: buildRouteTrie/buildPerRouterTrie operate on plain
|
|
17
|
+
// manifest data, and collectFallbackClientRefs keys on the global-registry
|
|
18
|
+
// Symbol.for("react.client.reference"), so it detects client references in a
|
|
19
|
+
// boundary tree regardless of which realm imported the walker. Only
|
|
20
|
+
// generateManifestFull must stay on the runner (it invokes user handlers via
|
|
21
|
+
// RangoContext from the runner realm) — see the runner.import below.
|
|
22
|
+
import { buildRouteTrie, buildPerRouterTrie } from "../../build/route-trie.js";
|
|
23
|
+
import { collectFallbackClientRefs } from "../../build/collect-fallback-refs.js";
|
|
14
24
|
import {
|
|
15
25
|
flattenLeafEntries,
|
|
16
26
|
buildRouteToStaticPrefix,
|
|
@@ -20,7 +30,13 @@ import {
|
|
|
20
30
|
expandPrerenderRoutes,
|
|
21
31
|
renderStaticHandlers,
|
|
22
32
|
} from "./prerender-collection.js";
|
|
33
|
+
import {
|
|
34
|
+
resolveHostRouterHandlers,
|
|
35
|
+
DiscoveryError,
|
|
36
|
+
type CaughtDiscoveryError,
|
|
37
|
+
} from "./discovery-errors.js";
|
|
23
38
|
import { createRangoDebugger, timed, NS } from "../debug.js";
|
|
39
|
+
import { computeProductionHash } from "../plugins/client-ref-hashing.js";
|
|
24
40
|
|
|
25
41
|
const debug = createRangoDebugger(NS.discovery);
|
|
26
42
|
|
|
@@ -56,32 +72,25 @@ export async function discoverRouters(
|
|
|
56
72
|
if (!registry || registry.size === 0) {
|
|
57
73
|
// No RSC routers found directly. Check for host routers with lazy handlers
|
|
58
74
|
// that need to be resolved to trigger sub-app createRouter() calls.
|
|
75
|
+
//
|
|
76
|
+
// Handler failures are collected rather than swallowed: when the registry
|
|
77
|
+
// is still empty afterwards, these errors (typically a sub-app whose router
|
|
78
|
+
// module failed to import) are the most likely cause and are surfaced in
|
|
79
|
+
// the terminal "No routers found" error below.
|
|
80
|
+
const discoveryErrors: CaughtDiscoveryError[] = [];
|
|
59
81
|
try {
|
|
60
82
|
const hostRegistry: Map<string, any> | undefined =
|
|
61
83
|
serverMod.HostRouterRegistry;
|
|
62
84
|
|
|
63
85
|
if (hostRegistry && hostRegistry.size > 0) {
|
|
64
86
|
console.log(
|
|
65
|
-
`[
|
|
87
|
+
`[rango] Found ${hostRegistry.size} host router(s), resolving lazy handlers...`,
|
|
66
88
|
);
|
|
67
89
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
await route.handler();
|
|
73
|
-
} catch {
|
|
74
|
-
// Lazy handler may fail in temp server context, that's OK
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
if (entry.fallback && typeof entry.fallback.handler === "function") {
|
|
79
|
-
try {
|
|
80
|
-
await entry.fallback.handler();
|
|
81
|
-
} catch {
|
|
82
|
-
// Fallback handler may fail in temp server context
|
|
83
|
-
}
|
|
84
|
-
}
|
|
90
|
+
const handlerErrors = await resolveHostRouterHandlers(hostRegistry);
|
|
91
|
+
discoveryErrors.push(...handlerErrors);
|
|
92
|
+
for (const { context, error } of handlerErrors) {
|
|
93
|
+
debug?.("caught error while resolving %s: %O", context, error);
|
|
85
94
|
}
|
|
86
95
|
|
|
87
96
|
// Re-read RouterRegistry - sub-app createRouter() calls should have populated it
|
|
@@ -96,19 +105,22 @@ export async function discoverRouters(
|
|
|
96
105
|
registry = freshRegistry;
|
|
97
106
|
}
|
|
98
107
|
}
|
|
99
|
-
} catch {
|
|
100
|
-
// Host-router discovery is best-effort;
|
|
108
|
+
} catch (error) {
|
|
109
|
+
// Host-router discovery is best-effort; record the failure so it can be
|
|
110
|
+
// surfaced if no routers are found.
|
|
111
|
+
discoveryErrors.push({ context: "host-router discovery", error });
|
|
101
112
|
}
|
|
102
113
|
|
|
103
114
|
// If still no routers after host router resolution, fail
|
|
104
115
|
if (!registry || registry.size === 0) {
|
|
105
|
-
throw new
|
|
106
|
-
`[rsc-router] No routers found in registry after importing ${state.resolvedEntryPath}`,
|
|
107
|
-
);
|
|
116
|
+
throw new DiscoveryError(state.resolvedEntryPath, discoveryErrors);
|
|
108
117
|
}
|
|
109
118
|
}
|
|
110
119
|
|
|
111
|
-
//
|
|
120
|
+
// generateManifestFull must run in the RSC runner realm: it invokes the
|
|
121
|
+
// user's urlpatterns.handler() via RangoContext, consuming router instances
|
|
122
|
+
// from the runner. The trie/fallback-ref builders are pure transforms over
|
|
123
|
+
// its output and are imported directly from source above.
|
|
112
124
|
const buildMod = await timed(
|
|
113
125
|
debug,
|
|
114
126
|
"inner: import @rangojs/router/build",
|
|
@@ -145,6 +157,29 @@ export async function discoverRouters(
|
|
|
145
157
|
// Collect all manifests for trie building (avoid re-running generateManifest)
|
|
146
158
|
const allManifests: Array<{ id: string; manifest: any }> = [];
|
|
147
159
|
|
|
160
|
+
// Built-in clientChunks context (present only when the built-in strategy is
|
|
161
|
+
// active). Collect the production hashes of "use client" error/notFound
|
|
162
|
+
// fallback modules so the strategy can route them into app-fallback.
|
|
163
|
+
const clientChunkCtx = state.opts?.clientChunkCtx;
|
|
164
|
+
const collectClientFallbackRef = clientChunkCtx
|
|
165
|
+
? (refKey: string) =>
|
|
166
|
+
clientChunkCtx.fallbackRefs.add(
|
|
167
|
+
computeProductionHash(state.projectRoot, refKey),
|
|
168
|
+
)
|
|
169
|
+
: undefined;
|
|
170
|
+
// Router-level boundary defaults (`createRouter({ defaultErrorBoundary, ... })`)
|
|
171
|
+
// are NOT in EntryData, so generateManifestFull's walk misses them. Collect any
|
|
172
|
+
// "use client" default boundary directly off the router instance. The value is
|
|
173
|
+
// commonly a handler function wrapping the client boundary in server providers,
|
|
174
|
+
// so collectFallbackClientRefs invokes + walks the tree. The walker keys on the
|
|
175
|
+
// global-registry Symbol.for("react.client.reference"), so it detects client
|
|
176
|
+
// references in a runner-realm boundary tree even when imported here directly.
|
|
177
|
+
const collectFromBoundaryNode = (node: unknown): void => {
|
|
178
|
+
if (collectClientFallbackRef) {
|
|
179
|
+
collectFallbackClientRefs(node, collectClientFallbackRef);
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
|
|
148
183
|
const manifestGenStart = debug ? performance.now() : 0;
|
|
149
184
|
for (const [id, router] of registry) {
|
|
150
185
|
if (!router.urlpatterns || !generateManifestFull) {
|
|
@@ -154,10 +189,23 @@ export async function discoverRouters(
|
|
|
154
189
|
const manifest = generateManifestFull(
|
|
155
190
|
router.urlpatterns,
|
|
156
191
|
routerMountIndex,
|
|
157
|
-
|
|
192
|
+
{
|
|
193
|
+
...(router.__basename ? { urlPrefix: router.__basename } : {}),
|
|
194
|
+
...(collectClientFallbackRef ? { collectClientFallbackRef } : {}),
|
|
195
|
+
},
|
|
158
196
|
);
|
|
159
197
|
routerMountIndex++;
|
|
160
198
|
allManifests.push({ id, manifest });
|
|
199
|
+
|
|
200
|
+
// Router-level "use client" boundary defaults -> app-fallback (the
|
|
201
|
+
// route-tree errorBoundary()/notFoundBoundary() helpers are already
|
|
202
|
+
// collected inside generateManifestFull via collectClientFallbackRef).
|
|
203
|
+
if (collectClientFallbackRef) {
|
|
204
|
+
collectFromBoundaryNode(router.__defaultErrorBoundary);
|
|
205
|
+
collectFromBoundaryNode(router.__defaultNotFoundBoundary);
|
|
206
|
+
collectFromBoundaryNode(router.__notFound);
|
|
207
|
+
}
|
|
208
|
+
|
|
161
209
|
const routeCount = Object.keys(manifest.routeManifest).length;
|
|
162
210
|
const staticRoutes = Object.values(manifest.routeManifest).filter(
|
|
163
211
|
(p: any) => !p.includes(":") && !p.includes("*"),
|
|
@@ -209,24 +257,23 @@ export async function discoverRouters(
|
|
|
209
257
|
// Flatten prefix tree leaf nodes into precomputed entries.
|
|
210
258
|
// Leaf nodes (no children) can have their routes used directly by
|
|
211
259
|
// evaluateLazyEntry() without running the handler at runtime.
|
|
260
|
+
// Walk once into a per-router array, then fold it into the merged array;
|
|
261
|
+
// the merged and per-router entries are identical, so a second walk is
|
|
262
|
+
// redundant. Append order is preserved within and across routers.
|
|
263
|
+
const routerPrecomputed: PrecomputedEntry[] = [];
|
|
212
264
|
flattenLeafEntries(
|
|
213
265
|
manifest.prefixTree,
|
|
214
266
|
manifest.routeManifest,
|
|
215
|
-
|
|
267
|
+
routerPrecomputed,
|
|
216
268
|
);
|
|
269
|
+
newMergedPrecomputedEntries.push(...routerPrecomputed);
|
|
217
270
|
|
|
218
271
|
// Store per-router manifest and precomputed entries for isolated virtual modules.
|
|
219
272
|
newPerRouterManifestDataMap.set(id, manifest.routeManifest);
|
|
220
|
-
const routerPrecomputed: PrecomputedEntry[] = [];
|
|
221
|
-
flattenLeafEntries(
|
|
222
|
-
manifest.prefixTree,
|
|
223
|
-
manifest.routeManifest,
|
|
224
|
-
routerPrecomputed,
|
|
225
|
-
);
|
|
226
273
|
newPerRouterPrecomputedMap.set(id, routerPrecomputed);
|
|
227
274
|
|
|
228
275
|
console.log(
|
|
229
|
-
`[
|
|
276
|
+
`[rango] Router "${id}" -> ${routeCount} routes ` +
|
|
230
277
|
`(${staticRoutes} static, ${dynamicRoutes} dynamic)`,
|
|
231
278
|
);
|
|
232
279
|
}
|
|
@@ -242,7 +289,7 @@ export async function discoverRouters(
|
|
|
242
289
|
);
|
|
243
290
|
if (autoIds.length > 1) {
|
|
244
291
|
console.warn(
|
|
245
|
-
`[
|
|
292
|
+
`[rango] WARNING: ${autoIds.length} routers use auto-generated IDs (${autoIds.join(", ")}). ` +
|
|
246
293
|
`In multi-router setups, each createRouter() must have an explicit \`id\` option ` +
|
|
247
294
|
`to ensure per-router manifest data is matched correctly at runtime. ` +
|
|
248
295
|
`Example: createRouter({ id: "site", ... })`,
|
|
@@ -260,8 +307,7 @@ export async function discoverRouters(
|
|
|
260
307
|
let newMergedRouteTrie: any = null;
|
|
261
308
|
const trieStart = debug ? performance.now() : 0;
|
|
262
309
|
if (Object.keys(newMergedRouteManifest).length > 0) {
|
|
263
|
-
|
|
264
|
-
if (buildRouteTrie && mergedRouteAncestry) {
|
|
310
|
+
if (mergedRouteAncestry) {
|
|
265
311
|
// Build routeToStaticPrefix from saved manifests
|
|
266
312
|
const routeToStaticPrefix: Record<string, string> = {};
|
|
267
313
|
for (const { manifest } of allManifests) {
|
|
@@ -294,60 +340,27 @@ export async function discoverRouters(
|
|
|
294
340
|
}
|
|
295
341
|
}
|
|
296
342
|
|
|
343
|
+
// buildRouteTrie reads these via ?.has / ?.[] — empty is observationally
|
|
344
|
+
// identical to undefined, so no empty->undefined coercion is needed.
|
|
297
345
|
newMergedRouteTrie = buildRouteTrie(
|
|
298
346
|
newMergedRouteManifest,
|
|
299
347
|
mergedRouteAncestry,
|
|
300
348
|
routeToStaticPrefix,
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
passthroughRouteNames.size > 0 ? passthroughRouteNames : undefined,
|
|
306
|
-
Object.keys(mergedResponseTypeRoutes).length > 0
|
|
307
|
-
? mergedResponseTypeRoutes
|
|
308
|
-
: undefined,
|
|
349
|
+
mergedRouteTrailingSlash,
|
|
350
|
+
prerenderRouteNames,
|
|
351
|
+
passthroughRouteNames,
|
|
352
|
+
mergedResponseTypeRoutes,
|
|
309
353
|
);
|
|
310
354
|
|
|
311
|
-
// Build per-router tries for multi-router isolation.
|
|
355
|
+
// Build per-router tries for multi-router isolation. Uses the single
|
|
356
|
+
// shared buildPerRouterTrie so the production serialized trie is built by
|
|
357
|
+
// exactly the same code as the dev/HMR runtime rebuild (manifest-init.ts).
|
|
358
|
+
// Returns null for route-less manifests (route-trie.ts).
|
|
312
359
|
for (const { id, manifest } of allManifests) {
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
)
|
|
317
|
-
continue;
|
|
318
|
-
const perRouterStaticPrefix: Record<string, string> = {};
|
|
319
|
-
for (const name of Object.keys(manifest.routeManifest)) {
|
|
320
|
-
perRouterStaticPrefix[name] = "";
|
|
360
|
+
const perRouterTrie = buildPerRouterTrie(manifest);
|
|
361
|
+
if (perRouterTrie) {
|
|
362
|
+
newPerRouterTrieMap.set(id, perRouterTrie);
|
|
321
363
|
}
|
|
322
|
-
buildRouteToStaticPrefix(manifest.prefixTree, perRouterStaticPrefix);
|
|
323
|
-
|
|
324
|
-
const perRouterPrerenderNames = manifest.prerenderRoutes
|
|
325
|
-
? new Set<string>(manifest.prerenderRoutes)
|
|
326
|
-
: undefined;
|
|
327
|
-
const perRouterPassthroughNames = manifest.passthroughRoutes
|
|
328
|
-
? new Set<string>(manifest.passthroughRoutes)
|
|
329
|
-
: undefined;
|
|
330
|
-
|
|
331
|
-
const perRouterTrie = buildRouteTrie(
|
|
332
|
-
manifest.routeManifest,
|
|
333
|
-
manifest._routeAncestry,
|
|
334
|
-
perRouterStaticPrefix,
|
|
335
|
-
manifest.routeTrailingSlash &&
|
|
336
|
-
Object.keys(manifest.routeTrailingSlash).length > 0
|
|
337
|
-
? manifest.routeTrailingSlash
|
|
338
|
-
: undefined,
|
|
339
|
-
perRouterPrerenderNames && perRouterPrerenderNames.size > 0
|
|
340
|
-
? perRouterPrerenderNames
|
|
341
|
-
: undefined,
|
|
342
|
-
perRouterPassthroughNames && perRouterPassthroughNames.size > 0
|
|
343
|
-
? perRouterPassthroughNames
|
|
344
|
-
: undefined,
|
|
345
|
-
manifest.responseTypeRoutes &&
|
|
346
|
-
Object.keys(manifest.responseTypeRoutes).length > 0
|
|
347
|
-
? manifest.responseTypeRoutes
|
|
348
|
-
: undefined,
|
|
349
|
-
);
|
|
350
|
-
newPerRouterTrieMap.set(id, perRouterTrie);
|
|
351
364
|
}
|
|
352
365
|
}
|
|
353
366
|
}
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Router discovery error aggregation.
|
|
3
|
+
*
|
|
4
|
+
* During host-router discovery the lazy mounts registered by a host router are
|
|
5
|
+
* invoked to trigger each sub-app's createRouter() registration. Some mount
|
|
6
|
+
* failures are expected in the temporary discovery server context (a sub-app may
|
|
7
|
+
* reference runtime-only bindings), so each is invoked defensively and its error
|
|
8
|
+
* is collected rather than thrown.
|
|
9
|
+
*
|
|
10
|
+
* Previously these errors were discarded with an empty `catch {}`. When a real
|
|
11
|
+
* failure - typically a sub-app whose router module fails to import - left the
|
|
12
|
+
* registry empty, discovery reported the misleading "No routers found" message
|
|
13
|
+
* with no trace of the underlying cause. The collected errors are now surfaced
|
|
14
|
+
* via the `DiscoveryError` thrown at the end of discovery (issue #499).
|
|
15
|
+
*
|
|
16
|
+
* Which entries to invoke is taken from the consumer's declared intent, not
|
|
17
|
+
* inferred from the function's shape. A host route is registered either with
|
|
18
|
+
* `.map((request) => Response)` (an inline request handler, `kind: "handler"`)
|
|
19
|
+
* or `.lazy(() => import("./sub-app"))` (a lazy mount, `kind: "lazy"`). Only
|
|
20
|
+
* `kind === "lazy"` entries are invoked here; inline handlers are never invoked
|
|
21
|
+
* during discovery (they need a Request and register no routers). Because a lazy
|
|
22
|
+
* entry is known to be a module loader, ANY failure it produces - a synchronous
|
|
23
|
+
* throw or a rejected promise - is a genuine discovery failure and is collected.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
/** An error caught (and previously swallowed) while resolving host routers. */
|
|
27
|
+
export interface CaughtDiscoveryError {
|
|
28
|
+
/** Human-readable description of where the error was caught. */
|
|
29
|
+
context: string;
|
|
30
|
+
/** The caught value (an Error or otherwise). */
|
|
31
|
+
error: unknown;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Minimal shape of a host registry entry needed for mount resolution.
|
|
36
|
+
* Mirrors the runtime HostRouterRegistry value without coupling to its type.
|
|
37
|
+
*/
|
|
38
|
+
interface HostRegistryRoute {
|
|
39
|
+
handler?: unknown;
|
|
40
|
+
kind?: string;
|
|
41
|
+
}
|
|
42
|
+
interface HostRegistryEntry {
|
|
43
|
+
routes: HostRegistryRoute[];
|
|
44
|
+
fallback?: HostRegistryRoute | null;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Indent every non-empty line of `text` by `pad`. */
|
|
48
|
+
function indent(text: string, pad: string): string {
|
|
49
|
+
return text
|
|
50
|
+
.split("\n")
|
|
51
|
+
.map((line) => (line.length > 0 ? pad + line : line))
|
|
52
|
+
.join("\n");
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Invoke a single lazy mount to trigger its sub-app import (and createRouter()
|
|
57
|
+
* registration), collecting any failure under `context`. The entry is known to
|
|
58
|
+
* be a loader (`kind === "lazy"`), so both a synchronous throw and a rejected
|
|
59
|
+
* promise are genuine failures - no shape heuristics are needed.
|
|
60
|
+
*/
|
|
61
|
+
async function invokeLazyMount(
|
|
62
|
+
loader: () => unknown,
|
|
63
|
+
context: string,
|
|
64
|
+
errors: CaughtDiscoveryError[],
|
|
65
|
+
): Promise<void> {
|
|
66
|
+
try {
|
|
67
|
+
await loader();
|
|
68
|
+
} catch (error) {
|
|
69
|
+
errors.push({ context, error });
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Whether a registry route is a `.lazy()` mount with an invokable loader. */
|
|
74
|
+
function isLazyMount(
|
|
75
|
+
route: HostRegistryRoute | null | undefined,
|
|
76
|
+
): route is { handler: () => unknown; kind: "lazy" } {
|
|
77
|
+
return (
|
|
78
|
+
!!route && route.kind === "lazy" && typeof route.handler === "function"
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Invoke every lazy mount in the host registry to trigger sub-app
|
|
84
|
+
* createRouter() registration, collecting (not throwing) any failures.
|
|
85
|
+
*
|
|
86
|
+
* Only `.lazy()` entries are invoked; `.map()` inline request handlers are
|
|
87
|
+
* skipped (they need a Request and register no routers). Failures are returned
|
|
88
|
+
* rather than thrown because some mounts legitimately fail in the temporary
|
|
89
|
+
* discovery server context; the caller decides whether the failures matter,
|
|
90
|
+
* which is only when discovery finds no routers at all.
|
|
91
|
+
*/
|
|
92
|
+
export async function resolveHostRouterHandlers(
|
|
93
|
+
hostRegistry: Map<string, HostRegistryEntry>,
|
|
94
|
+
): Promise<CaughtDiscoveryError[]> {
|
|
95
|
+
const errors: CaughtDiscoveryError[] = [];
|
|
96
|
+
|
|
97
|
+
for (const [hostId, entry] of hostRegistry) {
|
|
98
|
+
for (const route of entry.routes) {
|
|
99
|
+
if (isLazyMount(route)) {
|
|
100
|
+
await invokeLazyMount(
|
|
101
|
+
route.handler,
|
|
102
|
+
`host "${hostId}" route handler`,
|
|
103
|
+
errors,
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
if (isLazyMount(entry.fallback)) {
|
|
108
|
+
await invokeLazyMount(
|
|
109
|
+
entry.fallback.handler,
|
|
110
|
+
`host "${hostId}" fallback handler`,
|
|
111
|
+
errors,
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return errors;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Build the terminal "No routers found" message, appending any errors caught
|
|
121
|
+
* during host-router discovery so the real cause is visible.
|
|
122
|
+
*
|
|
123
|
+
* The aggregated errors are inlined into the message (in addition to being
|
|
124
|
+
* attached via `cause` on `DiscoveryError`) so they survive every caller: the
|
|
125
|
+
* dev/HMR paths log `err.message`, and the build path re-throws using
|
|
126
|
+
* `err.stack`, which begins with the message. None of those callers traverse
|
|
127
|
+
* `cause`, so the message must carry the detail. Each error includes its stack
|
|
128
|
+
* when available.
|
|
129
|
+
*/
|
|
130
|
+
export function formatNoRoutersError(
|
|
131
|
+
entryPath: string | undefined,
|
|
132
|
+
errors: CaughtDiscoveryError[],
|
|
133
|
+
): string {
|
|
134
|
+
const base = `[rango] No routers found in registry after importing ${entryPath}`;
|
|
135
|
+
if (errors.length === 0) {
|
|
136
|
+
return base;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const formatted = errors
|
|
140
|
+
.map(({ context, error }) => {
|
|
141
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
142
|
+
const detail = err.stack ?? err.message;
|
|
143
|
+
return ` - while resolving ${context}:\n${indent(detail, " ")}`;
|
|
144
|
+
})
|
|
145
|
+
.join("\n");
|
|
146
|
+
|
|
147
|
+
return (
|
|
148
|
+
`${base}\n\n` +
|
|
149
|
+
`${errors.length} error(s) were caught during host-router discovery and ` +
|
|
150
|
+
`likely explain why no routers were registered:\n${formatted}`
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Reduce the caught errors to an `ErrorOptions.cause`: a single failure becomes
|
|
156
|
+
* the direct cause; multiple failures are wrapped in an `AggregateError` so
|
|
157
|
+
* each underlying error remains reachable. No errors -> no cause.
|
|
158
|
+
*/
|
|
159
|
+
function toCause(errors: CaughtDiscoveryError[]): unknown {
|
|
160
|
+
if (errors.length === 0) return undefined;
|
|
161
|
+
if (errors.length === 1) return errors[0].error;
|
|
162
|
+
return new AggregateError(
|
|
163
|
+
errors.map((e) => e.error),
|
|
164
|
+
"Multiple host-router handlers failed during discovery",
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Thrown when router discovery completes without finding any routers.
|
|
170
|
+
*
|
|
171
|
+
* Carries the entry path and the individual failures caught while resolving
|
|
172
|
+
* host-router lazy handlers. The formatted detail is embedded in `message` (for
|
|
173
|
+
* callers that log `err.message`/`err.stack`) and the underlying error(s) are
|
|
174
|
+
* also attached via `cause` (a single failure directly, multiple wrapped in an
|
|
175
|
+
* `AggregateError`) for cause-aware tooling such as the Vite error overlay.
|
|
176
|
+
*/
|
|
177
|
+
export class DiscoveryError extends Error {
|
|
178
|
+
/** The entry file that was imported before discovery gave up. */
|
|
179
|
+
readonly entryPath: string | undefined;
|
|
180
|
+
/** Individual failures caught while resolving host-router handlers. */
|
|
181
|
+
readonly caught: CaughtDiscoveryError[];
|
|
182
|
+
|
|
183
|
+
constructor(entryPath: string | undefined, caught: CaughtDiscoveryError[]) {
|
|
184
|
+
super(formatNoRoutersError(entryPath, caught));
|
|
185
|
+
const cause = toCause(caught);
|
|
186
|
+
if (cause !== undefined) {
|
|
187
|
+
this.cause = cause;
|
|
188
|
+
}
|
|
189
|
+
this.name = "DiscoveryError";
|
|
190
|
+
this.entryPath = entryPath;
|
|
191
|
+
this.caught = caught;
|
|
192
|
+
Object.setPrototypeOf(this, DiscoveryError.prototype);
|
|
193
|
+
}
|
|
194
|
+
}
|