@rangojs/router 0.0.0-experimental.bd6e11bc → 0.0.0-experimental.bdaf10aa
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 -4
- package/README.md +296 -887
- package/dist/bin/rango.js +459 -91
- package/dist/testing/vitest.js +36 -2
- package/dist/vite/index.js +1708 -414
- package/package.json +35 -10
- package/skills/api-client/SKILL.md +211 -0
- package/skills/breadcrumbs/SKILL.md +82 -5
- package/skills/bundle-analysis/SKILL.md +2 -2
- package/skills/cache-guide/SKILL.md +14 -9
- package/skills/caching/SKILL.md +221 -12
- package/skills/catalog.json +271 -0
- package/skills/comparison/SKILL.md +50 -0
- package/skills/comparison/agents/openai.yaml +4 -0
- package/skills/comparison/references/framework-comparison.md +837 -0
- package/skills/composability/SKILL.md +83 -2
- package/skills/css/SKILL.md +76 -0
- package/skills/debug-manifest/SKILL.md +5 -3
- package/skills/defer-hydration/SKILL.md +235 -0
- package/skills/document-cache/SKILL.md +11 -3
- package/skills/fonts/SKILL.md +1 -1
- package/skills/handler-use/SKILL.md +9 -9
- package/skills/hooks/SKILL.md +73 -900
- package/skills/hooks/data.md +273 -0
- package/skills/hooks/handle-and-actions.md +103 -0
- package/skills/hooks/navigation.md +110 -0
- package/skills/hooks/outlets.md +41 -0
- package/skills/hooks/state.md +228 -0
- package/skills/hooks/urls.md +135 -0
- package/skills/host-router/SKILL.md +84 -7
- package/skills/i18n/SKILL.md +1 -1
- package/skills/intercept/SKILL.md +51 -17
- package/skills/layout/SKILL.md +38 -16
- package/skills/links/SKILL.md +1 -1
- package/skills/loader/SKILL.md +48 -20
- package/skills/middleware/SKILL.md +11 -5
- package/skills/migrate-nextjs/SKILL.md +203 -20
- package/skills/migrate-react-router/SKILL.md +59 -675
- package/skills/migrate-react-router/cloudflare-workers.md +129 -0
- package/skills/migrate-react-router/component-migration.md +196 -0
- package/skills/migrate-react-router/data-and-actions.md +225 -0
- package/skills/migrate-react-router/route-mapping.md +271 -0
- package/skills/mime-routes/SKILL.md +3 -3
- package/skills/observability/SKILL.md +70 -5
- package/skills/parallel/SKILL.md +32 -8
- package/skills/ppr/SKILL.md +622 -0
- package/skills/prerender/SKILL.md +59 -28
- package/skills/rango/SKILL.md +124 -50
- package/skills/response-routes/SKILL.md +78 -46
- package/skills/route/SKILL.md +85 -6
- package/skills/router-setup/SKILL.md +41 -6
- package/skills/scripts/SKILL.md +179 -0
- package/skills/server-actions/SKILL.md +28 -3
- package/skills/shell-manifest/SKILL.md +185 -0
- package/skills/streams-and-websockets/SKILL.md +1 -1
- package/skills/tailwind/SKILL.md +28 -4
- package/skills/testing/SKILL.md +68 -654
- 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 +131 -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 +85 -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/theme/SKILL.md +1 -1
- package/skills/typesafety/SKILL.md +45 -918
- package/skills/typesafety/env-and-bindings.md +254 -0
- package/skills/typesafety/generated-files-and-cli.md +335 -0
- package/skills/typesafety/params-and-search.md +153 -0
- package/skills/typesafety/route-types.md +209 -0
- package/skills/use-cache/SKILL.md +47 -17
- package/skills/vercel/SKILL.md +128 -0
- package/skills/view-transitions/SKILL.md +44 -1
- package/src/__augment-tests__/augmented.check.ts +2 -3
- package/src/__internal.ts +0 -65
- package/src/browser/action-coordinator.ts +1 -1
- package/src/browser/action-fence.ts +47 -0
- package/src/browser/app-shell.ts +14 -27
- package/src/browser/connection-warmup.ts +134 -0
- package/src/browser/cookie-name.ts +140 -0
- package/src/browser/event-controller.ts +178 -100
- package/src/browser/invalidate-client-cache.ts +52 -0
- package/src/browser/logging.ts +28 -0
- package/src/browser/merge-segment-loaders.ts +6 -4
- package/src/browser/navigation-bridge.ts +81 -68
- package/src/browser/navigation-client.ts +115 -70
- package/src/browser/navigation-store-handle.ts +38 -0
- package/src/browser/navigation-store.ts +153 -88
- package/src/browser/navigation-transaction.ts +0 -32
- package/src/browser/network-error-handler.ts +34 -7
- package/src/browser/partial-update.ts +157 -144
- package/src/browser/prefetch/cache.ts +148 -81
- package/src/browser/prefetch/fetch.ts +231 -51
- package/src/browser/prefetch/queue.ts +25 -7
- package/src/browser/rango-state.ts +157 -115
- package/src/browser/react/Link.tsx +40 -7
- package/src/browser/react/NavigationProvider.tsx +140 -99
- package/src/browser/react/ScrollRestoration.tsx +10 -6
- package/src/browser/react/filter-segment-order.ts +17 -2
- package/src/browser/react/index.ts +0 -51
- package/src/browser/react/location-state-shared.ts +14 -15
- package/src/browser/react/location-state.ts +0 -1
- package/src/browser/react/use-action.ts +6 -15
- package/src/browser/react/use-handle.ts +0 -5
- 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 +0 -2
- package/src/browser/react/use-router.ts +6 -4
- 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 +74 -8
- package/src/browser/rsc-router.tsx +97 -22
- package/src/browser/scroll-restoration.ts +15 -8
- package/src/browser/segment-reconciler.ts +31 -21
- package/src/browser/server-action-bridge.ts +216 -38
- package/src/browser/types.ts +94 -22
- package/src/browser/validate-redirect-origin.ts +43 -16
- package/src/build/generate-manifest.ts +155 -131
- package/src/build/generate-route-types.ts +1 -1
- package/src/build/index.ts +11 -5
- package/src/build/prefix-tree-utils.ts +123 -0
- package/src/build/route-trie.ts +152 -22
- package/src/build/route-types/ast-route-extraction.ts +15 -8
- package/src/build/route-types/codegen.ts +12 -1
- package/src/build/route-types/include-resolution.ts +455 -61
- package/src/build/route-types/param-extraction.ts +6 -3
- package/src/build/route-types/per-module-writer.ts +15 -2
- package/src/build/route-types/router-processing.ts +77 -41
- package/src/build/route-types/source-scan.ts +105 -7
- package/src/build/runtime-discovery.ts +4 -1
- package/src/cache/cache-error.ts +104 -0
- package/src/cache/cache-key-utils.ts +58 -13
- package/src/cache/cache-policy.ts +108 -34
- package/src/cache/cache-runtime.ts +454 -101
- package/src/cache/cache-scope.ts +159 -54
- package/src/cache/cache-tag.ts +149 -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 +2170 -377
- 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 +126 -41
- package/src/cache/handle-snapshot.ts +70 -0
- package/src/cache/index.ts +23 -20
- package/src/cache/memory-segment-store.ts +243 -37
- package/src/cache/profile-registry.ts +46 -31
- package/src/cache/read-through-swr.ts +56 -12
- package/src/cache/segment-codec.ts +13 -21
- package/src/cache/shell-snapshot.ts +417 -0
- package/src/cache/tag-invalidation.ts +230 -0
- package/src/cache/types.ts +194 -99
- package/src/cache/vercel/index.ts +11 -0
- package/src/cache/vercel/vercel-cache-store.ts +1132 -0
- package/src/client.rsc.tsx +39 -22
- package/src/client.tsx +28 -58
- package/src/cloudflare/index.ts +11 -0
- package/src/cloudflare/tracing.ts +108 -0
- package/src/component-utils.ts +19 -0
- package/src/components/DefaultDocument.tsx +8 -2
- package/src/context-var.ts +13 -1
- package/src/decode-loader-results.ts +18 -2
- package/src/defer.ts +185 -0
- package/src/deps/ssr.ts +0 -1
- package/src/encode-kv.ts +49 -0
- package/src/errors.ts +0 -3
- package/src/escape-script.ts +52 -0
- package/src/handle.ts +57 -40
- package/src/handles/MetaTags.tsx +24 -53
- package/src/handles/Scripts.tsx +183 -0
- package/src/handles/breadcrumbs.ts +35 -8
- package/src/handles/deferred-resolution.ts +127 -0
- package/src/handles/is-thenable.ts +18 -0
- package/src/handles/meta.ts +14 -40
- package/src/handles/script.ts +244 -0
- package/src/host/cookie-handler.ts +9 -60
- package/src/host/errors.ts +13 -22
- package/src/host/index.ts +7 -0
- package/src/host/pattern-matcher.ts +23 -52
- package/src/host/router.ts +1 -65
- package/src/host/testing.ts +40 -27
- package/src/host/types.ts +6 -2
- package/src/href-client.ts +7 -12
- package/src/index.rsc.ts +88 -8
- package/src/index.ts +90 -16
- package/src/internal-debug.ts +11 -10
- package/src/loader.rsc.ts +19 -9
- package/src/loader.ts +12 -4
- 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 +75 -7
- package/src/redirect-origin.ts +114 -0
- package/src/regex-escape.ts +8 -0
- package/src/render-error-thrower.tsx +20 -0
- package/src/response-utils.ts +25 -0
- package/src/root-error-boundary.tsx +1 -19
- package/src/route-content-wrapper.tsx +13 -49
- package/src/route-definition/dsl-helpers.ts +60 -53
- package/src/route-definition/helper-factories.ts +0 -2
- package/src/route-definition/helpers-types.ts +46 -46
- package/src/route-definition/index.ts +1 -2
- package/src/route-definition/redirect.ts +44 -11
- package/src/route-definition/resolve-handler-use.ts +6 -1
- package/src/route-definition/use-item-types.ts +3 -6
- package/src/route-map-builder.ts +41 -20
- package/src/route-types.ts +0 -5
- package/src/router/content-negotiation.ts +58 -23
- package/src/router/error-handling.ts +44 -17
- package/src/router/find-match.ts +129 -30
- package/src/router/handler-context.ts +6 -1
- package/src/router/instrument.ts +355 -0
- package/src/router/intercept-resolution.ts +35 -2
- package/src/router/lazy-includes.ts +79 -56
- package/src/router/loader-resolution.ts +151 -73
- package/src/router/logging.ts +0 -6
- package/src/router/manifest.ts +74 -40
- package/src/router/match-api.ts +76 -52
- package/src/router/match-context.ts +0 -22
- package/src/router/match-handlers.ts +181 -178
- package/src/router/match-middleware/background-revalidation.ts +40 -24
- package/src/router/match-middleware/cache-lookup.ts +115 -194
- package/src/router/match-middleware/cache-store.ts +61 -50
- 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 +36 -67
- package/src/router/metrics.ts +0 -34
- package/src/router/middleware-types.ts +0 -116
- package/src/router/middleware.ts +231 -120
- package/src/router/navigation-snapshot.ts +7 -56
- package/src/router/params-util.ts +23 -0
- package/src/router/parse-pattern.ts +115 -0
- package/src/router/pattern-matching.ts +99 -152
- package/src/router/prefetch-cache-ttl.ts +51 -0
- package/src/router/prefetch-limits.ts +37 -0
- package/src/router/prerender-match.ts +111 -66
- package/src/router/preview-match.ts +3 -1
- package/src/router/request-classification.ts +47 -42
- package/src/router/revalidation.ts +75 -81
- package/src/router/route-snapshot.ts +14 -3
- package/src/router/router-context.ts +6 -29
- package/src/router/router-interfaces.ts +70 -8
- package/src/router/router-options.ts +126 -4
- package/src/router/segment-resolution/fresh.ts +104 -80
- package/src/router/segment-resolution/helpers.ts +86 -6
- package/src/router/segment-resolution/loader-cache.ts +155 -39
- package/src/router/segment-resolution/loader-mask.ts +60 -0
- package/src/router/segment-resolution/loader-snapshot.ts +259 -0
- package/src/router/segment-resolution/mask-nested.ts +83 -0
- package/src/router/segment-resolution/revalidation.ts +215 -304
- 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 +35 -15
- 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 +54 -35
- package/src/router/telemetry-otel.ts +160 -200
- package/src/router/telemetry.ts +9 -23
- package/src/router/timeout.ts +0 -20
- package/src/router/tracing.ts +215 -0
- package/src/router/trie-matching.ts +171 -64
- package/src/router/types.ts +1 -63
- package/src/router/url-params.ts +13 -5
- package/src/router.ts +119 -48
- package/src/rsc/full-payload.ts +70 -0
- package/src/rsc/handler-context.ts +1 -0
- package/src/rsc/handler.ts +267 -152
- package/src/rsc/helpers.ts +78 -4
- package/src/rsc/index.ts +1 -4
- package/src/rsc/json-route-result.ts +38 -0
- package/src/rsc/loader-fetch.ts +114 -38
- package/src/rsc/manifest-init.ts +29 -42
- package/src/rsc/nonce.ts +10 -1
- package/src/rsc/origin-guard.ts +11 -15
- package/src/rsc/progressive-enhancement.ts +120 -13
- package/src/rsc/redirect-guard.ts +100 -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 +58 -141
- package/src/rsc/rsc-rendering.ts +492 -49
- package/src/rsc/runtime-warnings.ts +14 -0
- package/src/rsc/server-action.ts +268 -82
- package/src/rsc/shell-capture.ts +1190 -0
- package/src/rsc/shell-serve.ts +181 -0
- package/src/rsc/transition-gate.ts +89 -0
- package/src/rsc/types.ts +45 -3
- package/src/runtime-env.ts +18 -0
- package/src/search-params.ts +31 -26
- package/src/segment-loader-promise.ts +49 -4
- package/src/segment-system.tsx +260 -95
- package/src/server/context.ts +99 -9
- package/src/server/cookie-parse.ts +32 -0
- package/src/server/cookie-store.ts +125 -2
- package/src/server/handle-store.ts +21 -38
- package/src/server/loader-registry.ts +33 -42
- package/src/server/request-context.ts +379 -138
- package/src/ssr/index.tsx +491 -182
- package/src/ssr/inject-rsc-eager.ts +167 -0
- package/src/ssr/ssr-root.tsx +228 -0
- package/src/static-handler.ts +10 -13
- package/src/testing/cache-status.ts +44 -48
- package/src/testing/collect-handle.ts +14 -31
- package/src/testing/dispatch.ts +533 -160
- package/src/testing/e2e/fixture.ts +45 -11
- package/src/testing/e2e/index.ts +1 -22
- package/src/testing/e2e/matchers.ts +0 -16
- package/src/testing/e2e/parity.ts +85 -4
- package/src/testing/e2e/server.ts +12 -0
- package/src/testing/flight-matchers.ts +7 -14
- package/src/testing/flight-normalize.ts +11 -0
- package/src/testing/flight-runtime.d.ts +36 -0
- package/src/testing/flight-tree.ts +682 -0
- package/src/testing/flight.entry.ts +30 -0
- package/src/testing/flight.ts +145 -70
- package/src/testing/generated-routes.ts +26 -50
- package/src/testing/index.ts +18 -19
- package/src/testing/internal/context.ts +184 -68
- 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 +134 -115
- package/src/testing/run-loader.ts +140 -51
- package/src/testing/run-middleware.ts +59 -33
- package/src/testing/run-transition-when.ts +164 -0
- package/src/testing/vitest-stubs/cloudflare-email.ts +1 -1
- package/src/testing/vitest-stubs/cloudflare-workers.ts +1 -1
- package/src/testing/vitest.ts +138 -16
- package/src/theme/ThemeProvider.tsx +56 -84
- 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 +15 -15
- package/src/types/handler-context.ts +45 -15
- package/src/types/index.ts +2 -10
- package/src/types/loader-types.ts +6 -3
- package/src/types/request-scope.ts +8 -22
- package/src/types/route-config.ts +20 -52
- package/src/types/route-entry.ts +0 -6
- package/src/types/segments.ts +100 -13
- package/src/urls/include-helper.ts +10 -12
- package/src/urls/include-provider.ts +71 -0
- package/src/urls/index.ts +2 -8
- package/src/urls/path-helper-types.ts +52 -14
- package/src/urls/path-helper.ts +5 -54
- package/src/urls/pattern-types.ts +36 -0
- package/src/urls/type-extraction.ts +76 -42
- package/src/urls/urls-function.ts +0 -14
- package/src/use-loader.tsx +0 -186
- package/src/vercel/index.ts +11 -0
- package/src/vercel/tracing.ts +88 -0
- package/src/vite/discovery/bundle-postprocess.ts +2 -1
- package/src/vite/discovery/dev-prerender-cache.ts +117 -0
- package/src/vite/discovery/discover-routers.ts +34 -43
- package/src/vite/discovery/discovery-errors.ts +61 -0
- package/src/vite/discovery/prerender-collection.ts +33 -46
- package/src/vite/discovery/state.ts +12 -1
- package/src/vite/discovery/virtual-module-codegen.ts +1 -11
- package/src/vite/index.ts +9 -0
- package/src/vite/inject-client-debug.ts +88 -0
- package/src/vite/plugin-types.ts +143 -10
- package/src/vite/plugins/cjs-to-esm.ts +8 -12
- package/src/vite/plugins/client-ref-dedup.ts +0 -11
- package/src/vite/plugins/client-ref-hashing.ts +0 -10
- package/src/vite/plugins/cloudflare-protocol-stub.ts +0 -20
- package/src/vite/plugins/expose-action-id.ts +2 -73
- package/src/vite/plugins/expose-id-utils.ts +85 -56
- package/src/vite/plugins/expose-ids/export-analysis.ts +30 -43
- package/src/vite/plugins/expose-ids/handler-transform.ts +5 -31
- package/src/vite/plugins/expose-ids/loader-transform.ts +12 -20
- package/src/vite/plugins/expose-ids/router-transform.ts +98 -26
- package/src/vite/plugins/expose-internal-ids.ts +10 -1
- package/src/vite/plugins/performance-tracks.ts +0 -3
- package/src/vite/plugins/refresh-cmd.ts +1 -1
- package/src/vite/plugins/use-cache-transform.ts +21 -46
- package/src/vite/plugins/vercel-output.ts +384 -0
- package/src/vite/plugins/version-injector.ts +22 -27
- package/src/vite/plugins/version-plugin.ts +6 -66
- package/src/vite/plugins/virtual-entries.ts +137 -26
- package/src/vite/rango.ts +146 -135
- package/src/vite/router-discovery.ts +189 -48
- package/src/vite/utils/ast-handler-extract.ts +11 -20
- package/src/vite/utils/bundle-analysis.ts +6 -13
- package/src/vite/utils/client-chunks.ts +0 -6
- package/src/vite/utils/directive-prologue.ts +40 -0
- package/src/vite/utils/forward-user-plugins.ts +0 -22
- package/src/vite/utils/manifest-utils.ts +4 -75
- package/src/vite/utils/package-resolution.ts +1 -73
- package/src/vite/utils/prerender-utils.ts +71 -44
- package/src/vite/utils/shared-utils.ts +55 -37
- 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
|
@@ -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,
|
|
@@ -107,7 +117,10 @@ export async function discoverRouters(
|
|
|
107
117
|
}
|
|
108
118
|
}
|
|
109
119
|
|
|
110
|
-
//
|
|
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.
|
|
111
124
|
const buildMod = await timed(
|
|
112
125
|
debug,
|
|
113
126
|
"inner: import @rangojs/router/build",
|
|
@@ -158,11 +171,12 @@ export async function discoverRouters(
|
|
|
158
171
|
// are NOT in EntryData, so generateManifestFull's walk misses them. Collect any
|
|
159
172
|
// "use client" default boundary directly off the router instance. The value is
|
|
160
173
|
// commonly a handler function wrapping the client boundary in server providers,
|
|
161
|
-
// so collectFallbackClientRefs invokes + walks the tree.
|
|
162
|
-
// so it
|
|
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.
|
|
163
177
|
const collectFromBoundaryNode = (node: unknown): void => {
|
|
164
|
-
if (collectClientFallbackRef
|
|
165
|
-
|
|
178
|
+
if (collectClientFallbackRef) {
|
|
179
|
+
collectFallbackClientRefs(node, collectClientFallbackRef);
|
|
166
180
|
}
|
|
167
181
|
};
|
|
168
182
|
|
|
@@ -172,7 +186,7 @@ export async function discoverRouters(
|
|
|
172
186
|
continue;
|
|
173
187
|
}
|
|
174
188
|
|
|
175
|
-
const manifest = generateManifestFull(
|
|
189
|
+
const manifest = await generateManifestFull(
|
|
176
190
|
router.urlpatterns,
|
|
177
191
|
routerMountIndex,
|
|
178
192
|
{
|
|
@@ -243,20 +257,19 @@ export async function discoverRouters(
|
|
|
243
257
|
// Flatten prefix tree leaf nodes into precomputed entries.
|
|
244
258
|
// Leaf nodes (no children) can have their routes used directly by
|
|
245
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[] = [];
|
|
246
264
|
flattenLeafEntries(
|
|
247
265
|
manifest.prefixTree,
|
|
248
266
|
manifest.routeManifest,
|
|
249
|
-
|
|
267
|
+
routerPrecomputed,
|
|
250
268
|
);
|
|
269
|
+
newMergedPrecomputedEntries.push(...routerPrecomputed);
|
|
251
270
|
|
|
252
271
|
// Store per-router manifest and precomputed entries for isolated virtual modules.
|
|
253
272
|
newPerRouterManifestDataMap.set(id, manifest.routeManifest);
|
|
254
|
-
const routerPrecomputed: PrecomputedEntry[] = [];
|
|
255
|
-
flattenLeafEntries(
|
|
256
|
-
manifest.prefixTree,
|
|
257
|
-
manifest.routeManifest,
|
|
258
|
-
routerPrecomputed,
|
|
259
|
-
);
|
|
260
273
|
newPerRouterPrecomputedMap.set(id, routerPrecomputed);
|
|
261
274
|
|
|
262
275
|
console.log(
|
|
@@ -294,8 +307,7 @@ export async function discoverRouters(
|
|
|
294
307
|
let newMergedRouteTrie: any = null;
|
|
295
308
|
const trieStart = debug ? performance.now() : 0;
|
|
296
309
|
if (Object.keys(newMergedRouteManifest).length > 0) {
|
|
297
|
-
|
|
298
|
-
if (buildRouteTrie && mergedRouteAncestry) {
|
|
310
|
+
if (mergedRouteAncestry) {
|
|
299
311
|
// Build routeToStaticPrefix from saved manifests
|
|
300
312
|
const routeToStaticPrefix: Record<string, string> = {};
|
|
301
313
|
for (const { manifest } of allManifests) {
|
|
@@ -340,36 +352,15 @@ export async function discoverRouters(
|
|
|
340
352
|
mergedResponseTypeRoutes,
|
|
341
353
|
);
|
|
342
354
|
|
|
343
|
-
// 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).
|
|
344
359
|
for (const { id, manifest } of allManifests) {
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
)
|
|
349
|
-
continue;
|
|
350
|
-
const perRouterStaticPrefix: Record<string, string> = {};
|
|
351
|
-
for (const name of Object.keys(manifest.routeManifest)) {
|
|
352
|
-
perRouterStaticPrefix[name] = "";
|
|
360
|
+
const perRouterTrie = buildPerRouterTrie(manifest);
|
|
361
|
+
if (perRouterTrie) {
|
|
362
|
+
newPerRouterTrieMap.set(id, perRouterTrie);
|
|
353
363
|
}
|
|
354
|
-
buildRouteToStaticPrefix(manifest.prefixTree, perRouterStaticPrefix);
|
|
355
|
-
|
|
356
|
-
const perRouterPrerenderNames = manifest.prerenderRoutes
|
|
357
|
-
? new Set<string>(manifest.prerenderRoutes)
|
|
358
|
-
: undefined;
|
|
359
|
-
const perRouterPassthroughNames = manifest.passthroughRoutes
|
|
360
|
-
? new Set<string>(manifest.passthroughRoutes)
|
|
361
|
-
: undefined;
|
|
362
|
-
|
|
363
|
-
const perRouterTrie = buildRouteTrie(
|
|
364
|
-
manifest.routeManifest,
|
|
365
|
-
manifest._routeAncestry,
|
|
366
|
-
perRouterStaticPrefix,
|
|
367
|
-
manifest.routeTrailingSlash,
|
|
368
|
-
perRouterPrerenderNames,
|
|
369
|
-
perRouterPassthroughNames,
|
|
370
|
-
manifest.responseTypeRoutes,
|
|
371
|
-
);
|
|
372
|
-
newPerRouterTrieMap.set(id, perRouterTrie);
|
|
373
364
|
}
|
|
374
365
|
}
|
|
375
366
|
}
|
|
@@ -192,3 +192,64 @@ export class DiscoveryError extends Error {
|
|
|
192
192
|
Object.setPrototypeOf(this, DiscoveryError.prototype);
|
|
193
193
|
}
|
|
194
194
|
}
|
|
195
|
+
|
|
196
|
+
/** How the dev caller should surface a discovery failure. */
|
|
197
|
+
export interface DiscoveryFailureReport {
|
|
198
|
+
level: "error" | "warn";
|
|
199
|
+
message: string;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Decide how to surface a dev-boot discovery failure in the terminal.
|
|
204
|
+
*
|
|
205
|
+
* The bare "no routers found" case (a DiscoveryError with no caught host-handler
|
|
206
|
+
* failures) is ambiguous. It is either:
|
|
207
|
+
* - a genuine misconfiguration — the entry never calls createRouter(), or the
|
|
208
|
+
* configured entry path is wrong; or
|
|
209
|
+
* - a transient artifact of a Vite dependency re-optimization racing with boot
|
|
210
|
+
* discovery (a module read before the entry import resolves to the
|
|
211
|
+
* pre-optimize copy of the runner graph while createRouter() populated the
|
|
212
|
+
* post-optimize copy — see router-discovery.ts).
|
|
213
|
+
*
|
|
214
|
+
* We can only tell them apart when the caller observed the dep optimizer's
|
|
215
|
+
* `browserHash` change across the discovery attempt (`reoptimizeObserved`): a
|
|
216
|
+
* reload-causing re-optimization landed mid-flight, so the empty read was
|
|
217
|
+
* transient and the app self-heals per-request (handler.ts builds the trie from
|
|
218
|
+
* the router's live urlpatterns) with discovery re-running on the next boot.
|
|
219
|
+
*
|
|
220
|
+
* A DiscoveryError that DOES carry caught host-handler failures already embeds
|
|
221
|
+
* the real cause in its message, and any non-DiscoveryError is a hard failure;
|
|
222
|
+
* both stay loud with full detail.
|
|
223
|
+
*/
|
|
224
|
+
export function describeDiscoveryFailure(
|
|
225
|
+
err: unknown,
|
|
226
|
+
opts: { reoptimizeObserved?: boolean } = {},
|
|
227
|
+
): DiscoveryFailureReport {
|
|
228
|
+
if (err instanceof DiscoveryError && err.caught.length === 0) {
|
|
229
|
+
const entry = err.entryPath ?? "the router entry";
|
|
230
|
+
if (opts.reoptimizeObserved) {
|
|
231
|
+
return {
|
|
232
|
+
level: "warn",
|
|
233
|
+
message:
|
|
234
|
+
`[rango] No routers found while Vite was re-optimizing dependencies on ` +
|
|
235
|
+
`dev boot. This is transient: routes are served per-request and ` +
|
|
236
|
+
`discovery re-runs automatically, so it clears on the next boot. If ` +
|
|
237
|
+
`routes still 404, confirm ${entry} calls createRouter().`,
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
return {
|
|
241
|
+
level: "error",
|
|
242
|
+
message:
|
|
243
|
+
`${err.message}\n` +
|
|
244
|
+
` Ensure ${entry} calls createRouter() at module top level and that the ` +
|
|
245
|
+
`configured router entry path is correct.`,
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const e = err as { stack?: string; message?: string };
|
|
250
|
+
const detail = e?.stack ?? e?.message ?? String(err);
|
|
251
|
+
return {
|
|
252
|
+
level: "error",
|
|
253
|
+
message: `[rango] Router discovery failed: ${detail}`,
|
|
254
|
+
};
|
|
255
|
+
}
|
|
@@ -6,13 +6,14 @@
|
|
|
6
6
|
* generation.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import { contextSet } from "../../context-var.js";
|
|
9
|
+
import { contextSet, hasContextVars } from "../../context-var.js";
|
|
10
10
|
import {
|
|
11
11
|
encodePathParam,
|
|
12
12
|
substituteRouteParams,
|
|
13
13
|
runWithConcurrency,
|
|
14
14
|
groupByConcurrency,
|
|
15
15
|
notifyOnError,
|
|
16
|
+
resolvePrerenderError,
|
|
16
17
|
stageBuildAssetModule,
|
|
17
18
|
} from "../utils/prerender-utils.js";
|
|
18
19
|
import type { DiscoveryState } from "./state.js";
|
|
@@ -135,9 +136,7 @@ export async function expandPrerenderRoutes(
|
|
|
135
136
|
(performance.now() - getParamsStart).toFixed(1),
|
|
136
137
|
);
|
|
137
138
|
const concurrency = def.options?.concurrency ?? 1;
|
|
138
|
-
const hasBuildVars =
|
|
139
|
-
Object.keys(buildVars).length > 0 ||
|
|
140
|
-
Object.getOwnPropertySymbols(buildVars).length > 0;
|
|
139
|
+
const hasBuildVars = hasContextVars(buildVars);
|
|
141
140
|
for (const params of paramsList) {
|
|
142
141
|
let url = substituteRouteParams(
|
|
143
142
|
pattern,
|
|
@@ -233,6 +232,10 @@ export async function expandPrerenderRoutes(
|
|
|
233
232
|
const manifestEntries: Record<string, string> = {};
|
|
234
233
|
let doneCount = 0;
|
|
235
234
|
let skipCount = 0;
|
|
235
|
+
// #587: a render error reaches here (matchForPrerender now re-throws it rather
|
|
236
|
+
// than baking the error boundary). Default "fail" fails the build; "warn" logs
|
|
237
|
+
// and skips baking the URL.
|
|
238
|
+
const prerenderOnError = state.opts?.prerenderOnError ?? "fail";
|
|
236
239
|
const startTotal = performance.now();
|
|
237
240
|
|
|
238
241
|
// Group entries by concurrency for batched rendering.
|
|
@@ -282,10 +285,9 @@ export async function expandPrerenderRoutes(
|
|
|
282
285
|
const interceptKey = `${result.routeName}/${paramHash}/i`;
|
|
283
286
|
const interceptValue = JSON.stringify({
|
|
284
287
|
segments: [...result.segments, ...result.interceptSegments],
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
},
|
|
288
|
+
// interceptHandles is the pre-encoded MERGED (main + intercept)
|
|
289
|
+
// handle string (the producer merged before encoding).
|
|
290
|
+
handles: result.interceptHandles ?? "",
|
|
289
291
|
});
|
|
290
292
|
manifestEntries[interceptKey] = stageBuildAssetModule(
|
|
291
293
|
state.projectRoot,
|
|
@@ -300,35 +302,22 @@ export async function expandPrerenderRoutes(
|
|
|
300
302
|
doneCount++;
|
|
301
303
|
break;
|
|
302
304
|
} catch (err: any) {
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
`[rango] SKIP ${entry.urlPath.padEnd(40)} (${elapsed}ms) - ${err.message}`,
|
|
307
|
-
);
|
|
308
|
-
skipCount++;
|
|
309
|
-
notifyOnError(
|
|
310
|
-
registry,
|
|
311
|
-
err,
|
|
312
|
-
"prerender",
|
|
313
|
-
entry.routeName,
|
|
314
|
-
entry.urlPath,
|
|
315
|
-
true,
|
|
316
|
-
);
|
|
317
|
-
break;
|
|
318
|
-
}
|
|
319
|
-
// Regular error: log, notify, and fail the build
|
|
305
|
+
// Skip, or a render error under prerender.onError "warn": skip the
|
|
306
|
+
// URL (never bake the error page, #587). A render error under the
|
|
307
|
+
// default "fail" re-throws below to fail the build.
|
|
320
308
|
const elapsed = (performance.now() - startUrl).toFixed(0);
|
|
321
|
-
|
|
322
|
-
`[rango] FAIL ${entry.urlPath.padEnd(40)} (${elapsed}ms) - ${err.message}`,
|
|
323
|
-
);
|
|
324
|
-
notifyOnError(
|
|
309
|
+
resolvePrerenderError(
|
|
325
310
|
registry,
|
|
326
311
|
err,
|
|
312
|
+
prerenderOnError,
|
|
313
|
+
entry.urlPath.padEnd(40),
|
|
314
|
+
elapsed,
|
|
327
315
|
"prerender",
|
|
328
316
|
entry.routeName,
|
|
329
317
|
entry.urlPath,
|
|
330
318
|
);
|
|
331
|
-
|
|
319
|
+
skipCount++;
|
|
320
|
+
break;
|
|
332
321
|
}
|
|
333
322
|
}
|
|
334
323
|
},
|
|
@@ -381,6 +370,7 @@ export async function renderStaticHandlers(
|
|
|
381
370
|
let staticDone = 0;
|
|
382
371
|
let staticSkip = 0;
|
|
383
372
|
let totalStaticCount = 0;
|
|
373
|
+
const prerenderOnError = state.opts?.prerenderOnError ?? "fail";
|
|
384
374
|
|
|
385
375
|
// Count handlers for the log header
|
|
386
376
|
for (const [, exportNames] of state.resolvedStaticModules) {
|
|
@@ -420,7 +410,8 @@ export async function renderStaticHandlers(
|
|
|
420
410
|
!state.isBuildMode,
|
|
421
411
|
);
|
|
422
412
|
if (result) {
|
|
423
|
-
|
|
413
|
+
// result.handles is the pre-encoded handle string ("" when none).
|
|
414
|
+
const hasHandles = result.handles !== "";
|
|
424
415
|
const exportValue = hasHandles
|
|
425
416
|
? JSON.stringify(result)
|
|
426
417
|
: JSON.stringify(result.encoded);
|
|
@@ -436,23 +427,19 @@ export async function renderStaticHandlers(
|
|
|
436
427
|
break;
|
|
437
428
|
}
|
|
438
429
|
} catch (err: any) {
|
|
439
|
-
|
|
440
|
-
const elapsed = (performance.now() - startHandler).toFixed(0);
|
|
441
|
-
console.log(
|
|
442
|
-
`[rango] SKIP ${name.padEnd(40)} (${elapsed}ms) - ${err.message}`,
|
|
443
|
-
);
|
|
444
|
-
staticSkip++;
|
|
445
|
-
notifyOnError(registry, err, "static", undefined, undefined, true);
|
|
446
|
-
handled = true;
|
|
447
|
-
break;
|
|
448
|
-
}
|
|
449
|
-
// Regular error: log, notify, and fail the build
|
|
430
|
+
// Same Skip/warn/fail policy as the prerender loop (shared helper).
|
|
450
431
|
const elapsed = (performance.now() - startHandler).toFixed(0);
|
|
451
|
-
|
|
452
|
-
|
|
432
|
+
resolvePrerenderError(
|
|
433
|
+
registry,
|
|
434
|
+
err,
|
|
435
|
+
prerenderOnError,
|
|
436
|
+
name.padEnd(40),
|
|
437
|
+
elapsed,
|
|
438
|
+
"static",
|
|
453
439
|
);
|
|
454
|
-
|
|
455
|
-
|
|
440
|
+
staticSkip++;
|
|
441
|
+
handled = true;
|
|
442
|
+
break;
|
|
456
443
|
}
|
|
457
444
|
}
|
|
458
445
|
if (!handled) {
|
|
@@ -19,7 +19,18 @@ export interface PluginOptions {
|
|
|
19
19
|
/** Build-time env option from rango() config. */
|
|
20
20
|
buildEnv?: import("../plugin-types.js").BuildEnvOption;
|
|
21
21
|
/** Deployment preset (needed for buildEnv "auto" resolution). */
|
|
22
|
-
preset?: "node" | "cloudflare";
|
|
22
|
+
preset?: "node" | "cloudflare" | "vercel";
|
|
23
|
+
/**
|
|
24
|
+
* Route-discovery scan filter (glob include/exclude) from rango() config.
|
|
25
|
+
* Compiled into `DiscoveryState.scanFilter` once `projectRoot` is known.
|
|
26
|
+
*/
|
|
27
|
+
discovery?: { include?: string[]; exclude?: string[] };
|
|
28
|
+
/**
|
|
29
|
+
* What to do when a Prerender/Static render throws at build time, from
|
|
30
|
+
* rango({ prerender: { onError } }). "fail" (default) fails the build; "warn"
|
|
31
|
+
* logs and skips baking the URL. See {@link import("../plugin-types.js").RangoOptions}.
|
|
32
|
+
*/
|
|
33
|
+
prerenderOnError?: "fail" | "warn";
|
|
23
34
|
/**
|
|
24
35
|
* Shared context the built-in clientChunks strategy reads. Discovery populates
|
|
25
36
|
* it (registered fallback hashes + single-router name) before the client build
|
|
@@ -49,7 +49,6 @@ export function generateRoutesManifestModule(state: DiscoveryState): string {
|
|
|
49
49
|
);
|
|
50
50
|
genFileVars.push(varName);
|
|
51
51
|
} else {
|
|
52
|
-
// Routers without sourceFile: inline their manifest data directly
|
|
53
52
|
routersWithoutGenFile.push({
|
|
54
53
|
id: entry.id,
|
|
55
54
|
manifest: entry.routeManifest,
|
|
@@ -68,14 +67,12 @@ export function generateRoutesManifestModule(state: DiscoveryState): string {
|
|
|
68
67
|
`clearAllRouterData();`,
|
|
69
68
|
];
|
|
70
69
|
|
|
71
|
-
// Flatten NamedRoutes entries: search schema objects -> plain string paths
|
|
72
70
|
if (genFileVars.length > 0) {
|
|
73
71
|
lines.push(
|
|
74
72
|
`function __flat(r) { const o = {}; for (const [k, v] of Object.entries(r)) o[k] = typeof v === "string" ? v : v.path; return o; }`,
|
|
75
73
|
);
|
|
76
74
|
}
|
|
77
75
|
|
|
78
|
-
// Build the merged manifest from gen file imports + inlined data
|
|
79
76
|
if (genFileVars.length === 1 && routersWithoutGenFile.length === 0) {
|
|
80
77
|
lines.push(`setCachedManifest(__flat(${genFileVars[0]}));`);
|
|
81
78
|
} else {
|
|
@@ -86,7 +83,6 @@ export function generateRoutesManifestModule(state: DiscoveryState): string {
|
|
|
86
83
|
lines.push(`setCachedManifest({ ${parts.join(", ")} });`);
|
|
87
84
|
}
|
|
88
85
|
|
|
89
|
-
// Set per-router manifests
|
|
90
86
|
let genVarIdx = 0;
|
|
91
87
|
for (const entry of state.perRouterManifests) {
|
|
92
88
|
if (entry.sourceFile) {
|
|
@@ -114,8 +110,6 @@ export function generateRoutesManifestModule(state: DiscoveryState): string {
|
|
|
114
110
|
// against live router.urlpatterns, which is always correct after a
|
|
115
111
|
// program reload.
|
|
116
112
|
|
|
117
|
-
// Register lazy loaders for per-router manifest modules.
|
|
118
|
-
// Each import() uses a static string literal so Rollup creates separate chunks.
|
|
119
113
|
for (const routerId of state.perRouterManifestDataMap.keys()) {
|
|
120
114
|
lines.push(
|
|
121
115
|
`registerRouterManifestLoader(${JSON.stringify(routerId)}, () => import(${JSON.stringify(VIRTUAL_ROUTES_MANIFEST_ID + "/" + routerId)}));`,
|
|
@@ -129,9 +123,6 @@ export function generateRoutesManifestModule(state: DiscoveryState): string {
|
|
|
129
123
|
return lines.join("\n");
|
|
130
124
|
}
|
|
131
125
|
|
|
132
|
-
// No manifest: either discovery hasn't completed or no runner (Cloudflare dev).
|
|
133
|
-
// Still inject __PRERENDER_DEV_URL so the prerender store can fetch on-demand.
|
|
134
|
-
// Re-resolve origin now since the server is listening by module load time.
|
|
135
126
|
if (!state.isBuildMode) {
|
|
136
127
|
const origin =
|
|
137
128
|
state.devServerOrigin ||
|
|
@@ -160,7 +151,6 @@ export function generatePerRouterModule(
|
|
|
160
151
|
const lines: string[] = [];
|
|
161
152
|
|
|
162
153
|
if (routerEntry?.sourceFile) {
|
|
163
|
-
// Import manifest from the gen file so HMR auto-propagates
|
|
164
154
|
const routerDir = dirname(routerEntry.sourceFile);
|
|
165
155
|
const routerBasename = basename(routerEntry.sourceFile).replace(
|
|
166
156
|
/\.(tsx?|jsx?)$/,
|
|
@@ -189,5 +179,5 @@ export function generatePerRouterModule(
|
|
|
189
179
|
`export const precomputedEntries = ${jsonParseExpression(entries)};`,
|
|
190
180
|
);
|
|
191
181
|
}
|
|
192
|
-
return lines.join("\n") || "
|
|
182
|
+
return lines.join("\n") || "";
|
|
193
183
|
}
|
package/src/vite/index.ts
CHANGED
|
@@ -8,10 +8,19 @@
|
|
|
8
8
|
|
|
9
9
|
export { rango } from "./rango.js";
|
|
10
10
|
export { poke } from "./plugins/refresh-cmd.js";
|
|
11
|
+
// The built-in clientChunks strategy, exported so a custom `clientChunks`
|
|
12
|
+
// function can OVERLAY it (route a few modules to a dedicated chunk, delegate
|
|
13
|
+
// the rest) instead of replacing the whole route/marker grouping. Without this
|
|
14
|
+
// a consumer override silently loses app-fallback/route splitting for the
|
|
15
|
+
// entire app. Note: called without a ClientChunkContext the fallbackRefs-based
|
|
16
|
+
// `app-fallback` split is inactive — discovery wires it only for the built-in.
|
|
17
|
+
export { directoryClientChunks } from "./utils/client-chunks.js";
|
|
11
18
|
|
|
12
19
|
export type {
|
|
13
20
|
RangoNodeOptions,
|
|
14
21
|
RangoCloudflareOptions,
|
|
22
|
+
RangoVercelOptions,
|
|
23
|
+
VercelPresetOptions,
|
|
15
24
|
RangoOptions,
|
|
16
25
|
ClientChunks,
|
|
17
26
|
ClientChunkMeta,
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import type { Connect } from "vite";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Bake the resolved INTERNAL_RANGO_DEBUG value into the router's `internal-debug`
|
|
5
|
+
* module so the flag reaches the CLIENT debug logs by just setting the env var.
|
|
6
|
+
*
|
|
7
|
+
* internal-debug.ts normally reads the flag via `typeof __RANGO_DEBUG__`, a Vite
|
|
8
|
+
* define. That delivery is unreliable on the client: in dev Vite ships the define
|
|
9
|
+
* only as an injected global whose presence varies across consumer setups, so the
|
|
10
|
+
* module can fall through to `process.env` (undefined in the browser) and the FE
|
|
11
|
+
* debug flag silently stays false while the server logs work. A Vite `transform`
|
|
12
|
+
* runs on the module regardless of how (or whether) the define is delivered, in
|
|
13
|
+
* both dev and build and for every environment, so the discovery plugin uses this
|
|
14
|
+
* to replace the module with the resolved literal.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Scope to the router's own internal-debug module: the published package
|
|
19
|
+
* (`/@rangojs/router/`, incl. pnpm's nested layout) or the monorepo workspace
|
|
20
|
+
* (`/packages/rangojs-router/`). The package-anchored path avoids matching a
|
|
21
|
+
* consumer file that merely sits under a directory named `rangojs-router`.
|
|
22
|
+
* Accepts module ids and dev-server URLs (`/@fs/...internal-debug.ts?v=abc`).
|
|
23
|
+
*/
|
|
24
|
+
export function isRouterInternalDebugId(id: string): boolean {
|
|
25
|
+
if (!id.includes("internal-debug")) return false;
|
|
26
|
+
const norm = id.replace(/\\/g, "/");
|
|
27
|
+
return (
|
|
28
|
+
/\/internal-debug\.[cm]?[jt]sx?(\?|$)/.test(norm) &&
|
|
29
|
+
(norm.includes("/@rangojs/router/") ||
|
|
30
|
+
norm.includes("/packages/rangojs-router/"))
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Transform: replace the module with the resolved literal.
|
|
36
|
+
* Returns null for any module that is not the router's internal-debug module.
|
|
37
|
+
*/
|
|
38
|
+
export function injectClientDebugFlag(
|
|
39
|
+
id: string,
|
|
40
|
+
): { code: string; map: null } | null {
|
|
41
|
+
// Cheap early-out: this hook runs on every module in every environment.
|
|
42
|
+
if (!isRouterInternalDebugId(id)) return null;
|
|
43
|
+
// Emit the whole module: internal-debug.ts has a single export, kept in sync.
|
|
44
|
+
return {
|
|
45
|
+
code: `export const INTERNAL_RANGO_DEBUG = ${!!process.env.INTERNAL_RANGO_DEBUG};\n`,
|
|
46
|
+
map: null,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Dev middleware companion: serve the internal-debug module `no-cache` so the
|
|
52
|
+
* browser revalidates it (etag) instead of trusting an immutable cache entry.
|
|
53
|
+
*
|
|
54
|
+
* Why: the transform bakes the flag into module CONTENT, but a published
|
|
55
|
+
* consumer resolves the module into node_modules, where dev serves it as
|
|
56
|
+
* `internal-debug.ts?v=<hash>` with `Cache-Control: max-age=31536000,immutable`.
|
|
57
|
+
* That `?v=` hash does not vary with env vars (verified on Vite 8: getConfigHash
|
|
58
|
+
* hashes NODE_ENV, resolve, plugin names, optimizeDeps -- not arbitrary env
|
|
59
|
+
* state), so toggling the flag changed the content under an unchanged immutable
|
|
60
|
+
* URL: a browser that ever loaded the app with the flag off kept the
|
|
61
|
+
* baked-`false` module across dev-server restarts, and INTERNAL_RANGO_DEBUG
|
|
62
|
+
* never reached the FE logs while the server logs worked. The monorepo was
|
|
63
|
+
* immune -- workspace source is outside node_modules and served no-cache --
|
|
64
|
+
* which is why this bit only npm consumers.
|
|
65
|
+
*
|
|
66
|
+
* internal-debug.ts is the ONLY flag-varying module in the graph (its importers
|
|
67
|
+
* are byte-identical across flag states), so forcing revalidation for this one
|
|
68
|
+
* tiny module is sufficient and costs one conditional request per session. The
|
|
69
|
+
* middleware is unconditional (not gated on the flag) so an already-poisoned
|
|
70
|
+
* cache heals in both directions. Alternatives that do not work: a plugin
|
|
71
|
+
* `resolveId` appending a flag query never fires in Vite 8 dev for
|
|
72
|
+
* fs-resolvable relative imports, and the pre-#621 `define` no longer rotates
|
|
73
|
+
* the optimizer hash (define contents are not part of getConfigHash).
|
|
74
|
+
*/
|
|
75
|
+
export function internalDebugNoCacheMiddleware(): Connect.NextHandleFunction {
|
|
76
|
+
return function rangoInternalDebugNoCache(req, res, next) {
|
|
77
|
+
if (req.url && isRouterInternalDebugId(req.url)) {
|
|
78
|
+
const setHeader = res.setHeader.bind(res);
|
|
79
|
+
res.setHeader = (name, value) => {
|
|
80
|
+
return setHeader(
|
|
81
|
+
name,
|
|
82
|
+
name.toLowerCase() === "cache-control" ? "no-cache" : value,
|
|
83
|
+
);
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
next();
|
|
87
|
+
};
|
|
88
|
+
}
|