@rangojs/router 0.0.0-experimental.9c9afef3 → 0.0.0-experimental.a014d2b7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +8 -0
- package/README.md +245 -49
- package/dist/bin/rango.js +440 -133
- package/dist/testing/vitest.js +82 -0
- package/dist/vite/index.js +3373 -1176
- package/dist/vite/index.js.bak +5448 -0
- package/dist/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
- package/package.json +68 -14
- package/skills/api-client/SKILL.md +211 -0
- package/skills/breadcrumbs/SKILL.md +64 -2
- package/skills/bundle-analysis/SKILL.md +159 -0
- package/skills/cache-guide/SKILL.md +224 -32
- package/skills/caching/SKILL.md +279 -17
- package/skills/composability/SKILL.md +27 -3
- package/skills/css/SKILL.md +76 -0
- package/skills/debug-manifest/SKILL.md +4 -2
- package/skills/document-cache/SKILL.md +78 -55
- package/skills/handler-use/SKILL.md +364 -0
- package/skills/hooks/SKILL.md +250 -30
- package/skills/host-router/SKILL.md +83 -23
- package/skills/i18n/SKILL.md +276 -0
- package/skills/intercept/SKILL.md +87 -18
- package/skills/layout/SKILL.md +35 -9
- package/skills/links/SKILL.md +249 -17
- package/skills/loader/SKILL.md +235 -9
- package/skills/middleware/SKILL.md +52 -13
- package/skills/migrate-nextjs/SKILL.md +584 -0
- package/skills/migrate-react-router/SKILL.md +771 -0
- package/skills/mime-routes/SKILL.md +28 -1
- package/skills/observability/SKILL.md +172 -0
- package/skills/parallel/SKILL.md +77 -7
- package/skills/prerender/SKILL.md +172 -125
- package/skills/rango/SKILL.md +251 -22
- package/skills/react-compiler/SKILL.md +168 -0
- package/skills/response-routes/SKILL.md +123 -48
- package/skills/route/SKILL.md +70 -5
- package/skills/router-setup/SKILL.md +65 -8
- package/skills/scripts/SKILL.md +179 -0
- package/skills/server-actions/SKILL.md +775 -0
- package/skills/streams-and-websockets/SKILL.md +283 -0
- package/skills/tailwind/SKILL.md +27 -3
- package/skills/testing/SKILL.md +130 -0
- package/skills/testing/bindings.md +103 -0
- package/skills/testing/cache-prerender.md +127 -0
- package/skills/testing/client-components.md +124 -0
- package/skills/testing/e2e-parity.md +125 -0
- package/skills/testing/flight.md +91 -0
- package/skills/testing/handles.md +129 -0
- package/skills/testing/loader.md +128 -0
- package/skills/testing/middleware.md +99 -0
- package/skills/testing/render-handler.md +122 -0
- package/skills/testing/response-routes.md +95 -0
- package/skills/testing/reverse-and-types.md +84 -0
- package/skills/testing/server-actions.md +107 -0
- package/skills/testing/server-tree.md +128 -0
- package/skills/testing/setup.md +123 -0
- package/skills/typesafety/SKILL.md +322 -29
- package/skills/use-cache/SKILL.md +57 -14
- package/skills/view-transitions/SKILL.md +337 -0
- package/src/__augment-tests__/augment.ts +81 -0
- package/src/__augment-tests__/augmented.check.ts +116 -0
- package/src/__internal.ts +1 -66
- package/src/browser/action-coordinator.ts +53 -36
- package/src/browser/action-fence.ts +47 -0
- package/src/browser/app-shell.ts +39 -0
- package/src/browser/app-version.ts +14 -0
- package/src/browser/connection-warmup.ts +134 -0
- package/src/browser/cookie-name.ts +140 -0
- package/src/browser/event-controller.ts +192 -150
- package/src/browser/history-state.ts +21 -0
- package/src/browser/index.ts +3 -3
- package/src/browser/invalidate-client-cache.ts +52 -0
- package/src/browser/navigation-bridge.ts +131 -30
- package/src/browser/navigation-client.ts +186 -100
- package/src/browser/navigation-store-handle.ts +38 -0
- package/src/browser/navigation-store.ts +157 -74
- package/src/browser/navigation-transaction.ts +9 -59
- package/src/browser/network-error-handler.ts +34 -7
- package/src/browser/partial-update.ts +165 -112
- package/src/browser/prefetch/cache.ts +205 -62
- package/src/browser/prefetch/fetch.ts +347 -39
- package/src/browser/prefetch/queue.ts +42 -8
- package/src/browser/rango-state.ts +158 -76
- package/src/browser/react/Link.tsx +102 -15
- package/src/browser/react/NavigationProvider.tsx +295 -119
- package/src/browser/react/ScrollRestoration.tsx +10 -6
- package/src/browser/react/context.ts +7 -2
- package/src/browser/react/deferred-handle-resolution.ts +75 -0
- package/src/browser/react/filter-segment-order.ts +66 -7
- package/src/browser/react/index.ts +0 -48
- package/src/browser/react/location-state-shared.ts +178 -8
- package/src/browser/react/location-state.ts +39 -14
- package/src/browser/react/use-action.ts +6 -15
- package/src/browser/react/use-handle.ts +23 -69
- 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 +32 -7
- package/src/browser/react/use-params.ts +20 -10
- package/src/browser/react/use-reverse.ts +106 -0
- package/src/browser/react/use-router.ts +46 -11
- package/src/browser/react/use-search-params.ts +0 -5
- package/src/browser/react/use-segments.ts +11 -21
- package/src/browser/response-adapter.ts +99 -8
- package/src/browser/rsc-router.tsx +114 -24
- package/src/browser/scroll-restoration.ts +37 -22
- package/src/browser/segment-reconciler.ts +36 -14
- package/src/browser/segment-structure-assert.ts +2 -2
- package/src/browser/server-action-bridge.ts +222 -72
- package/src/browser/types.ts +102 -12
- package/src/browser/validate-redirect-origin.ts +43 -16
- package/src/build/collect-fallback-refs.ts +107 -0
- package/src/build/generate-manifest.ts +65 -40
- package/src/build/generate-route-types.ts +5 -1
- package/src/build/index.ts +8 -2
- package/src/build/prefix-tree-utils.ts +123 -0
- package/src/build/route-trie.ts +165 -36
- package/src/build/route-types/ast-route-extraction.ts +15 -8
- package/src/build/route-types/codegen.ts +16 -5
- package/src/build/route-types/include-resolution.ts +125 -24
- package/src/build/route-types/param-extraction.ts +6 -3
- package/src/build/route-types/per-module-writer.ts +22 -6
- package/src/build/route-types/router-processing.ts +260 -94
- package/src/build/route-types/scan-filter.ts +9 -2
- package/src/build/route-types/source-scan.ts +216 -0
- package/src/build/runtime-discovery.ts +9 -20
- package/src/cache/cache-error.ts +104 -0
- package/src/cache/cache-key-utils.ts +29 -13
- package/src/cache/cache-policy.ts +108 -34
- package/src/cache/cache-runtime.ts +224 -41
- package/src/cache/cache-scope.ts +188 -82
- package/src/cache/cache-tag.ts +103 -0
- package/src/cache/cf/cf-base64.ts +33 -0
- package/src/cache/cf/cf-cache-constants.ts +127 -0
- package/src/cache/cf/cf-cache-store.ts +1989 -378
- package/src/cache/cf/cf-cache-types.ts +349 -0
- package/src/cache/cf/cf-kv-utils.ts +46 -0
- package/src/cache/cf/cf-tag-marker-memo.ts +105 -0
- package/src/cache/cf/index.ts +6 -16
- package/src/cache/document-cache.ts +89 -21
- package/src/cache/handle-snapshot.ts +70 -0
- package/src/cache/index.ts +10 -20
- package/src/cache/memory-segment-store.ts +136 -37
- package/src/cache/profile-registry.ts +46 -31
- package/src/cache/read-through-swr.ts +56 -12
- package/src/cache/segment-codec.ts +9 -17
- package/src/cache/tag-invalidation.ts +230 -0
- package/src/cache/types.ts +37 -100
- package/src/client.rsc.tsx +44 -21
- package/src/client.tsx +119 -290
- package/src/cloudflare/index.ts +11 -0
- package/src/cloudflare/tracing.ts +109 -0
- package/src/component-utils.ts +19 -0
- package/src/components/DefaultDocument.tsx +8 -2
- package/src/context-var.ts +18 -6
- package/src/decode-loader-results.ts +52 -0
- package/src/defer.ts +196 -0
- package/src/deps/ssr.ts +0 -1
- package/src/encode-kv.ts +49 -0
- package/src/errors.ts +30 -4
- package/src/escape-script.ts +52 -0
- package/src/handle.ts +70 -22
- package/src/handles/MetaTags.tsx +62 -19
- package/src/handles/Scripts.tsx +183 -0
- package/src/handles/breadcrumbs.ts +37 -8
- package/src/handles/is-thenable.ts +19 -0
- package/src/handles/meta.ts +51 -40
- package/src/handles/script.ts +244 -0
- package/src/host/cookie-handler.ts +9 -60
- package/src/host/errors.ts +0 -24
- package/src/host/index.ts +8 -2
- package/src/host/pattern-matcher.ts +23 -52
- package/src/host/router.ts +107 -99
- package/src/host/testing.ts +40 -27
- package/src/host/types.ts +37 -4
- package/src/host/utils.ts +1 -1
- package/src/href-client.ts +137 -22
- package/src/index.rsc.ts +99 -13
- package/src/index.ts +139 -19
- package/src/internal-debug.ts +11 -10
- package/src/loader-store.ts +500 -0
- package/src/loader.rsc.ts +20 -13
- package/src/loader.ts +12 -11
- package/src/missing-id-error.ts +68 -0
- package/src/outlet-context.ts +1 -1
- package/src/outlet-provider.tsx +1 -5
- package/src/prerender/param-hash.ts +16 -16
- package/src/prerender/store.ts +37 -41
- package/src/prerender.ts +198 -82
- package/src/redirect-origin.ts +100 -0
- package/src/regex-escape.ts +8 -0
- package/src/render-error-thrower.tsx +20 -0
- package/src/response-utils.ts +62 -0
- package/src/reverse.ts +65 -15
- package/src/root-error-boundary.tsx +1 -19
- package/src/route-content-wrapper.tsx +19 -77
- package/src/route-definition/dsl-helpers.ts +461 -304
- package/src/route-definition/helper-factories.ts +28 -140
- package/src/route-definition/helpers-types.ts +143 -69
- package/src/route-definition/index.ts +4 -2
- package/src/route-definition/redirect.ts +51 -10
- package/src/route-definition/resolve-handler-use.ts +160 -0
- package/src/route-definition/use-item-types.ts +29 -0
- package/src/route-map-builder.ts +0 -16
- package/src/route-types.ts +37 -46
- package/src/router/basename.ts +14 -0
- package/src/router/content-negotiation.ts +164 -17
- package/src/router/error-handling.ts +45 -18
- package/src/router/find-match.ts +44 -23
- package/src/router/handler-context.ts +52 -31
- package/src/router/instrument.ts +350 -0
- package/src/router/intercept-resolution.ts +48 -24
- package/src/router/lazy-includes.ts +15 -52
- package/src/router/loader-resolution.ts +268 -56
- package/src/router/logging.ts +0 -6
- package/src/router/manifest.ts +40 -42
- package/src/router/match-api.ts +124 -204
- package/src/router/match-context.ts +0 -22
- package/src/router/match-handlers.ts +58 -58
- package/src/router/match-middleware/background-revalidation.ts +40 -24
- package/src/router/match-middleware/cache-lookup.ts +170 -276
- package/src/router/match-middleware/cache-store.ts +64 -52
- package/src/router/match-middleware/intercept-resolution.ts +0 -22
- package/src/router/match-middleware/segment-resolution.ts +45 -14
- package/src/router/match-pipelines.ts +1 -42
- package/src/router/match-result.ts +87 -39
- package/src/router/metrics.ts +0 -34
- package/src/router/middleware-types.ts +7 -140
- package/src/router/middleware.ts +266 -169
- package/src/router/navigation-snapshot.ts +131 -0
- package/src/router/params-util.ts +23 -0
- package/src/router/pattern-matching.ts +132 -90
- package/src/router/prefetch-cache-ttl.ts +51 -0
- package/src/router/prerender-match.ts +195 -56
- package/src/router/preview-match.ts +32 -102
- package/src/router/request-classification.ts +276 -0
- package/src/router/revalidation.ts +123 -73
- package/src/router/route-snapshot.ts +244 -0
- package/src/router/router-context.ts +3 -28
- package/src/router/router-interfaces.ts +115 -35
- package/src/router/router-options.ts +172 -15
- package/src/router/router-registry.ts +2 -5
- package/src/router/segment-resolution/fresh.ts +162 -84
- package/src/router/segment-resolution/helpers.ts +86 -6
- package/src/router/segment-resolution/loader-cache.ts +76 -39
- package/src/router/segment-resolution/revalidation.ts +351 -321
- package/src/router/segment-resolution/static-store.ts +19 -5
- package/src/router/segment-resolution/streamed-handler-telemetry.ts +52 -0
- package/src/router/segment-resolution/view-transition-default.ts +56 -0
- package/src/router/segment-resolution.ts +5 -1
- package/src/router/segment-wrappers.ts +6 -5
- package/src/router/state-cookie-name.ts +33 -0
- package/src/router/substitute-pattern-params.ts +56 -0
- package/src/router/telemetry-otel.ts +161 -199
- package/src/router/telemetry.ts +96 -19
- package/src/router/timeout.ts +0 -20
- package/src/router/tracing.ts +206 -0
- package/src/router/trie-matching.ts +163 -59
- package/src/router/types.ts +9 -63
- package/src/router/url-params.ts +44 -0
- package/src/router.ts +157 -54
- package/src/rsc/handler-context.ts +3 -2
- package/src/rsc/handler.ts +655 -529
- package/src/rsc/helpers.ts +168 -46
- package/src/rsc/index.ts +2 -5
- package/src/rsc/json-route-result.ts +38 -0
- package/src/rsc/loader-fetch.ts +122 -31
- package/src/rsc/manifest-init.ts +33 -42
- package/src/rsc/origin-guard.ts +39 -25
- package/src/rsc/progressive-enhancement.ts +131 -14
- package/src/rsc/redirect-guard.ts +99 -0
- package/src/rsc/response-cache-serve.ts +238 -0
- package/src/rsc/response-error.ts +79 -12
- package/src/rsc/response-route-handler.ts +99 -189
- package/src/rsc/rsc-rendering.ts +109 -74
- package/src/rsc/runtime-warnings.ts +23 -10
- package/src/rsc/server-action.ts +287 -115
- package/src/rsc/ssr-setup.ts +18 -2
- package/src/rsc/transition-gate.ts +89 -0
- package/src/rsc/types.ts +29 -9
- package/src/runtime-env.ts +18 -0
- package/src/search-params.ts +35 -30
- package/src/segment-content-promise.ts +67 -0
- package/src/segment-loader-promise.ts +149 -0
- package/src/segment-system.tsx +236 -202
- package/src/serialize.ts +243 -0
- package/src/server/context.ts +224 -52
- package/src/server/cookie-parse.ts +32 -0
- package/src/server/cookie-store.ts +80 -5
- package/src/server/handle-store.ts +40 -38
- package/src/server/loader-registry.ts +38 -46
- package/src/server/request-context.ts +401 -173
- package/src/ssr/index.tsx +24 -16
- package/src/static-handler.ts +27 -18
- package/src/testing/cache-status.ts +162 -0
- package/src/testing/collect-handle.ts +40 -0
- package/src/testing/dispatch.ts +701 -0
- package/src/testing/dom.entry.ts +22 -0
- package/src/testing/e2e/fixture.ts +188 -0
- package/src/testing/e2e/index.ts +128 -0
- package/src/testing/e2e/matchers.ts +35 -0
- package/src/testing/e2e/page-helpers.ts +272 -0
- package/src/testing/e2e/parity.ts +387 -0
- package/src/testing/e2e/server.ts +195 -0
- package/src/testing/flight-matchers.ts +97 -0
- package/src/testing/flight-normalize.ts +11 -0
- package/src/testing/flight-runtime.d.ts +57 -0
- package/src/testing/flight-tree.ts +682 -0
- package/src/testing/flight.entry.ts +52 -0
- package/src/testing/flight.ts +257 -0
- package/src/testing/generated-routes.ts +183 -0
- package/src/testing/index.ts +105 -0
- package/src/testing/internal/context.ts +371 -0
- package/src/testing/internal/flight-client-globals.ts +30 -0
- package/src/testing/internal/seed-vars.ts +54 -0
- package/src/testing/render-handler.ts +357 -0
- package/src/testing/render-route.tsx +581 -0
- package/src/testing/run-loader.ts +385 -0
- package/src/testing/run-middleware.ts +205 -0
- package/src/testing/run-transition-when.ts +164 -0
- package/src/testing/vitest-stubs/cloudflare-email.ts +9 -0
- package/src/testing/vitest-stubs/cloudflare-workers.ts +21 -0
- package/src/testing/vitest-stubs/plugin-rsc.ts +16 -0
- package/src/testing/vitest-stubs/version.ts +5 -0
- package/src/testing/vitest.ts +305 -0
- package/src/theme/ThemeProvider.tsx +20 -58
- package/src/theme/ThemeScript.tsx +7 -9
- package/src/theme/constants.ts +52 -13
- package/src/theme/index.ts +0 -7
- package/src/theme/theme-context.ts +1 -5
- package/src/theme/theme-script.ts +22 -21
- package/src/theme/use-theme.ts +0 -3
- package/src/types/boundaries.ts +0 -35
- package/src/types/cache-types.ts +17 -8
- package/src/types/error-types.ts +30 -90
- package/src/types/global-namespace.ts +54 -41
- package/src/types/handler-context.ts +125 -71
- package/src/types/index.ts +3 -10
- package/src/types/loader-types.ts +40 -11
- package/src/types/request-scope.ts +112 -0
- package/src/types/route-config.ts +6 -50
- package/src/types/route-entry.ts +12 -7
- package/src/types/segments.ts +136 -15
- package/src/urls/include-helper.ts +33 -70
- package/src/urls/index.ts +1 -11
- package/src/urls/path-helper-types.ts +68 -18
- package/src/urls/path-helper.ts +57 -111
- package/src/urls/pattern-types.ts +48 -19
- package/src/urls/response-types.ts +25 -22
- package/src/urls/type-extraction.ts +58 -139
- package/src/urls/urls-function.ts +1 -19
- package/src/use-loader.tsx +346 -89
- package/src/vite/debug.ts +185 -0
- package/src/vite/discovery/bundle-postprocess.ts +36 -38
- package/src/vite/discovery/discover-routers.ts +130 -85
- package/src/vite/discovery/discovery-errors.ts +194 -0
- package/src/vite/discovery/gate-state.ts +171 -0
- package/src/vite/discovery/prerender-collection.ts +214 -132
- package/src/vite/discovery/route-types-writer.ts +40 -84
- package/src/vite/discovery/self-gen-tracking.ts +27 -1
- package/src/vite/discovery/state.ts +57 -4
- package/src/vite/discovery/virtual-module-codegen.ts +14 -34
- package/src/vite/index.ts +6 -0
- package/src/vite/inject-client-debug.ts +36 -0
- package/src/vite/plugin-types.ts +178 -5
- package/src/vite/plugins/cjs-to-esm.ts +16 -19
- package/src/vite/plugins/client-ref-dedup.ts +16 -11
- package/src/vite/plugins/client-ref-hashing.ts +28 -15
- package/src/vite/plugins/cloudflare-protocol-loader-hook.d.mts +23 -0
- package/src/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
- package/src/vite/plugins/cloudflare-protocol-stub.ts +194 -0
- package/src/vite/plugins/expose-action-id.ts +48 -95
- package/src/vite/plugins/expose-id-utils.ts +96 -51
- package/src/vite/plugins/expose-ids/export-analysis.ts +101 -34
- package/src/vite/plugins/expose-ids/handler-transform.ts +15 -64
- package/src/vite/plugins/expose-ids/loader-transform.ts +14 -24
- package/src/vite/plugins/expose-ids/router-transform.ts +118 -29
- package/src/vite/plugins/expose-internal-ids.ts +553 -317
- package/src/vite/plugins/performance-tracks.ts +64 -170
- package/src/vite/plugins/refresh-cmd.ts +89 -27
- package/src/vite/plugins/use-cache-transform.ts +73 -83
- package/src/vite/plugins/version-injector.ts +40 -29
- package/src/vite/plugins/version-plugin.ts +37 -40
- package/src/vite/plugins/virtual-entries.ts +39 -25
- package/src/vite/rango.ts +118 -114
- package/src/vite/router-discovery.ts +941 -142
- package/src/vite/utils/ast-handler-extract.ts +26 -35
- package/src/vite/utils/banner.ts +1 -1
- package/src/vite/utils/bundle-analysis.ts +10 -15
- package/src/vite/utils/client-chunks.ts +184 -0
- package/src/vite/utils/directive-prologue.ts +40 -0
- package/src/vite/utils/forward-user-plugins.ts +171 -0
- package/src/vite/utils/manifest-utils.ts +4 -59
- package/src/vite/utils/package-resolution.ts +20 -52
- package/src/vite/utils/prerender-utils.ts +81 -34
- package/src/vite/utils/shared-utils.ts +92 -42
- package/src/browser/action-response-classifier.ts +0 -99
- package/src/browser/debug-channel.ts +0 -93
- 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
|
@@ -20,6 +20,9 @@ import type { Plugin } from "vite";
|
|
|
20
20
|
import path from "node:path";
|
|
21
21
|
import MagicString from "magic-string";
|
|
22
22
|
import { normalizePath, hashId } from "./expose-id-utils.js";
|
|
23
|
+
import { createRangoDebugger, createCounter, NS } from "../debug.js";
|
|
24
|
+
|
|
25
|
+
const debug = createRangoDebugger(NS.transform);
|
|
23
26
|
|
|
24
27
|
const CACHE_RUNTIME_IMPORT = "@rangojs/router/cache-runtime";
|
|
25
28
|
|
|
@@ -27,11 +30,14 @@ const CACHE_RUNTIME_IMPORT = "@rangojs/router/cache-runtime";
|
|
|
27
30
|
// and should not be wrapped (children can't be cache-keyed).
|
|
28
31
|
const LAYOUT_TEMPLATE_PATTERN = /\/(layout|template)\.(tsx?|jsx?)$/;
|
|
29
32
|
|
|
33
|
+
export const USE_CACHE_DIRECTIVE_RE: RegExp = /^use cache(:\s*[\w-]+)?$/;
|
|
34
|
+
|
|
30
35
|
export function useCacheTransform(): Plugin {
|
|
31
36
|
let projectRoot = "";
|
|
32
37
|
let isBuild = false;
|
|
33
38
|
let rscTransforms: typeof import("@vitejs/plugin-rsc/transforms") | null =
|
|
34
39
|
null;
|
|
40
|
+
const counter = createCounter(debug, "use-cache");
|
|
35
41
|
|
|
36
42
|
return {
|
|
37
43
|
name: "@rangojs/router:use-cache",
|
|
@@ -42,6 +48,10 @@ export function useCacheTransform(): Plugin {
|
|
|
42
48
|
isBuild = config.command === "build";
|
|
43
49
|
},
|
|
44
50
|
|
|
51
|
+
buildEnd() {
|
|
52
|
+
counter?.flush();
|
|
53
|
+
},
|
|
54
|
+
|
|
45
55
|
async transform(code, id) {
|
|
46
56
|
// Only process in RSC environment
|
|
47
57
|
if (this.environment?.name !== "rsc") return;
|
|
@@ -55,63 +65,60 @@ export function useCacheTransform(): Plugin {
|
|
|
55
65
|
// Only JS/TS files
|
|
56
66
|
if (!/\.(tsx?|jsx?|mjs)$/.test(id)) return;
|
|
57
67
|
|
|
58
|
-
|
|
59
|
-
|
|
68
|
+
const start = counter ? performance.now() : 0;
|
|
69
|
+
try {
|
|
70
|
+
if (!rscTransforms) {
|
|
71
|
+
try {
|
|
72
|
+
rscTransforms = await import("@vitejs/plugin-rsc/transforms");
|
|
73
|
+
} catch {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const {
|
|
79
|
+
hasDirective,
|
|
80
|
+
transformWrapExport,
|
|
81
|
+
transformHoistInlineDirective,
|
|
82
|
+
} = rscTransforms;
|
|
83
|
+
|
|
84
|
+
let ast: any;
|
|
60
85
|
try {
|
|
61
|
-
|
|
86
|
+
const { parseAst } = await import("vite");
|
|
87
|
+
ast = parseAst(code, { lang: "tsx" });
|
|
62
88
|
} catch {
|
|
63
89
|
return;
|
|
64
90
|
}
|
|
65
|
-
}
|
|
66
91
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
const filePath = normalizePath(path.relative(projectRoot, id));
|
|
83
|
-
const isLayoutOrTemplate = LAYOUT_TEMPLATE_PATTERN.test(id);
|
|
92
|
+
const filePath = normalizePath(path.relative(projectRoot, id));
|
|
93
|
+
const isLayoutOrTemplate = LAYOUT_TEMPLATE_PATTERN.test(id);
|
|
94
|
+
|
|
95
|
+
if (hasDirective(ast.body, "use cache")) {
|
|
96
|
+
return transformFileLevelUseCache(
|
|
97
|
+
code,
|
|
98
|
+
ast,
|
|
99
|
+
filePath,
|
|
100
|
+
id,
|
|
101
|
+
isBuild,
|
|
102
|
+
isLayoutOrTemplate,
|
|
103
|
+
transformWrapExport,
|
|
104
|
+
);
|
|
105
|
+
}
|
|
84
106
|
|
|
85
|
-
|
|
86
|
-
if (hasDirective(ast.body, "use cache")) {
|
|
87
|
-
return transformFileLevelUseCache(
|
|
107
|
+
const functionResult = transformFunctionLevelUseCache(
|
|
88
108
|
code,
|
|
89
109
|
ast,
|
|
90
110
|
filePath,
|
|
91
111
|
id,
|
|
92
112
|
isBuild,
|
|
93
|
-
|
|
94
|
-
transformWrapExport,
|
|
113
|
+
transformHoistInlineDirective,
|
|
95
114
|
);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
// Check for function-level "use cache" / "use cache: profileName"
|
|
99
|
-
// (only if there's no file-level directive but code still contains the string)
|
|
100
|
-
const functionResult = transformFunctionLevelUseCache(
|
|
101
|
-
code,
|
|
102
|
-
ast,
|
|
103
|
-
filePath,
|
|
104
|
-
id,
|
|
105
|
-
isBuild,
|
|
106
|
-
transformHoistInlineDirective,
|
|
107
|
-
);
|
|
108
115
|
|
|
109
|
-
|
|
110
|
-
// exist. A file may contain both valid and invalid "use cache" directives
|
|
111
|
-
// in different functions — the invalid ones should still warn.
|
|
112
|
-
warnOnNearMissDirectives(ast, id, this.warn.bind(this));
|
|
116
|
+
warnOnNearMissDirectives(ast, id, this.warn.bind(this));
|
|
113
117
|
|
|
114
|
-
|
|
118
|
+
if (functionResult) return functionResult;
|
|
119
|
+
} finally {
|
|
120
|
+
counter?.record(id, performance.now() - start);
|
|
121
|
+
}
|
|
115
122
|
},
|
|
116
123
|
};
|
|
117
124
|
}
|
|
@@ -125,8 +132,7 @@ function transformFileLevelUseCache(
|
|
|
125
132
|
isLayoutOrTemplate: boolean,
|
|
126
133
|
transformWrapExport: (typeof import("@vitejs/plugin-rsc/transforms"))["transformWrapExport"],
|
|
127
134
|
) {
|
|
128
|
-
|
|
129
|
-
const nonFunctionExports: string[] = [];
|
|
135
|
+
const unconfirmedExports: string[] = [];
|
|
130
136
|
|
|
131
137
|
const { exportNames, output } = transformWrapExport(code, ast, {
|
|
132
138
|
runtime: (value: string, name: string) => {
|
|
@@ -135,30 +141,38 @@ function transformFileLevelUseCache(
|
|
|
135
141
|
},
|
|
136
142
|
rejectNonAsyncFunction: false,
|
|
137
143
|
filter: (name: string, meta: { isFunction?: boolean }) => {
|
|
138
|
-
// Skip default export of layout/template files (they receive children)
|
|
139
144
|
if (name === "default" && isLayoutOrTemplate) return false;
|
|
140
|
-
//
|
|
141
|
-
|
|
142
|
-
|
|
145
|
+
// isFunction is boolean | undefined: true = confirmed function, false =
|
|
146
|
+
// confirmed non-function, undefined = cannot tell statically (e.g. a
|
|
147
|
+
// factory/HOF initializer `const x = makeCached(fn)`). Deliberate policy:
|
|
148
|
+
// require a confirmed function and reject everything else, including
|
|
149
|
+
// indeterminate initializers that may be functions at runtime -- rewrite
|
|
150
|
+
// those as direct async functions. (Pre-#1246 plugin-rsc reported false,
|
|
151
|
+
// not undefined, here, so === false would wrongly wrap them post-bump.)
|
|
152
|
+
if (meta.isFunction !== true) {
|
|
153
|
+
unconfirmedExports.push(name);
|
|
143
154
|
return false;
|
|
144
155
|
}
|
|
145
156
|
return true;
|
|
146
157
|
},
|
|
147
158
|
});
|
|
148
159
|
|
|
149
|
-
if (
|
|
160
|
+
if (unconfirmedExports.length > 0) {
|
|
161
|
+
const plural = unconfirmedExports.length > 1;
|
|
150
162
|
throw new Error(
|
|
151
|
-
`[rango:use-cache] File-level "use cache" in ${sourceId}
|
|
152
|
-
`
|
|
153
|
-
`${
|
|
154
|
-
|
|
155
|
-
`
|
|
156
|
-
`
|
|
163
|
+
`[rango:use-cache] File-level "use cache" in ${sourceId} only wraps ` +
|
|
164
|
+
`exports that are statically-confirmed functions. ` +
|
|
165
|
+
`${plural ? "These exports are" : "This export is"} not: ` +
|
|
166
|
+
`${unconfirmedExports.map((n) => `"${n}"`).join(", ")}. ` +
|
|
167
|
+
`Declare them directly (export async function foo() {} or ` +
|
|
168
|
+
`export const foo = async () => {}). A factory or otherwise ` +
|
|
169
|
+
`statically-indeterminate initializer (export const foo = makeCached(fn)) ` +
|
|
170
|
+
`is rejected even if it returns a function at runtime -- rewrite it as a ` +
|
|
171
|
+
`direct async function, or move non-function exports to a separate module.`,
|
|
157
172
|
);
|
|
158
173
|
}
|
|
159
174
|
|
|
160
175
|
if (exportNames.length === 0) {
|
|
161
|
-
// Even if no exports were wrapped, strip the directive
|
|
162
176
|
const s = new MagicString(code);
|
|
163
177
|
const directive = findFileLevelDirective(ast);
|
|
164
178
|
if (directive) {
|
|
@@ -175,12 +189,10 @@ function transformFileLevelUseCache(
|
|
|
175
189
|
return;
|
|
176
190
|
}
|
|
177
191
|
|
|
178
|
-
// Prepend the import
|
|
179
192
|
output.prepend(
|
|
180
193
|
`import { registerCachedFunction as __rango_registerCachedFunction } from ${JSON.stringify(CACHE_RUNTIME_IMPORT)};\n`,
|
|
181
194
|
);
|
|
182
195
|
|
|
183
|
-
// Replace the directive with a comment
|
|
184
196
|
const directive = findFileLevelDirective(ast);
|
|
185
197
|
if (directive) {
|
|
186
198
|
output.overwrite(
|
|
@@ -206,7 +218,7 @@ function transformFunctionLevelUseCache(
|
|
|
206
218
|
) {
|
|
207
219
|
try {
|
|
208
220
|
const { output, names } = transformHoistInlineDirective(code, ast, {
|
|
209
|
-
directive:
|
|
221
|
+
directive: USE_CACHE_DIRECTIVE_RE,
|
|
210
222
|
runtime: (
|
|
211
223
|
value: string,
|
|
212
224
|
name: string,
|
|
@@ -224,9 +236,6 @@ function transformFunctionLevelUseCache(
|
|
|
224
236
|
|
|
225
237
|
if (names.length === 0) return;
|
|
226
238
|
|
|
227
|
-
// Use a top-level import instead of await import() — the hoisted wrapper
|
|
228
|
-
// may be placed in a non-async context (e.g., inside a synchronous
|
|
229
|
-
// urls() callback) where await is not allowed.
|
|
230
239
|
output.prepend(
|
|
231
240
|
`import { registerCachedFunction as __rango_registerCachedFunction } from ${JSON.stringify(CACHE_RUNTIME_IMPORT)};\n`,
|
|
232
241
|
);
|
|
@@ -241,9 +250,6 @@ function transformFunctionLevelUseCache(
|
|
|
241
250
|
}
|
|
242
251
|
}
|
|
243
252
|
|
|
244
|
-
/**
|
|
245
|
-
* Find the file-level "use cache" directive AST node for removal.
|
|
246
|
-
*/
|
|
247
253
|
function findFileLevelDirective(
|
|
248
254
|
ast: any,
|
|
249
255
|
): { start: number; end: number } | null {
|
|
@@ -260,23 +266,8 @@ function findFileLevelDirective(
|
|
|
260
266
|
return null;
|
|
261
267
|
}
|
|
262
268
|
|
|
263
|
-
/**
|
|
264
|
-
* The valid directive regex (must stay in sync with transformFunctionLevelUseCache).
|
|
265
|
-
*/
|
|
266
|
-
const VALID_DIRECTIVE_RE = /^use cache(:\s*[\w-]+)?$/;
|
|
267
|
-
|
|
268
|
-
/**
|
|
269
|
-
* Regex for near-miss: starts with "use cache:" but has invalid tokens.
|
|
270
|
-
*/
|
|
271
269
|
const NEAR_MISS_RE = /^use cache:\s*.+$/;
|
|
272
270
|
|
|
273
|
-
/**
|
|
274
|
-
* Walk the AST looking for string literals that look like malformed
|
|
275
|
-
* "use cache" directives and emit a Vite warning for each.
|
|
276
|
-
*
|
|
277
|
-
* This catches cases like `"use cache: bad.name"` or `"use cache: "`
|
|
278
|
-
* that the transform regex silently ignores.
|
|
279
|
-
*/
|
|
280
271
|
function warnOnNearMissDirectives(
|
|
281
272
|
ast: any,
|
|
282
273
|
fileId: string,
|
|
@@ -294,7 +285,7 @@ function warnOnNearMissDirectives(
|
|
|
294
285
|
if (
|
|
295
286
|
value.startsWith("use cache") &&
|
|
296
287
|
NEAR_MISS_RE.test(value) &&
|
|
297
|
-
!
|
|
288
|
+
!USE_CACHE_DIRECTIVE_RE.test(value)
|
|
298
289
|
) {
|
|
299
290
|
const profilePart = value.slice("use cache:".length).trim();
|
|
300
291
|
warn(
|
|
@@ -304,7 +295,6 @@ function warnOnNearMissDirectives(
|
|
|
304
295
|
}
|
|
305
296
|
}
|
|
306
297
|
|
|
307
|
-
// Walk into function bodies where directives appear
|
|
308
298
|
for (const key of Object.keys(node)) {
|
|
309
299
|
const child = node[key];
|
|
310
300
|
if (Array.isArray(child)) {
|
|
@@ -1,12 +1,26 @@
|
|
|
1
1
|
import type { Plugin } from "vite";
|
|
2
2
|
import { resolve } from "node:path";
|
|
3
3
|
import * as Vite from "vite";
|
|
4
|
+
import { resolveRscEntryFromConfig } from "../utils/shared-utils.js";
|
|
5
|
+
import { RSC_ENTRY_BOOTSTRAP_IMPORTS } from "./virtual-entries.js";
|
|
4
6
|
|
|
5
7
|
/**
|
|
6
|
-
* Plugin that auto-injects VERSION and
|
|
7
|
-
* If a custom entry.rsc file uses createRSCHandler but
|
|
8
|
-
* this transform adds the import and property
|
|
9
|
-
*
|
|
8
|
+
* Plugin that auto-injects VERSION, routes-manifest, and loader-manifest into
|
|
9
|
+
* custom entry.rsc files. If a custom entry.rsc file uses createRSCHandler but
|
|
10
|
+
* doesn't pass version, this transform adds the import and property
|
|
11
|
+
* automatically. It also ensures the routes-manifest and loader-manifest
|
|
12
|
+
* virtual modules are always imported.
|
|
13
|
+
*
|
|
14
|
+
* The loader-manifest import is what makes fetchable loaders resolvable on a
|
|
15
|
+
* custom worker entry. The virtual RSC entry (getVirtualEntryRSC) imports the
|
|
16
|
+
* loader manifest itself, but a hand-written entry (e.g. a Cloudflare
|
|
17
|
+
* worker.rsc.tsx) does not — so without this injection, setLoaderImports() is
|
|
18
|
+
* never bundled, lazyLoaderImports stays null at runtime, and a fetchable
|
|
19
|
+
* loader that is never imported by the server graph (not registered via
|
|
20
|
+
* loader(), reachable only through a client component) cannot be found by the
|
|
21
|
+
* _rsc_loader endpoint in production. Dev still works because it resolves
|
|
22
|
+
* loaders by parsing their id into a file path; only production relied on the
|
|
23
|
+
* manifest, hence the production-only failure this fixes.
|
|
10
24
|
* @internal
|
|
11
25
|
*/
|
|
12
26
|
export function createVersionInjectorPlugin(
|
|
@@ -20,18 +34,7 @@ export function createVersionInjectorPlugin(
|
|
|
20
34
|
|
|
21
35
|
configResolved(config) {
|
|
22
36
|
let entryPath = rscEntryPath;
|
|
23
|
-
|
|
24
|
-
// The @cloudflare/vite-plugin reads wrangler config (toml/json/jsonc)
|
|
25
|
-
// and sets optimizeDeps.entries on the RSC environment.
|
|
26
|
-
if (!entryPath) {
|
|
27
|
-
const rscEnvConfig = (config.environments as any)?.["rsc"];
|
|
28
|
-
const entries = rscEnvConfig?.optimizeDeps?.entries;
|
|
29
|
-
if (typeof entries === "string") {
|
|
30
|
-
entryPath = entries;
|
|
31
|
-
} else if (Array.isArray(entries) && entries.length > 0) {
|
|
32
|
-
entryPath = entries[0];
|
|
33
|
-
}
|
|
34
|
-
}
|
|
37
|
+
if (!entryPath) entryPath = resolveRscEntryFromConfig(config);
|
|
35
38
|
if (entryPath) {
|
|
36
39
|
resolvedEntryPath = resolve(config.root, entryPath);
|
|
37
40
|
}
|
|
@@ -39,7 +42,6 @@ export function createVersionInjectorPlugin(
|
|
|
39
42
|
|
|
40
43
|
transform(code, id) {
|
|
41
44
|
if (!resolvedEntryPath) return null;
|
|
42
|
-
// Only transform the RSC entry file
|
|
43
45
|
const normalizedId = Vite.normalizePath(id);
|
|
44
46
|
const normalizedEntry = Vite.normalizePath(resolvedEntryPath);
|
|
45
47
|
|
|
@@ -47,16 +49,13 @@ export function createVersionInjectorPlugin(
|
|
|
47
49
|
return null;
|
|
48
50
|
}
|
|
49
51
|
|
|
50
|
-
//
|
|
51
|
-
//
|
|
52
|
-
const prepend: string[] =
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
if (!code.includes("virtual:rsc-router/routes-manifest")) {
|
|
56
|
-
prepend.push(`import "virtual:rsc-router/routes-manifest";`);
|
|
57
|
-
}
|
|
52
|
+
// Same startup bootstrap imports the generated virtual RSC entry uses,
|
|
53
|
+
// from the single shared list so the two paths cannot drift.
|
|
54
|
+
const prepend: string[] = RSC_ENTRY_BOOTSTRAP_IMPORTS.map(
|
|
55
|
+
(id) => `import "${id}";`,
|
|
56
|
+
);
|
|
58
57
|
|
|
59
|
-
|
|
58
|
+
let newCode = code;
|
|
60
59
|
const needsVersion =
|
|
61
60
|
code.includes("createRSCHandler") &&
|
|
62
61
|
!code.includes("@rangojs/router:version") &&
|
|
@@ -70,9 +69,21 @@ export function createVersionInjectorPlugin(
|
|
|
70
69
|
);
|
|
71
70
|
}
|
|
72
71
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
72
|
+
const lines = newCode.split("\n");
|
|
73
|
+
let insertAt = 0;
|
|
74
|
+
while (insertAt < lines.length) {
|
|
75
|
+
const trimmed = lines[insertAt]!.trim();
|
|
76
|
+
if (trimmed === "" || /^\/\/\/\s*<reference\b/.test(trimmed)) {
|
|
77
|
+
insertAt++;
|
|
78
|
+
} else {
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
newCode = [
|
|
83
|
+
...lines.slice(0, insertAt),
|
|
84
|
+
...prepend,
|
|
85
|
+
...lines.slice(insertAt),
|
|
86
|
+
].join("\n");
|
|
76
87
|
|
|
77
88
|
return {
|
|
78
89
|
code: newCode,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { parseAst, type Plugin } from "vite";
|
|
2
2
|
import { VIRTUAL_IDS, getVirtualVersionContent } from "./virtual-entries.js";
|
|
3
|
+
import { hasUseClientDirective } from "../utils/directive-prologue.js";
|
|
3
4
|
|
|
4
5
|
interface ClientModuleSignature {
|
|
5
6
|
key: string;
|
|
@@ -16,30 +17,17 @@ function normalizeModuleId(id: string): string {
|
|
|
16
17
|
function getClientModuleSignature(
|
|
17
18
|
source: string,
|
|
18
19
|
): ClientModuleSignature | undefined {
|
|
20
|
+
// Same leading-directive sniff the rango HMR plugin uses (shared helper, so the
|
|
21
|
+
// two agree on what counts as a client module). A parse failure yields false.
|
|
22
|
+
if (!hasUseClientDirective(source)) return undefined;
|
|
23
|
+
|
|
19
24
|
let program: any;
|
|
20
25
|
try {
|
|
21
|
-
program = parseAst(source, {
|
|
26
|
+
program = parseAst(source, { lang: "tsx" });
|
|
22
27
|
} catch {
|
|
23
28
|
return undefined;
|
|
24
29
|
}
|
|
25
30
|
|
|
26
|
-
let isUseClient = false;
|
|
27
|
-
for (const node of program.body ?? []) {
|
|
28
|
-
if (
|
|
29
|
-
node?.type === "ExpressionStatement" &&
|
|
30
|
-
node.expression?.type === "Literal" &&
|
|
31
|
-
typeof node.expression.value === "string"
|
|
32
|
-
) {
|
|
33
|
-
if (node.expression.value === "use client") {
|
|
34
|
-
isUseClient = true;
|
|
35
|
-
}
|
|
36
|
-
continue;
|
|
37
|
-
}
|
|
38
|
-
break;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
if (!isUseClient) return undefined;
|
|
42
|
-
|
|
43
31
|
const exports = new Set<string>();
|
|
44
32
|
let hasDefault = false;
|
|
45
33
|
let hasExportAll = false;
|
|
@@ -133,14 +121,13 @@ export function createVersionPlugin(): Plugin {
|
|
|
133
121
|
let currentVersion = buildVersion;
|
|
134
122
|
let isDev = false;
|
|
135
123
|
let server: any = null;
|
|
124
|
+
let resolvedCacheDir: string | undefined;
|
|
136
125
|
const clientModuleSignatures = new Map<string, ClientModuleSignature>();
|
|
137
126
|
|
|
138
127
|
let versionCounter = 0;
|
|
139
128
|
const bumpVersion = (reason: string) => {
|
|
140
|
-
// Use timestamp + counter to guarantee uniqueness even when multiple
|
|
141
|
-
// bumps happen within the same millisecond (e.g. cascading HMR events).
|
|
142
129
|
currentVersion = Date.now().toString(16) + String(++versionCounter);
|
|
143
|
-
console.log(`[
|
|
130
|
+
console.log(`[rango] ${reason}, version updated: ${currentVersion}`);
|
|
144
131
|
|
|
145
132
|
const rscEnv = server?.environments?.rsc;
|
|
146
133
|
const versionMod = rscEnv?.moduleGraph?.getModuleById(
|
|
@@ -157,6 +144,9 @@ export function createVersionPlugin(): Plugin {
|
|
|
157
144
|
|
|
158
145
|
configResolved(config) {
|
|
159
146
|
isDev = config.command === "serve";
|
|
147
|
+
resolvedCacheDir = config.cacheDir
|
|
148
|
+
? String(config.cacheDir).replace(/\\/g, "/")
|
|
149
|
+
: undefined;
|
|
160
150
|
},
|
|
161
151
|
|
|
162
152
|
configureServer(devServer) {
|
|
@@ -203,19 +193,15 @@ export function createVersionPlugin(): Plugin {
|
|
|
203
193
|
return null;
|
|
204
194
|
},
|
|
205
195
|
|
|
206
|
-
// Track RSC module changes and update version
|
|
207
196
|
async hotUpdate(ctx) {
|
|
208
197
|
if (!isDev) return;
|
|
209
198
|
|
|
210
|
-
// Check if this is an RSC environment update (not client/ssr)
|
|
211
|
-
// RSC modules affect server-rendered content and cached payloads
|
|
212
|
-
// In Vite 6, environment is accessed via `this.environment`
|
|
213
199
|
const isRscModule = this.environment?.name === "rsc";
|
|
214
200
|
|
|
215
201
|
if (!isRscModule) return;
|
|
216
202
|
|
|
217
|
-
|
|
218
|
-
|
|
203
|
+
if (isViteDepCachePath(ctx.file, resolvedCacheDir)) return;
|
|
204
|
+
|
|
219
205
|
if (
|
|
220
206
|
ctx.modules.length === 1 &&
|
|
221
207
|
ctx.modules[0].id === "\0" + VIRTUAL_IDS.version
|
|
@@ -230,9 +216,6 @@ export function createVersionPlugin(): Plugin {
|
|
|
230
216
|
const source = await ctx.read();
|
|
231
217
|
const nextSignature = getClientModuleSignature(source);
|
|
232
218
|
if (nextSignature) {
|
|
233
|
-
// "use client" file — compare export signatures.
|
|
234
|
-
// client-component-hmr may have cleared ctx.modules, so we
|
|
235
|
-
// cannot rely on ctx.modules.length for these files.
|
|
236
219
|
clientModuleSignatures.set(filePath, nextSignature);
|
|
237
220
|
if (
|
|
238
221
|
previousSignature &&
|
|
@@ -243,20 +226,11 @@ export function createVersionPlugin(): Plugin {
|
|
|
243
226
|
} else {
|
|
244
227
|
clientModuleSignatures.delete(filePath);
|
|
245
228
|
if (!previousSignature) {
|
|
246
|
-
// Not and never was "use client" — use module graph check.
|
|
247
|
-
// ctx.modules is reliable for pure server files (only
|
|
248
|
-
// client-component-hmr clears it for "use client" modules).
|
|
249
229
|
if (ctx.modules.length === 0) return;
|
|
250
230
|
}
|
|
251
|
-
// Was "use client" but directive removed — boundary changed,
|
|
252
|
-
// bump below.
|
|
253
231
|
}
|
|
254
|
-
} catch {
|
|
255
|
-
// Fail open: if we can't read or parse the update, invalidate.
|
|
256
|
-
}
|
|
232
|
+
} catch {}
|
|
257
233
|
} else {
|
|
258
|
-
// Non-code file (json, css, etc.) — only bump if it's actually
|
|
259
|
-
// referenced by the RSC module graph.
|
|
260
234
|
if (ctx.modules.length === 0) return;
|
|
261
235
|
}
|
|
262
236
|
|
|
@@ -264,3 +238,26 @@ export function createVersionPlugin(): Plugin {
|
|
|
264
238
|
},
|
|
265
239
|
};
|
|
266
240
|
}
|
|
241
|
+
|
|
242
|
+
export function isViteDepCachePath(
|
|
243
|
+
filePath: string | undefined,
|
|
244
|
+
cacheDir?: string,
|
|
245
|
+
): boolean {
|
|
246
|
+
if (!filePath) return false;
|
|
247
|
+
const normalized = filePath.replace(/\\/g, "/");
|
|
248
|
+
|
|
249
|
+
if (cacheDir) {
|
|
250
|
+
const normalizedCacheDir = cacheDir.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
251
|
+
if (
|
|
252
|
+
normalized === normalizedCacheDir ||
|
|
253
|
+
normalized.startsWith(normalizedCacheDir + "/")
|
|
254
|
+
) {
|
|
255
|
+
return true;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
return (
|
|
260
|
+
/\/node_modules\/\.vite[^/]*\//.test(normalized) ||
|
|
261
|
+
normalized.includes("/.vite-isolated/")
|
|
262
|
+
);
|
|
263
|
+
}
|
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Default virtual entry file contents for rsc-router.
|
|
3
|
-
* These are used when users don't provide their own entry files.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
1
|
export const VIRTUAL_ENTRY_BROWSER: string = `
|
|
7
2
|
import {
|
|
8
3
|
createFromReadableStream,
|
|
@@ -14,7 +9,7 @@ import {
|
|
|
14
9
|
import { createElement, StrictMode } from "react";
|
|
15
10
|
import { hydrateRoot } from "react-dom/client";
|
|
16
11
|
import { rscStream } from "@rangojs/router/internal/deps/html-stream-client";
|
|
17
|
-
import { initBrowserApp,
|
|
12
|
+
import { initBrowserApp, Rango } from "@rangojs/router/browser";
|
|
18
13
|
|
|
19
14
|
async function initializeApp() {
|
|
20
15
|
const deps = {
|
|
@@ -25,11 +20,16 @@ async function initializeApp() {
|
|
|
25
20
|
createTemporaryReferenceSet,
|
|
26
21
|
};
|
|
27
22
|
|
|
28
|
-
|
|
23
|
+
// initBrowserApp resolves the initial payload and returns the browser app
|
|
24
|
+
// context, including strictMode (default true) from createRouter. StrictMode
|
|
25
|
+
// is the default; createRouter({ strictMode: false }) ships the opt-out in the
|
|
26
|
+
// payload metadata. StrictMode emits no DOM, so toggling never changes markup.
|
|
27
|
+
const { strictMode } = await initBrowserApp({ rscStream, deps });
|
|
29
28
|
|
|
29
|
+
const app = createElement(Rango);
|
|
30
30
|
hydrateRoot(
|
|
31
31
|
document,
|
|
32
|
-
createElement(StrictMode, null,
|
|
32
|
+
strictMode === false ? app : createElement(StrictMode, null, app)
|
|
33
33
|
);
|
|
34
34
|
}
|
|
35
35
|
|
|
@@ -52,9 +52,31 @@ export const renderHTML = createSSRHandler({
|
|
|
52
52
|
`.trim();
|
|
53
53
|
|
|
54
54
|
/**
|
|
55
|
-
*
|
|
55
|
+
* Virtual modules an RSC entry must import at startup to register the data the
|
|
56
|
+
* request handler needs before the first request arrives:
|
|
57
|
+
*
|
|
58
|
+
* - routes-manifest: the pre-generated route map so href()/matching work on a
|
|
59
|
+
* cold start (full map in build, in-memory no-op in dev).
|
|
60
|
+
* - loader-manifest: setLoaderImports() for fetchable loaders. Critical for
|
|
61
|
+
* serverless/multi-process deployments and for fetchable loaders reachable
|
|
62
|
+
* only through a client component — without it those loaders are never
|
|
63
|
+
* registered for the _rsc_loader endpoint and fail in production.
|
|
64
|
+
*
|
|
65
|
+
* Single source of truth: both the generated virtual RSC entry below and the
|
|
66
|
+
* custom-entry injector (version-injector) consume this list, so a new
|
|
67
|
+
* bootstrap manifest cannot be added to one path and forgotten on the other.
|
|
68
|
+
* That exact drift (loader-manifest present here but missing from the injector)
|
|
69
|
+
* is what left fetchable loaders unresolved on custom worker entries.
|
|
56
70
|
*/
|
|
71
|
+
export const RSC_ENTRY_BOOTSTRAP_IMPORTS: readonly string[] = [
|
|
72
|
+
"virtual:rsc-router/routes-manifest",
|
|
73
|
+
"virtual:rsc-router/loader-manifest",
|
|
74
|
+
];
|
|
75
|
+
|
|
57
76
|
export function getVirtualEntryRSC(routerPath: string): string {
|
|
77
|
+
const bootstrapImports = RSC_ENTRY_BOOTSTRAP_IMPORTS.map(
|
|
78
|
+
(id) => `import "${id}";`,
|
|
79
|
+
).join("\n");
|
|
58
80
|
return `
|
|
59
81
|
import {
|
|
60
82
|
renderToReadableStream,
|
|
@@ -68,15 +90,9 @@ import { router } from "${routerPath}";
|
|
|
68
90
|
import { createRSCHandler } from "@rangojs/router/internal/rsc-handler";
|
|
69
91
|
import { VERSION } from "@rangojs/router:version";
|
|
70
92
|
|
|
71
|
-
//
|
|
72
|
-
//
|
|
73
|
-
|
|
74
|
-
import "virtual:rsc-router/loader-manifest";
|
|
75
|
-
|
|
76
|
-
// Import pre-generated route manifest so href() works immediately on cold start.
|
|
77
|
-
// In build mode, this contains the full route map generated at build time.
|
|
78
|
-
// In dev mode, this is a no-op (manifest is populated in-memory by the discovery plugin).
|
|
79
|
-
import "virtual:rsc-router/routes-manifest";
|
|
93
|
+
// Startup bootstrap imports (routes + loader manifests). See
|
|
94
|
+
// RSC_ENTRY_BOOTSTRAP_IMPORTS — the same list the custom-entry injector uses.
|
|
95
|
+
${bootstrapImports}
|
|
80
96
|
|
|
81
97
|
// Lazily create the handler on first request so that ESM live bindings
|
|
82
98
|
// have resolved by the time we read \`router\`. During HMR the module may
|
|
@@ -87,6 +103,11 @@ export default function handler(request, env) {
|
|
|
87
103
|
_handler = createRSCHandler({
|
|
88
104
|
router,
|
|
89
105
|
version: VERSION,
|
|
106
|
+
// Forward the router's CSP nonce provider. createRSCHandler reads the
|
|
107
|
+
// provider only from options.nonce; without this, createRouter({ nonce })
|
|
108
|
+
// is silently dropped on the Node preset (the Cloudflare path wires it via
|
|
109
|
+
// router.fetch). router.nonce is undefined when unconfigured, a safe no-op.
|
|
110
|
+
nonce: router.nonce,
|
|
90
111
|
deps: {
|
|
91
112
|
renderToReadableStream,
|
|
92
113
|
decodeReply,
|
|
@@ -104,9 +125,6 @@ export default function handler(request, env) {
|
|
|
104
125
|
`.trim();
|
|
105
126
|
}
|
|
106
127
|
|
|
107
|
-
/**
|
|
108
|
-
* Virtual module IDs
|
|
109
|
-
*/
|
|
110
128
|
export const VIRTUAL_IDS = {
|
|
111
129
|
browser: "virtual:rsc-router/entry.browser.js",
|
|
112
130
|
ssr: "virtual:rsc-router/entry.ssr.js",
|
|
@@ -114,10 +132,6 @@ export const VIRTUAL_IDS = {
|
|
|
114
132
|
version: "@rangojs/router:version",
|
|
115
133
|
} as const;
|
|
116
134
|
|
|
117
|
-
/**
|
|
118
|
-
* Virtual module content for version.
|
|
119
|
-
* Exports VERSION - a timestamp that changes on server restart (dev) or at build time (production).
|
|
120
|
-
*/
|
|
121
135
|
export function getVirtualVersionContent(version: string): string {
|
|
122
136
|
return `export const VERSION = ${JSON.stringify(version)};`;
|
|
123
137
|
}
|