@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
|
@@ -16,8 +16,13 @@ import {
|
|
|
16
16
|
formatNestedRouterConflictError,
|
|
17
17
|
findNestedRouterConflict,
|
|
18
18
|
findRouterFiles,
|
|
19
|
+
createScanFilter,
|
|
19
20
|
} from "../build/generate-route-types.js";
|
|
20
21
|
import { firstCodeMatchIndex } from "../build/route-types/source-scan.js";
|
|
22
|
+
import {
|
|
23
|
+
injectClientDebugFlag,
|
|
24
|
+
internalDebugNoCacheMiddleware,
|
|
25
|
+
} from "./inject-client-debug.js";
|
|
21
26
|
import { createVersionPlugin } from "./plugins/version-plugin.js";
|
|
22
27
|
import { createVirtualStubPlugin } from "./plugins/virtual-stub-plugin.js";
|
|
23
28
|
import {
|
|
@@ -41,6 +46,12 @@ import {
|
|
|
41
46
|
peekSelfGenWrite,
|
|
42
47
|
} from "./discovery/self-gen-tracking.js";
|
|
43
48
|
import { discoverRouters } from "./discovery/discover-routers.js";
|
|
49
|
+
import { describeDiscoveryFailure } from "./discovery/discovery-errors.js";
|
|
50
|
+
import {
|
|
51
|
+
createDevPrerenderCache,
|
|
52
|
+
devPrerenderCacheKey,
|
|
53
|
+
payloadBodiesFromResult,
|
|
54
|
+
} from "./discovery/dev-prerender-cache.js";
|
|
44
55
|
import {
|
|
45
56
|
writeCombinedRouteTypesWithTracking,
|
|
46
57
|
writeRouteTypesFiles,
|
|
@@ -309,23 +320,23 @@ export function createRouterDiscoveryPlugin(
|
|
|
309
320
|
return {
|
|
310
321
|
name: "@rangojs/router:discovery",
|
|
311
322
|
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
// rest of the RSC entry. A previous design forced them into dedicated
|
|
320
|
-
// __prerender-handlers / __static-handlers chunks via manualChunks,
|
|
321
|
-
// but Rollup hoisted all shared dependencies into those chunks,
|
|
322
|
-
// inflating them to ~1 MB with active runtime code. Handler code is
|
|
323
|
-
// evicted in closeBundle regardless of which chunk it lands in.
|
|
324
|
-
return config;
|
|
323
|
+
// Make INTERNAL_RANGO_DEBUG reach the CLIENT debug logs by just setting the
|
|
324
|
+
// env var. See injectClientDebugFlag: bakes the resolved flag into the
|
|
325
|
+
// internal-debug module so FE debug no longer depends on Vite delivering the
|
|
326
|
+
// `__RANGO_DEBUG__` define to the client (which it does only as an injected
|
|
327
|
+
// global whose presence varies across consumer setups). Runs in dev and build.
|
|
328
|
+
transform(_code, id) {
|
|
329
|
+
return injectClientDebugFlag(id);
|
|
325
330
|
},
|
|
326
331
|
|
|
327
332
|
configResolved(config) {
|
|
328
333
|
s.projectRoot = config.root;
|
|
334
|
+
// Compile the optional discovery scan filter (glob include/exclude) now
|
|
335
|
+
// that the project root is known. findRouterFiles() below — and the
|
|
336
|
+
// build/HMR rediscovery paths — honor s.scanFilter.
|
|
337
|
+
s.scanFilter = opts?.discovery
|
|
338
|
+
? createScanFilter(s.projectRoot, opts.discovery)
|
|
339
|
+
: undefined;
|
|
329
340
|
s.isBuildMode = config.command === "build";
|
|
330
341
|
viteCommand = config.command as "serve" | "build";
|
|
331
342
|
viteMode = config.mode;
|
|
@@ -384,6 +395,11 @@ export function createRouterDiscoveryPlugin(
|
|
|
384
395
|
if ((globalThis as any).__rscRouterDiscoveryActive) return;
|
|
385
396
|
s.devServer = server;
|
|
386
397
|
|
|
398
|
+
// Serve the internal-debug module no-cache: consumers resolve it into
|
|
399
|
+
// node_modules, where dev's immutable `?v=` caching pinned browsers to a
|
|
400
|
+
// stale baked INTERNAL_RANGO_DEBUG. See internalDebugNoCacheMiddleware.
|
|
401
|
+
server.middlewares.use(internalDebugNoCacheMiddleware());
|
|
402
|
+
|
|
387
403
|
// Discovery promise that the handler can await if requests arrive
|
|
388
404
|
// before discovery completes
|
|
389
405
|
let resolveDiscovery: () => void;
|
|
@@ -424,7 +440,8 @@ export function createRouterDiscoveryPlugin(
|
|
|
424
440
|
releaseBuildEnv(s).catch(() => {});
|
|
425
441
|
});
|
|
426
442
|
|
|
427
|
-
// Mirror the build-path contract (
|
|
443
|
+
// Mirror the build-path contract (the buildStart hook below, which sets
|
|
444
|
+
// __rscRouterDiscoveryActive before running user modules):
|
|
428
445
|
// set __rscRouterDiscoveryActive before running user modules so any
|
|
429
446
|
// module-level router.reverse() calls return a placeholder instead
|
|
430
447
|
// of throwing. The temp Vite server's module runner has its own
|
|
@@ -541,6 +558,14 @@ export function createRouterDiscoveryPlugin(
|
|
|
541
558
|
);
|
|
542
559
|
console.warn(`[rango] Failed to create temp runner: ${err.message}`);
|
|
543
560
|
}
|
|
561
|
+
// Reached only on failure (runner unavailable, or create/import threw
|
|
562
|
+
// AFTER the server was created). Close the just-created server so a
|
|
563
|
+
// failed discovery does not leak it until the next call or dev shutdown,
|
|
564
|
+
// and null the refs so the reuse path above starts clean. Mirrors the
|
|
565
|
+
// close pattern used when an existing server is discarded (above).
|
|
566
|
+
await prerenderTempServer?.close().catch(() => {});
|
|
567
|
+
prerenderTempServer = null;
|
|
568
|
+
prerenderNodeRegistry = null;
|
|
544
569
|
return null;
|
|
545
570
|
}
|
|
546
571
|
|
|
@@ -642,6 +667,30 @@ export function createRouterDiscoveryPlugin(
|
|
|
642
667
|
return tempRscEnv;
|
|
643
668
|
}
|
|
644
669
|
|
|
670
|
+
// Surface a discovery failure on either dev path (Node RSC runner or the
|
|
671
|
+
// Cloudflare temp Node server). `hashBefore`/`hashAfter` are the discovering
|
|
672
|
+
// environment's dep-optimizer browserHash snapshots: a change across the
|
|
673
|
+
// attempt means a reload-causing re-optimization landed mid-flight, so an
|
|
674
|
+
// empty registry was the transient race (downgraded to a warning) rather
|
|
675
|
+
// than a genuine misconfig (loud, actionable error). Shared so both catch
|
|
676
|
+
// sites frame the same failure identically.
|
|
677
|
+
const emitDiscoveryFailure = (
|
|
678
|
+
err: unknown,
|
|
679
|
+
hashBefore: string | undefined,
|
|
680
|
+
hashAfter: string | undefined,
|
|
681
|
+
): void => {
|
|
682
|
+
const reoptimizeObserved =
|
|
683
|
+
hashBefore !== undefined &&
|
|
684
|
+
hashAfter !== undefined &&
|
|
685
|
+
hashBefore !== hashAfter;
|
|
686
|
+
const report = describeDiscoveryFailure(err, { reoptimizeObserved });
|
|
687
|
+
if (report.level === "warn") {
|
|
688
|
+
console.warn(report.message);
|
|
689
|
+
} else {
|
|
690
|
+
console.error(report.message);
|
|
691
|
+
}
|
|
692
|
+
};
|
|
693
|
+
|
|
645
694
|
const discover = async () => {
|
|
646
695
|
const discoverStart = performance.now();
|
|
647
696
|
const rscEnv = (server.environments as any)?.rsc;
|
|
@@ -657,18 +706,26 @@ export function createRouterDiscoveryPlugin(
|
|
|
657
706
|
|
|
658
707
|
// Create a temp Node.js server to run runtime discovery and generate
|
|
659
708
|
// named route types (static parser can't resolve factory calls).
|
|
709
|
+
// The temp server is a separate Vite instance with its own dep
|
|
710
|
+
// optimizer; snapshot ITS browserHash (hoisted so the catch can tell a
|
|
711
|
+
// transient re-optimization apart from a genuine empty registry, the
|
|
712
|
+
// same way the Node path below does).
|
|
713
|
+
let tempRscEnv: any;
|
|
714
|
+
let optimizerHashBefore: string | undefined;
|
|
660
715
|
try {
|
|
661
716
|
// Acquire build-time env bindings for dev prerender
|
|
662
717
|
await timed(debugDiscovery, "acquireBuildEnv", () =>
|
|
663
718
|
acquireBuildEnv(s, viteCommand, viteMode),
|
|
664
719
|
);
|
|
665
720
|
|
|
666
|
-
|
|
721
|
+
tempRscEnv = await timed(
|
|
667
722
|
debugDiscovery,
|
|
668
723
|
"getOrCreateTempServer",
|
|
669
724
|
() => getOrCreateTempServer(),
|
|
670
725
|
);
|
|
671
726
|
if (tempRscEnv) {
|
|
727
|
+
optimizerHashBefore =
|
|
728
|
+
tempRscEnv.depsOptimizer?.metadata?.browserHash;
|
|
672
729
|
await timed(debugDiscovery, "discoverRouters (cloudflare)", () =>
|
|
673
730
|
discoverRouters(s, tempRscEnv),
|
|
674
731
|
);
|
|
@@ -677,8 +734,10 @@ export function createRouterDiscoveryPlugin(
|
|
|
677
734
|
);
|
|
678
735
|
}
|
|
679
736
|
} catch (err: any) {
|
|
680
|
-
|
|
681
|
-
|
|
737
|
+
emitDiscoveryFailure(
|
|
738
|
+
err,
|
|
739
|
+
optimizerHashBefore,
|
|
740
|
+
tempRscEnv?.depsOptimizer?.metadata?.browserHash,
|
|
682
741
|
);
|
|
683
742
|
}
|
|
684
743
|
|
|
@@ -690,6 +749,13 @@ export function createRouterDiscoveryPlugin(
|
|
|
690
749
|
return;
|
|
691
750
|
}
|
|
692
751
|
|
|
752
|
+
// Snapshot the dep-optimizer hash before discovery so the catch can tell
|
|
753
|
+
// a transient re-optimization race apart from a genuine empty registry.
|
|
754
|
+
// A reload-causing re-optimization regenerates browserHash; if it changed
|
|
755
|
+
// across the attempt, an empty read was almost certainly the race below.
|
|
756
|
+
const optimizerHashBefore: string | undefined =
|
|
757
|
+
rscEnv.depsOptimizer?.metadata?.browserHash;
|
|
758
|
+
|
|
693
759
|
try {
|
|
694
760
|
// Acquire build-time env bindings for dev prerender (Node.js path)
|
|
695
761
|
debugDiscovery?.("dev: node path start");
|
|
@@ -697,21 +763,31 @@ export function createRouterDiscoveryPlugin(
|
|
|
697
763
|
acquireBuildEnv(s, viteCommand, viteMode),
|
|
698
764
|
);
|
|
699
765
|
|
|
700
|
-
//
|
|
701
|
-
//
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
766
|
+
// Discover routers FIRST, then arm the manifest-readiness gate on the
|
|
767
|
+
// server module discovery actually read the registry from.
|
|
768
|
+
//
|
|
769
|
+
// We deliberately do NOT pre-import "@rangojs/router/server" before the
|
|
770
|
+
// entry to arm the gate early. During a Vite dependency re-optimization
|
|
771
|
+
// (dev boot after a lockfile change, or `vite dev --force`), a module
|
|
772
|
+
// imported here before the entry resolves to the pre-optimize copy of
|
|
773
|
+
// the runner's module graph, while discoverRouters' entry import — which
|
|
774
|
+
// awaits the in-flight re-optimization — resolves to the post-optimize
|
|
775
|
+
// copy. createRouter() then populates RouterRegistry on the fresh copy,
|
|
776
|
+
// but a stale pre-imported "@rangojs/router/server" reads the other
|
|
777
|
+
// copy's empty Map and discovery throws a spurious "No routers found"
|
|
778
|
+
// even though the app is configured correctly. discoverRouters imports
|
|
779
|
+
// the entry first and reads the registry off the same instance, keeping
|
|
780
|
+
// read and write on one copy. The virtual manifest module's own gate
|
|
781
|
+
// (s.discoveryDone, armed by beginDiscoveryGate) already blocks early
|
|
782
|
+
// requests during discovery on the Node path, so arming
|
|
783
|
+
// manifestReadyPromise after discovery is sufficient here.
|
|
784
|
+
const serverMod = await timed(debugDiscovery, "discoverRouters", () =>
|
|
785
|
+
discoverRouters(s, rscEnv),
|
|
706
786
|
);
|
|
707
787
|
if (serverMod?.setManifestReadyPromise) {
|
|
708
788
|
serverMod.setManifestReadyPromise(discoveryPromise);
|
|
709
789
|
}
|
|
710
790
|
|
|
711
|
-
await timed(debugDiscovery, "discoverRouters", () =>
|
|
712
|
-
discoverRouters(s, rscEnv),
|
|
713
|
-
);
|
|
714
|
-
|
|
715
791
|
// Store server origin for dev prerender endpoint (virtual module injection)
|
|
716
792
|
s.devServerOrigin = getDevServerOrigin();
|
|
717
793
|
|
|
@@ -729,8 +805,10 @@ export function createRouterDiscoveryPlugin(
|
|
|
729
805
|
propagateDiscoveryState(rscEnv),
|
|
730
806
|
);
|
|
731
807
|
} catch (err: any) {
|
|
732
|
-
|
|
733
|
-
|
|
808
|
+
emitDiscoveryFailure(
|
|
809
|
+
err,
|
|
810
|
+
optimizerHashBefore,
|
|
811
|
+
rscEnv.depsOptimizer?.metadata?.browserHash,
|
|
734
812
|
);
|
|
735
813
|
} finally {
|
|
736
814
|
debugDiscovery?.(
|
|
@@ -763,6 +841,15 @@ export function createRouterDiscoveryPlugin(
|
|
|
763
841
|
// Registry from the main server's RSC environment (populated by discoverRouters)
|
|
764
842
|
let mainRegistry: Map<string, any> | null = null;
|
|
765
843
|
|
|
844
|
+
// Memoized /__rsc_prerender render results, keyed by router-instance
|
|
845
|
+
// identity (#654). The per-request entry re-import below is what makes
|
|
846
|
+
// identity a valid freshness key: an HMR-invalidated chain re-runs
|
|
847
|
+
// createRouter() and replaces the registry instance, so cached bodies
|
|
848
|
+
// for the old instance become unreachable; an untouched chain returns
|
|
849
|
+
// the same instance and the cached body is byte-identical to a fresh
|
|
850
|
+
// render. See dev-prerender-cache.ts for the full invariant.
|
|
851
|
+
const devPrerenderCache = createDevPrerenderCache();
|
|
852
|
+
|
|
766
853
|
// Push discovery state (manifest, trie, precomputed entries) to the
|
|
767
854
|
// server module so runtime request handling uses the current routes.
|
|
768
855
|
// Shared by initial discovery and HMR-triggered re-discovery.
|
|
@@ -853,9 +940,28 @@ export function createRouterDiscoveryPlugin(
|
|
|
853
940
|
|
|
854
941
|
if (!registry) {
|
|
855
942
|
// No main registry: the RSC env has no module runner (Cloudflare dev).
|
|
856
|
-
// Lazily create a Node.js temp server for prerender evaluation
|
|
857
|
-
|
|
858
|
-
|
|
943
|
+
// Lazily create a Node.js temp server for prerender evaluation, and
|
|
944
|
+
// re-import the entry through it on EVERY request — the temp server
|
|
945
|
+
// has its own file watcher, so a handler-only edit (a file without
|
|
946
|
+
// urls()/createRouter() that the main watcher's route-file sniff
|
|
947
|
+
// ignores) invalidates its module graph; the re-import re-evaluates
|
|
948
|
+
// exactly the dirty subgraph and re-registers fresh router
|
|
949
|
+
// instances. Before #654 the cached registry was only refreshed on
|
|
950
|
+
// route-file edits, so handler-only edits served stale prerender
|
|
951
|
+
// content on this path. Warm-cache re-imports are module-cache hits.
|
|
952
|
+
const tempRscEnv = await getOrCreateTempServer();
|
|
953
|
+
if (tempRscEnv) {
|
|
954
|
+
try {
|
|
955
|
+
await importEntryAndRegistry(tempRscEnv);
|
|
956
|
+
} catch (err: any) {
|
|
957
|
+
console.warn(
|
|
958
|
+
`[rango] Dev prerender module refresh failed: ${err.message}`,
|
|
959
|
+
);
|
|
960
|
+
res.statusCode = 500;
|
|
961
|
+
res.end(`Prerender handler error: ${err.message}`);
|
|
962
|
+
logResult(500, "temp module refresh failed");
|
|
963
|
+
return;
|
|
964
|
+
}
|
|
859
965
|
}
|
|
860
966
|
registry = prerenderNodeRegistry;
|
|
861
967
|
}
|
|
@@ -871,8 +977,38 @@ export function createRouterDiscoveryPlugin(
|
|
|
871
977
|
const wantRouteName = url.searchParams.get("routeName");
|
|
872
978
|
const wantPassthrough = url.searchParams.get("passthrough") === "1";
|
|
873
979
|
|
|
980
|
+
// One render warms BOTH variant keys (matchForPrerender computes the
|
|
981
|
+
// intercept segments unconditionally), so a route's main and modal
|
|
982
|
+
// variants cost a single render per HMR generation.
|
|
983
|
+
const variantDims = {
|
|
984
|
+
passthrough: wantPassthrough,
|
|
985
|
+
routeName: wantRouteName,
|
|
986
|
+
};
|
|
987
|
+
const keyMain = devPrerenderCacheKey(pathname, {
|
|
988
|
+
intercept: false,
|
|
989
|
+
...variantDims,
|
|
990
|
+
});
|
|
991
|
+
const keyIntercept = devPrerenderCacheKey(pathname, {
|
|
992
|
+
intercept: true,
|
|
993
|
+
...variantDims,
|
|
994
|
+
});
|
|
995
|
+
const requestedKey = wantIntercept ? keyIntercept : keyMain;
|
|
996
|
+
|
|
874
997
|
for (const [, routerInstance] of registry) {
|
|
875
998
|
if (!routerInstance.matchForPrerender) continue;
|
|
999
|
+
// Cache is consulted per router IN LOOP ORDER so multi-router
|
|
1000
|
+
// fall-through semantics are identical to the uncached path: a
|
|
1001
|
+
// router that never produced a payload for this key still runs
|
|
1002
|
+
// its matchForPrerender (cheap trie miss / intentionally-uncached
|
|
1003
|
+
// error retry) before the next router is considered.
|
|
1004
|
+
const cached = devPrerenderCache.get(routerInstance, requestedKey);
|
|
1005
|
+
if (cached !== undefined) {
|
|
1006
|
+
res.setHeader("content-type", "application/json");
|
|
1007
|
+
res.setHeader("x-rango-prerender-cache", "HIT");
|
|
1008
|
+
res.end(cached);
|
|
1009
|
+
logResult(200, "cache hit");
|
|
1010
|
+
return;
|
|
1011
|
+
}
|
|
876
1012
|
try {
|
|
877
1013
|
const result = await routerInstance.matchForPrerender(
|
|
878
1014
|
pathname,
|
|
@@ -888,26 +1024,31 @@ export function createRouterDiscoveryPlugin(
|
|
|
888
1024
|
// This prevents returning the wrong entry when multiple routers
|
|
889
1025
|
// have prerenderable routes sharing the same pathname.
|
|
890
1026
|
if (wantRouteName && result.routeName !== wantRouteName) continue;
|
|
1027
|
+
// Pre-encoded MERGED handle string in the intercept body comes
|
|
1028
|
+
// from the producer (handles are Flight-encoded so
|
|
1029
|
+
// Promise/ReactNode values survive the wire).
|
|
1030
|
+
const bodies = payloadBodiesFromResult(result);
|
|
1031
|
+
devPrerenderCache.set(routerInstance, keyMain, bodies.main);
|
|
1032
|
+
devPrerenderCache.set(
|
|
1033
|
+
routerInstance,
|
|
1034
|
+
keyIntercept,
|
|
1035
|
+
bodies.intercept,
|
|
1036
|
+
);
|
|
891
1037
|
res.setHeader("content-type", "application/json");
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
payload = {
|
|
895
|
-
segments: [...result.segments, ...result.interceptSegments],
|
|
896
|
-
handles: {
|
|
897
|
-
...result.handles,
|
|
898
|
-
...(result.interceptHandles || {}),
|
|
899
|
-
},
|
|
900
|
-
};
|
|
901
|
-
} else {
|
|
902
|
-
payload = { segments: result.segments, handles: result.handles };
|
|
903
|
-
}
|
|
904
|
-
res.end(JSON.stringify(payload));
|
|
1038
|
+
res.setHeader("x-rango-prerender-cache", "MISS");
|
|
1039
|
+
res.end(wantIntercept ? bodies.intercept : bodies.main);
|
|
905
1040
|
logResult(200, `match ${result.routeName}`);
|
|
906
1041
|
return;
|
|
907
1042
|
} catch (err: any) {
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
)
|
|
1043
|
+
// matchForPrerender now re-throws render failures instead of baking
|
|
1044
|
+
// an error page (issue #587). In dev there is no frozen artifact, so
|
|
1045
|
+
// we fall through (404 -> live handler). A `throw new Skip()` is the
|
|
1046
|
+
// expected "skip this URL" signal, not a failure, so it stays quiet.
|
|
1047
|
+
if (err?.name !== "Skip") {
|
|
1048
|
+
console.warn(
|
|
1049
|
+
`[rango] Dev prerender error for ${pathname} (serving live instead): ${err.message}`,
|
|
1050
|
+
);
|
|
1051
|
+
}
|
|
911
1052
|
}
|
|
912
1053
|
}
|
|
913
1054
|
|
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
import type MagicString from "magic-string";
|
|
2
2
|
import { hashInlineId, buildExportMap } from "../plugins/expose-id-utils.js";
|
|
3
3
|
|
|
4
|
-
// ---------------------------------------------------------------------------
|
|
5
|
-
// Types
|
|
6
|
-
// ---------------------------------------------------------------------------
|
|
7
|
-
|
|
8
4
|
/** Minimal ESTree Program node — avoids importing from `rollup` (not a direct dep). */
|
|
9
5
|
interface ProgramNode {
|
|
10
6
|
type: "Program";
|
|
@@ -70,10 +66,6 @@ function findImportInsertionPos(
|
|
|
70
66
|
return insertionPos;
|
|
71
67
|
}
|
|
72
68
|
|
|
73
|
-
// ---------------------------------------------------------------------------
|
|
74
|
-
// AST walking helper
|
|
75
|
-
// ---------------------------------------------------------------------------
|
|
76
|
-
|
|
77
69
|
/**
|
|
78
70
|
* Recursively walk an ESTree AST node, calling `enter` on each node.
|
|
79
71
|
* Parent is passed for context.
|
|
@@ -111,10 +103,6 @@ function walkNode(
|
|
|
111
103
|
ancestors.pop();
|
|
112
104
|
}
|
|
113
105
|
|
|
114
|
-
// ---------------------------------------------------------------------------
|
|
115
|
-
// AST analysis
|
|
116
|
-
// ---------------------------------------------------------------------------
|
|
117
|
-
|
|
118
106
|
/**
|
|
119
107
|
* Parse the file with Vite's parseAst and find all calls to `fnName`.
|
|
120
108
|
* Distinguishes between `export const X = fnName(...)` (exportInfo set)
|
|
@@ -294,16 +282,23 @@ function isInertExpression(node: any): boolean {
|
|
|
294
282
|
(e: any) => e === null || isInertExpression(e),
|
|
295
283
|
);
|
|
296
284
|
case "ObjectExpression":
|
|
297
|
-
return (node.properties ?? []).every(
|
|
298
|
-
(
|
|
285
|
+
return (node.properties ?? []).every((p: any) => {
|
|
286
|
+
// A spread inside an object (`{ ...x }`) is inert iff `x` is inert.
|
|
287
|
+
if (p.type === "SpreadElement" || p.type === "RestElement") {
|
|
288
|
+
return isInertExpression(p.argument);
|
|
289
|
+
}
|
|
290
|
+
return (
|
|
299
291
|
p.type === "Property" &&
|
|
300
292
|
(!p.computed || isInertExpression(p.key)) &&
|
|
301
|
-
isInertExpression(p.value)
|
|
302
|
-
|
|
293
|
+
isInertExpression(p.value)
|
|
294
|
+
);
|
|
295
|
+
});
|
|
303
296
|
case "UnaryExpression":
|
|
304
297
|
return isInertExpression(node.argument);
|
|
305
298
|
case "BinaryExpression":
|
|
306
299
|
return isInertExpression(node.left) && isInertExpression(node.right);
|
|
300
|
+
case "LogicalExpression":
|
|
301
|
+
return isInertExpression(node.left) && isInertExpression(node.right);
|
|
307
302
|
case "ConditionalExpression":
|
|
308
303
|
return (
|
|
309
304
|
isInertExpression(node.test) &&
|
|
@@ -426,10 +421,6 @@ export function extractModuleLevelDeclarations(
|
|
|
426
421
|
return declarations;
|
|
427
422
|
}
|
|
428
423
|
|
|
429
|
-
// ---------------------------------------------------------------------------
|
|
430
|
-
// Transform
|
|
431
|
-
// ---------------------------------------------------------------------------
|
|
432
|
-
|
|
433
424
|
/**
|
|
434
425
|
* Transform inline handler calls by extracting them into virtual modules.
|
|
435
426
|
* Only processes inline calls (exportInfo === null); export const calls are
|
|
@@ -5,9 +5,7 @@ import {
|
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Find matching close paren in bundled code using depth counting.
|
|
8
|
-
* Uses skipStringOrComment
|
|
9
|
-
* template literal ${...} expressions, comments, and nested strings.
|
|
10
|
-
* Returns the position after the closing paren, or -1 if unmatched.
|
|
8
|
+
* Uses skipStringOrComment to correctly handle template literals, comments, and nested strings.
|
|
11
9
|
* @internal Exported for testing only.
|
|
12
10
|
*/
|
|
13
11
|
export function findMatchingParenInBundle(
|
|
@@ -30,9 +28,8 @@ export function findMatchingParenInBundle(
|
|
|
30
28
|
}
|
|
31
29
|
|
|
32
30
|
/**
|
|
33
|
-
* Scan a bundled chunk for handler exports
|
|
34
|
-
*
|
|
35
|
-
* @internal Exported for testing only.
|
|
31
|
+
* Scan a bundled chunk for handler exports and extract their names + $$id values.
|
|
32
|
+
* Optionally detects passthrough flag. @internal Exported for testing only.
|
|
36
33
|
*/
|
|
37
34
|
export function extractHandlerExportsFromChunk(
|
|
38
35
|
chunkCode: string,
|
|
@@ -67,7 +64,7 @@ export function extractHandlerExportsFromChunk(
|
|
|
67
64
|
const closePos = findMatchingParenInBundle(chunkCode, afterOpen);
|
|
68
65
|
if (closePos !== -1) {
|
|
69
66
|
const callBody = chunkCode.slice(callStart.index, closePos);
|
|
70
|
-
isPassthrough = /passthrough\s*:\s*(!0|true)/.test(callBody);
|
|
67
|
+
isPassthrough = /passthrough\s*:\s*(!0|true)/.test(callBody); // !0 is minified true
|
|
71
68
|
}
|
|
72
69
|
}
|
|
73
70
|
}
|
|
@@ -79,9 +76,8 @@ export function extractHandlerExportsFromChunk(
|
|
|
79
76
|
}
|
|
80
77
|
|
|
81
78
|
/**
|
|
82
|
-
* Evict handler code from a bundled chunk, replacing full
|
|
83
|
-
*
|
|
84
|
-
* and bytes saved, or null if no changes were made.
|
|
79
|
+
* Evict handler code from a bundled chunk, replacing full call expressions with stubs.
|
|
80
|
+
* Returns the modified code and bytes saved, or null if no changes were made.
|
|
85
81
|
* @internal Exported for testing only.
|
|
86
82
|
*/
|
|
87
83
|
export function evictHandlerCode(
|
|
@@ -110,13 +106,11 @@ export function evictHandlerCode(
|
|
|
110
106
|
const closePos = findMatchingParenInBundle(modified, afterOpen);
|
|
111
107
|
if (closePos === -1) continue;
|
|
112
108
|
|
|
113
|
-
// Skip trailing whitespace and optional semicolon
|
|
114
109
|
let rangeEnd = closePos;
|
|
115
110
|
while (rangeEnd < modified.length && /\s/.test(modified[rangeEnd]))
|
|
116
111
|
rangeEnd++;
|
|
117
112
|
if (modified[rangeEnd] === ";") rangeEnd++;
|
|
118
113
|
|
|
119
|
-
// Validate: matched range must contain the expected handlerId
|
|
120
114
|
const matched = modified.slice(startMatch.index, rangeEnd);
|
|
121
115
|
if (!matched.includes(handlerId)) continue;
|
|
122
116
|
|
|
@@ -124,7 +118,6 @@ export function evictHandlerCode(
|
|
|
124
118
|
modified =
|
|
125
119
|
modified.slice(0, startMatch.index) + stub + modified.slice(rangeEnd);
|
|
126
120
|
|
|
127
|
-
// Remove the now-redundant $$id assignment line.
|
|
128
121
|
modified = modified.replace(
|
|
129
122
|
new RegExp(`\\n${eName}\\.\\$\\$id\\s*=\\s*"[^"]+";`),
|
|
130
123
|
"",
|
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
// Resolution of the public `clientChunks` option into the callback shape that
|
|
2
|
-
// @vitejs/plugin-rsc expects. See plugin-types.ts (ClientChunks) and
|
|
3
|
-
// docs/client-chunking.md for the contract. The mechanism: a distinct returned
|
|
4
|
-
// name yields a distinct, dynamically-imported client chunk, independent of how
|
|
5
|
-
// the RSC/server build chunked the importing modules.
|
|
6
|
-
|
|
7
1
|
import type { ClientChunkMeta, ClientChunks } from "../plugin-types.js";
|
|
8
2
|
import { createRangoDebugger, NS } from "../debug.js";
|
|
9
3
|
import { hashRefKey } from "../plugins/client-ref-hashing.js";
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { parseAst } from "vite";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Detect a leading `"use client"` (or `'use client'`) directive in a module's
|
|
5
|
+
* directive prologue, tolerating leading comments/whitespace before it.
|
|
6
|
+
*
|
|
7
|
+
* A bare `source.trimStart().startsWith` check only strips whitespace, so a
|
|
8
|
+
* license banner / `// @ts-nocheck` before the directive would be missed. This
|
|
9
|
+
* walks the leading ExpressionStatement string-literal directives via the AST
|
|
10
|
+
* (a leading comment is not a `program.body` node), the single source of truth
|
|
11
|
+
* shared by both the rango plugin's HMR client-module sniff and the version
|
|
12
|
+
* plugin's getClientModuleSignature — so the two can never drift on what counts
|
|
13
|
+
* as a client module.
|
|
14
|
+
*
|
|
15
|
+
* Returns false when the source cannot be parsed (a syntactically broken file is
|
|
16
|
+
* not treated as a client module).
|
|
17
|
+
*/
|
|
18
|
+
export function hasUseClientDirective(source: string): boolean {
|
|
19
|
+
let program: { body?: any[] };
|
|
20
|
+
try {
|
|
21
|
+
program = parseAst(source, { lang: "tsx" }) as { body?: any[] };
|
|
22
|
+
} catch {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
for (const node of program.body ?? []) {
|
|
26
|
+
if (
|
|
27
|
+
node?.type === "ExpressionStatement" &&
|
|
28
|
+
node.expression?.type === "Literal" &&
|
|
29
|
+
typeof node.expression.value === "string"
|
|
30
|
+
) {
|
|
31
|
+
if (node.expression.value === "use client") return true;
|
|
32
|
+
// Another leading string-literal directive (e.g. "use strict"): keep
|
|
33
|
+
// scanning the prologue.
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
// First non-directive statement ends the prologue.
|
|
37
|
+
break;
|
|
38
|
+
}
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
@@ -1,25 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Discovery Runner Config Parity
|
|
3
|
-
*
|
|
4
|
-
* The discovery temp server (createTempRscServer) runs the user's handler
|
|
5
|
-
* graph through a throwaway Node Vite server built with `configFile: false`.
|
|
6
|
-
* Without help, that server only sees a fixed Rango-owned plugin set, so any
|
|
7
|
-
* user resolution is absent during discovery, prerender, and static handler
|
|
8
|
-
* rendering — even though it applies at request time. Two flavors of user
|
|
9
|
-
* resolution must be carried across:
|
|
10
|
-
*
|
|
11
|
-
* - Third-party resolveId plugins (e.g. vite-tsconfig-paths) — forwarded as
|
|
12
|
-
* plugin instances, see selectForwardableResolvePlugins.
|
|
13
|
-
* - Native config-driven resolution, including Vite 8's built-in
|
|
14
|
-
* `resolve.tsconfigPaths` (which supersedes vite-tsconfig-paths) — forwarded
|
|
15
|
-
* as the data slice, see pickForwardedRunnerConfig.
|
|
16
|
-
*
|
|
17
|
-
* These helpers extract the resolution-relevant slice of the user's resolved
|
|
18
|
-
* config (resolve.*, define, oxc) and forward the user's resolution plugins
|
|
19
|
-
* into the temp server so discovery resolves modules the same way the real
|
|
20
|
-
* environment does.
|
|
21
|
-
*/
|
|
22
|
-
|
|
23
1
|
import type { Plugin, ResolvedConfig, UserConfig } from "vite";
|
|
24
2
|
|
|
25
3
|
/**
|
|
@@ -1,78 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
* Non-leaf nodes are skipped because they have nested lazy includes that
|
|
6
|
-
* require the handler to run for discovery.
|
|
7
|
-
*
|
|
8
|
-
* A leaf is also skipped when its staticPrefix collides with an ancestor
|
|
9
|
-
* include node's staticPrefix. That happens when a dynamic param collapses the
|
|
10
|
-
* staticPrefix of nested includes onto the parent's (e.g. `/m/:id/edit` -> sp
|
|
11
|
-
* `/m`): precomputing such a leaf under the collapsed prefix would let the
|
|
12
|
-
* ancestor's lazy entry claim a route it cannot register (the route is behind
|
|
13
|
-
* further nested lazy includes), producing a RouteNotFoundError at request time
|
|
14
|
-
* (issue #506). Those routes are resolved via the handler chain instead.
|
|
15
|
-
*/
|
|
16
|
-
export function flattenLeafEntries(
|
|
17
|
-
prefixTree: Record<string, any>,
|
|
18
|
-
routeManifest: Record<string, string>,
|
|
19
|
-
result: Array<{ staticPrefix: string; routes: Record<string, string> }>,
|
|
20
|
-
): void {
|
|
21
|
-
function visit(node: any, ancestorStaticPrefixes: Set<string>): void {
|
|
22
|
-
const children = node.children || {};
|
|
23
|
-
if (
|
|
24
|
-
Object.keys(children).length === 0 &&
|
|
25
|
-
node.routes &&
|
|
26
|
-
node.routes.length > 0
|
|
27
|
-
) {
|
|
28
|
-
// Leaf node. Skip if its staticPrefix collides with an ancestor include
|
|
29
|
-
// node's staticPrefix (dynamic-param collapse) — see doc comment above.
|
|
30
|
-
if (ancestorStaticPrefixes.has(node.staticPrefix)) {
|
|
31
|
-
return;
|
|
32
|
-
}
|
|
33
|
-
// Collect its routes from the manifest
|
|
34
|
-
const routes: Record<string, string> = {};
|
|
35
|
-
for (const name of node.routes) {
|
|
36
|
-
if (name in routeManifest) {
|
|
37
|
-
routes[name] = routeManifest[name];
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
result.push({ staticPrefix: node.staticPrefix, routes });
|
|
41
|
-
} else {
|
|
42
|
-
// Non-leaf: recurse into children, tracking this node's staticPrefix as
|
|
43
|
-
// an ancestor so a collapsed nested leaf below it is not over-claimed.
|
|
44
|
-
const nextAncestors = new Set(ancestorStaticPrefixes);
|
|
45
|
-
nextAncestors.add(node.staticPrefix);
|
|
46
|
-
for (const child of Object.values(children)) {
|
|
47
|
-
visit(child, nextAncestors);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
for (const node of Object.values(prefixTree)) {
|
|
52
|
-
visit(node, new Set());
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Walk prefix tree to map each route name to its scope's staticPrefix.
|
|
58
|
-
*/
|
|
59
|
-
export function buildRouteToStaticPrefix(
|
|
60
|
-
prefixTree: Record<string, any>,
|
|
61
|
-
result: Record<string, string>,
|
|
62
|
-
): void {
|
|
63
|
-
function visit(node: any): void {
|
|
64
|
-
const sp = node.staticPrefix || "";
|
|
65
|
-
for (const name of node.routes || []) {
|
|
66
|
-
result[name] = sp;
|
|
67
|
-
}
|
|
68
|
-
for (const child of Object.values(node.children || {})) {
|
|
69
|
-
visit(child);
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
for (const node of Object.values(prefixTree)) {
|
|
73
|
-
visit(node);
|
|
74
|
-
}
|
|
75
|
-
}
|
|
1
|
+
export {
|
|
2
|
+
flattenLeafEntries,
|
|
3
|
+
buildRouteToStaticPrefix,
|
|
4
|
+
} from "../../build/prefix-tree-utils.js";
|
|
76
5
|
|
|
77
6
|
/**
|
|
78
7
|
* Wrap a value as `JSON.parse('...')` instead of a JS object literal.
|