@rangojs/router 0.0.0-experimental.31 → 0.0.0-experimental.3232cd17
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 +4 -0
- package/README.md +198 -44
- package/dist/bin/rango.js +287 -105
- package/dist/testing/vitest.js +82 -0
- package/dist/vite/index.js +3248 -1117
- package/dist/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
- package/package.json +73 -21
- package/skills/api-client/SKILL.md +211 -0
- package/skills/breadcrumbs/SKILL.md +107 -1
- package/skills/bundle-analysis/SKILL.md +159 -0
- package/skills/cache-guide/SKILL.md +245 -21
- package/skills/caching/SKILL.md +302 -6
- package/skills/composability/SKILL.md +27 -2
- package/skills/css/SKILL.md +76 -0
- package/skills/document-cache/SKILL.md +78 -55
- package/skills/handler-use/SKILL.md +364 -0
- package/skills/hooks/SKILL.md +270 -30
- package/skills/host-router/SKILL.md +82 -22
- package/skills/i18n/SKILL.md +276 -0
- package/skills/intercept/SKILL.md +49 -5
- package/skills/layout/SKILL.md +35 -9
- package/skills/links/SKILL.md +249 -17
- package/skills/loader/SKILL.md +294 -30
- package/skills/middleware/SKILL.md +52 -13
- package/skills/migrate-nextjs/SKILL.md +584 -0
- package/skills/migrate-react-router/SKILL.md +769 -0
- package/skills/mime-routes/SKILL.md +27 -0
- package/skills/observability/SKILL.md +137 -0
- package/skills/parallel/SKILL.md +203 -7
- package/skills/prerender/SKILL.md +123 -100
- package/skills/rango/SKILL.md +250 -22
- package/skills/react-compiler/SKILL.md +168 -0
- package/skills/response-routes/SKILL.md +122 -47
- package/skills/route/SKILL.md +97 -5
- package/skills/router-setup/SKILL.md +90 -5
- 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 +129 -0
- package/skills/testing/bindings.md +89 -0
- package/skills/testing/cache-prerender.md +124 -0
- package/skills/testing/client-components.md +122 -0
- package/skills/testing/e2e-parity.md +125 -0
- package/skills/testing/flight.md +92 -0
- package/skills/testing/handles.md +129 -0
- package/skills/testing/loader.md +128 -0
- package/skills/testing/middleware.md +99 -0
- package/skills/testing/render-handler.md +121 -0
- package/skills/testing/response-routes.md +95 -0
- package/skills/testing/reverse-and-types.md +84 -0
- package/skills/testing/server-actions.md +107 -0
- package/skills/testing/server-tree.md +128 -0
- package/skills/testing/setup.md +120 -0
- package/skills/typesafety/SKILL.md +329 -27
- package/skills/use-cache/SKILL.md +36 -5
- package/skills/view-transitions/SKILL.md +294 -0
- package/src/__augment-tests__/augment.ts +81 -0
- package/src/__augment-tests__/augmented.check.ts +116 -0
- package/src/__internal.ts +67 -40
- 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/cookie-name.ts +140 -0
- package/src/browser/event-controller.ts +86 -147
- 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/link-interceptor.ts +4 -0
- package/src/browser/navigation-bridge.ts +148 -19
- package/src/browser/navigation-client.ts +187 -67
- package/src/browser/navigation-store-handle.ts +38 -0
- package/src/browser/navigation-store.ts +76 -67
- package/src/browser/navigation-transaction.ts +18 -66
- package/src/browser/partial-update.ts +123 -94
- package/src/browser/prefetch/cache.ts +214 -36
- package/src/browser/prefetch/fetch.ts +260 -38
- package/src/browser/prefetch/policy.ts +6 -0
- package/src/browser/prefetch/queue.ts +126 -20
- package/src/browser/prefetch/resource-ready.ts +77 -0
- package/src/browser/rango-state.ts +158 -76
- package/src/browser/react/Link.tsx +93 -11
- package/src/browser/react/NavigationProvider.tsx +115 -34
- package/src/browser/react/ScrollRestoration.tsx +10 -6
- package/src/browser/react/context.ts +7 -2
- package/src/browser/react/filter-segment-order.ts +49 -7
- package/src/browser/react/index.ts +0 -48
- package/src/browser/react/location-state-shared.ts +166 -8
- package/src/browser/react/location-state.ts +39 -14
- package/src/browser/react/use-action.ts +6 -15
- package/src/browser/react/use-handle.ts +23 -69
- package/src/browser/react/use-link-status.ts +0 -4
- package/src/browser/react/use-navigation.ts +22 -5
- 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 +52 -1
- package/src/browser/rsc-router.tsx +215 -76
- package/src/browser/scroll-restoration.ts +46 -39
- package/src/browser/segment-reconciler.ts +36 -9
- package/src/browser/segment-structure-assert.ts +2 -2
- package/src/browser/server-action-bridge.ts +176 -50
- package/src/browser/types.ts +95 -11
- 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 -0
- package/src/build/index.ts +8 -2
- package/src/build/prefix-tree-utils.ts +123 -0
- package/src/build/route-trie.ts +137 -32
- package/src/build/route-types/codegen.ts +4 -4
- package/src/build/route-types/include-resolution.ts +9 -2
- package/src/build/route-types/param-extraction.ts +6 -3
- package/src/build/route-types/per-module-writer.ts +7 -4
- package/src/build/route-types/router-processing.ts +278 -96
- package/src/build/route-types/scan-filter.ts +9 -2
- package/src/build/route-types/source-scan.ts +118 -0
- package/src/build/runtime-discovery.ts +9 -20
- package/src/cache/cache-error.ts +104 -0
- package/src/cache/cache-policy.ts +68 -28
- package/src/cache/cache-runtime.ts +149 -43
- package/src/cache/cache-scope.ts +148 -81
- package/src/cache/cache-tag.ts +98 -0
- package/src/cache/cf/cf-cache-store.ts +2550 -93
- package/src/cache/cf/index.ts +11 -17
- package/src/cache/document-cache.ts +78 -27
- package/src/cache/handle-snapshot.ts +63 -0
- package/src/cache/index.ts +23 -20
- package/src/cache/memory-segment-store.ts +136 -37
- package/src/cache/profile-registry.ts +6 -30
- package/src/cache/read-through-swr.ts +41 -11
- package/src/cache/segment-codec.ts +0 -16
- package/src/cache/tag-invalidation.ts +230 -0
- package/src/cache/taint.ts +55 -0
- package/src/cache/types.ts +33 -100
- package/src/cache/vercel/index.ts +11 -0
- package/src/cache/vercel/vercel-cache-store.ts +799 -0
- package/src/client.rsc.tsx +6 -21
- package/src/client.tsx +108 -290
- package/src/component-utils.ts +19 -0
- package/src/context-var.ts +84 -2
- package/src/debug.ts +2 -2
- package/src/decode-loader-results.ts +36 -0
- package/src/defer.ts +196 -0
- package/src/deps/ssr.ts +0 -1
- package/src/errors.ts +30 -4
- package/src/handle.ts +70 -22
- package/src/handles/MetaTags.tsx +0 -14
- package/src/handles/breadcrumbs.ts +16 -5
- package/src/handles/meta.ts +0 -39
- package/src/host/cookie-handler.ts +0 -36
- package/src/host/errors.ts +0 -24
- package/src/host/index.ts +8 -2
- package/src/host/pattern-matcher.ts +7 -50
- package/src/host/router.ts +107 -99
- package/src/host/testing.ts +40 -27
- package/src/host/types.ts +37 -4
- package/src/host/utils.ts +1 -1
- package/src/href-client.ts +137 -22
- package/src/index.rsc.ts +52 -26
- package/src/index.ts +100 -38
- package/src/internal-debug.ts +2 -4
- package/src/loader-store.ts +500 -0
- package/src/loader.rsc.ts +20 -13
- package/src/loader.ts +12 -11
- package/src/missing-id-error.ts +68 -0
- package/src/network-error-thrower.tsx +1 -6
- package/src/outlet-context.ts +1 -1
- package/src/outlet-provider.tsx +1 -5
- package/src/prerender/param-hash.ts +10 -11
- package/src/prerender/store.ts +37 -41
- package/src/prerender.ts +198 -82
- package/src/redirect-origin.ts +100 -0
- package/src/response-utils.ts +37 -0
- package/src/reverse.ts +65 -15
- package/src/root-error-boundary.tsx +1 -19
- package/src/route-content-wrapper.tsx +7 -72
- package/src/route-definition/dsl-helpers.ts +437 -274
- package/src/route-definition/helper-factories.ts +29 -139
- package/src/route-definition/helpers-types.ts +113 -37
- package/src/route-definition/index.ts +3 -0
- package/src/route-definition/redirect.ts +52 -10
- package/src/route-definition/resolve-handler-use.ts +161 -0
- package/src/route-definition/use-item-types.ts +32 -0
- package/src/route-map-builder.ts +7 -17
- package/src/route-types.ts +37 -41
- package/src/router/basename.ts +14 -0
- package/src/router/content-negotiation.ts +108 -9
- package/src/router/error-handling.ts +13 -17
- package/src/router/find-match.ts +45 -22
- package/src/router/handler-context.ts +83 -41
- package/src/router/intercept-resolution.ts +25 -23
- package/src/router/lazy-includes.ts +19 -53
- package/src/router/loader-resolution.ts +213 -30
- package/src/router/logging.ts +5 -8
- package/src/router/manifest.ts +49 -45
- package/src/router/match-api.ts +121 -205
- package/src/router/match-context.ts +0 -22
- package/src/router/match-handlers.ts +58 -58
- package/src/router/match-middleware/background-revalidation.ts +27 -6
- package/src/router/match-middleware/cache-lookup.ts +205 -249
- package/src/router/match-middleware/cache-store.ts +45 -32
- package/src/router/match-middleware/intercept-resolution.ts +8 -28
- package/src/router/match-middleware/segment-resolution.ts +52 -18
- package/src/router/match-pipelines.ts +1 -42
- package/src/router/match-result.ts +104 -40
- package/src/router/metrics.ts +5 -34
- package/src/router/middleware-types.ts +13 -142
- package/src/router/middleware.ts +173 -143
- package/src/router/navigation-snapshot.ts +131 -0
- package/src/router/params-util.ts +23 -0
- package/src/router/pattern-matching.ts +109 -63
- package/src/router/prerender-match.ts +192 -54
- package/src/router/preview-match.ts +32 -102
- package/src/router/request-classification.ts +276 -0
- package/src/router/revalidation.ts +63 -55
- package/src/router/route-snapshot.ts +244 -0
- package/src/router/router-context.ts +6 -28
- package/src/router/router-interfaces.ts +100 -35
- package/src/router/router-options.ts +91 -11
- package/src/router/router-registry.ts +2 -5
- package/src/router/segment-resolution/fresh.ts +242 -75
- package/src/router/segment-resolution/helpers.ts +64 -25
- package/src/router/segment-resolution/loader-cache.ts +41 -37
- package/src/router/segment-resolution/revalidation.ts +456 -372
- package/src/router/segment-resolution/static-store.ts +19 -5
- package/src/router/segment-resolution/streamed-handler-telemetry.ts +52 -0
- package/src/router/segment-resolution/view-transition-default.ts +36 -0
- package/src/router/segment-resolution.ts +4 -1
- package/src/router/segment-wrappers.ts +2 -3
- package/src/router/state-cookie-name.ts +33 -0
- package/src/router/substitute-pattern-params.ts +56 -0
- package/src/router/telemetry-otel.ts +0 -20
- package/src/router/telemetry.ts +96 -19
- package/src/router/timeout.ts +0 -20
- package/src/router/trie-matching.ts +91 -46
- package/src/router/types.ts +10 -63
- package/src/router/url-params.ts +44 -0
- package/src/router.ts +134 -43
- package/src/rsc/handler-context.ts +3 -2
- package/src/rsc/handler.ts +492 -383
- package/src/rsc/helpers.ts +162 -46
- package/src/rsc/index.ts +1 -1
- package/src/rsc/json-route-result.ts +38 -0
- package/src/rsc/loader-fetch.ts +23 -3
- package/src/rsc/manifest-init.ts +33 -42
- package/src/rsc/origin-guard.ts +39 -25
- package/src/rsc/progressive-enhancement.ts +30 -3
- package/src/rsc/redirect-guard.ts +99 -0
- package/src/rsc/response-error.ts +79 -12
- package/src/rsc/response-route-handler.ts +90 -63
- package/src/rsc/rsc-rendering.ts +56 -54
- package/src/rsc/runtime-warnings.ts +23 -10
- package/src/rsc/server-action.ts +74 -67
- package/src/rsc/ssr-setup.ts +18 -2
- package/src/rsc/types.ts +25 -6
- package/src/runtime-env.ts +18 -0
- package/src/search-params.ts +4 -20
- package/src/segment-content-promise.ts +67 -0
- package/src/segment-loader-promise.ts +134 -0
- package/src/segment-system.tsx +272 -129
- package/src/serialize.ts +243 -0
- package/src/server/context.ts +309 -61
- package/src/server/cookie-store.ts +80 -5
- package/src/server/handle-store.ts +26 -24
- package/src/server/loader-registry.ts +10 -28
- package/src/server/request-context.ts +348 -128
- package/src/ssr/index.tsx +23 -15
- 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 +618 -0
- package/src/testing/dom.entry.ts +22 -0
- package/src/testing/e2e/fixture.ts +188 -0
- package/src/testing/e2e/index.ts +128 -0
- package/src/testing/e2e/matchers.ts +35 -0
- package/src/testing/e2e/page-helpers.ts +272 -0
- package/src/testing/e2e/parity.ts +387 -0
- package/src/testing/e2e/server.ts +195 -0
- package/src/testing/flight-matchers.ts +97 -0
- package/src/testing/flight-normalize.ts +11 -0
- package/src/testing/flight-runtime.d.ts +57 -0
- package/src/testing/flight-tree.ts +682 -0
- package/src/testing/flight.entry.ts +52 -0
- package/src/testing/flight.ts +232 -0
- package/src/testing/generated-routes.ts +183 -0
- package/src/testing/index.ts +99 -0
- package/src/testing/internal/context.ts +348 -0
- package/src/testing/internal/flight-client-globals.ts +30 -0
- package/src/testing/internal/seed-vars.ts +54 -0
- package/src/testing/render-handler.ts +330 -0
- package/src/testing/render-route.tsx +566 -0
- package/src/testing/run-loader.ts +378 -0
- package/src/testing/run-middleware.ts +205 -0
- package/src/testing/vitest-stubs/cloudflare-email.ts +9 -0
- package/src/testing/vitest-stubs/cloudflare-workers.ts +21 -0
- package/src/testing/vitest-stubs/plugin-rsc.ts +16 -0
- package/src/testing/vitest-stubs/version.ts +5 -0
- package/src/testing/vitest.ts +305 -0
- package/src/theme/ThemeProvider.tsx +0 -52
- package/src/theme/ThemeScript.tsx +0 -6
- package/src/theme/constants.ts +0 -12
- package/src/theme/index.ts +0 -7
- package/src/theme/theme-context.ts +1 -5
- package/src/theme/theme-script.ts +0 -14
- package/src/theme/use-theme.ts +0 -3
- package/src/types/boundaries.ts +0 -35
- package/src/types/cache-types.ts +17 -8
- package/src/types/error-types.ts +30 -90
- package/src/types/global-namespace.ts +54 -41
- package/src/types/handler-context.ts +233 -81
- package/src/types/index.ts +1 -10
- package/src/types/loader-types.ts +44 -15
- package/src/types/request-scope.ts +107 -0
- package/src/types/route-config.ts +6 -50
- package/src/types/route-entry.ts +19 -7
- package/src/types/segments.ts +37 -14
- package/src/urls/include-helper.ts +33 -70
- package/src/urls/index.ts +1 -11
- package/src/urls/path-helper-types.ts +58 -11
- 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 -18
- 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 +192 -99
- 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 +51 -6
- package/src/vite/discovery/virtual-module-codegen.ts +14 -34
- package/src/vite/index.ts +8 -0
- package/src/vite/plugin-types.ts +187 -69
- package/src/vite/plugins/cjs-to-esm.ts +8 -18
- 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 +49 -98
- package/src/vite/plugins/expose-id-utils.ts +11 -50
- package/src/vite/plugins/expose-ids/export-analysis.ts +76 -34
- package/src/vite/plugins/expose-ids/handler-transform.ts +10 -48
- package/src/vite/plugins/expose-ids/loader-transform.ts +3 -20
- package/src/vite/plugins/expose-ids/router-transform.ts +20 -16
- package/src/vite/plugins/expose-internal-ids.ts +554 -317
- package/src/vite/plugins/performance-tracks.ts +89 -0
- package/src/vite/plugins/refresh-cmd.ts +89 -27
- package/src/vite/plugins/use-cache-transform.ts +73 -83
- package/src/vite/plugins/vercel-output.ts +258 -0
- package/src/vite/plugins/version-injector.ts +21 -25
- package/src/vite/plugins/version-plugin.ts +41 -20
- package/src/vite/plugins/virtual-entries.ts +2 -17
- package/src/vite/rango.ts +257 -289
- package/src/vite/router-discovery.ts +930 -140
- package/src/vite/utils/ast-handler-extract.ts +15 -31
- package/src/vite/utils/banner.ts +4 -4
- package/src/vite/utils/bundle-analysis.ts +10 -15
- package/src/vite/utils/client-chunks.ts +184 -0
- package/src/vite/utils/forward-user-plugins.ts +171 -0
- package/src/vite/utils/manifest-utils.ts +4 -59
- package/src/vite/utils/package-resolution.ts +20 -52
- package/src/vite/utils/prerender-utils.ts +27 -29
- package/src/vite/utils/shared-utils.ts +92 -42
- package/src/browser/action-response-classifier.ts +0 -99
- package/src/browser/react/use-client-cache.ts +0 -58
- package/src/browser/shallow.ts +0 -40
- package/src/handles/index.ts +0 -7
- package/src/router/middleware-cookies.ts +0 -55
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import type { Plugin, ResolvedConfig } from "vite";
|
|
2
|
+
import { createRangoDebugger, NS } from "../debug.js";
|
|
3
|
+
|
|
4
|
+
const debug = createRangoDebugger(NS.transform);
|
|
2
5
|
|
|
3
6
|
const CLIENT_IN_SERVER_PROXY_PREFIX =
|
|
4
7
|
"virtual:vite-rsc/client-in-server-package-proxy/";
|
|
@@ -62,6 +65,7 @@ export function extractPackageName(absolutePath: string): string | null {
|
|
|
62
65
|
*/
|
|
63
66
|
export function clientRefDedup(): Plugin {
|
|
64
67
|
let clientExclude: string[] = [];
|
|
68
|
+
const dedupedPackages = new Set<string>();
|
|
65
69
|
|
|
66
70
|
return {
|
|
67
71
|
name: "@rangojs/router:client-ref-dedup",
|
|
@@ -69,33 +73,35 @@ export function clientRefDedup(): Plugin {
|
|
|
69
73
|
apply: "serve",
|
|
70
74
|
|
|
71
75
|
configResolved(config: ResolvedConfig) {
|
|
72
|
-
// Respect user's optimizeDeps.exclude — if a package is explicitly
|
|
73
|
-
// excluded from pre-bundling, we shouldn't redirect it there.
|
|
74
76
|
const clientEnv = config.environments?.["client"];
|
|
75
77
|
clientExclude =
|
|
76
78
|
clientEnv?.optimizeDeps?.exclude ?? config.optimizeDeps?.exclude ?? [];
|
|
77
79
|
},
|
|
78
80
|
|
|
81
|
+
buildEnd() {
|
|
82
|
+
if (debug && dedupedPackages.size > 0) {
|
|
83
|
+
debug(
|
|
84
|
+
"client-ref-dedup: redirected %d package(s) (%s)",
|
|
85
|
+
dedupedPackages.size,
|
|
86
|
+
[...dedupedPackages].join(","),
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
|
|
79
91
|
resolveId(source, importer, options) {
|
|
80
|
-
// Only intercept in the client environment
|
|
81
92
|
if (this.environment?.name !== "client") return;
|
|
82
93
|
|
|
83
|
-
// Only handle imports from client-in-server-package-proxy virtual modules
|
|
84
94
|
if (!importer?.includes(CLIENT_IN_SERVER_PROXY_PREFIX)) return;
|
|
85
95
|
|
|
86
|
-
// Only handle absolute node_modules paths
|
|
87
96
|
if (!source.includes("/node_modules/")) return;
|
|
88
97
|
|
|
89
|
-
// Must have an importer
|
|
90
|
-
if (!importer) return;
|
|
91
|
-
|
|
92
98
|
const packageName = extractPackageName(source);
|
|
93
99
|
if (!packageName) return;
|
|
94
100
|
|
|
95
|
-
// Don't redirect packages that are excluded from optimization
|
|
96
101
|
if (clientExclude.includes(packageName)) return;
|
|
97
102
|
|
|
98
|
-
|
|
103
|
+
if (debug) dedupedPackages.add(packageName);
|
|
104
|
+
|
|
99
105
|
return `\0rango:dedup/${packageName}`;
|
|
100
106
|
},
|
|
101
107
|
|
|
@@ -104,7 +110,6 @@ export function clientRefDedup(): Plugin {
|
|
|
104
110
|
|
|
105
111
|
const packageName = id.slice("\0rango:dedup/".length);
|
|
106
112
|
|
|
107
|
-
// Re-export via bare specifier so Vite routes through pre-bundling
|
|
108
113
|
return [
|
|
109
114
|
`export * from ${JSON.stringify(packageName)};`,
|
|
110
115
|
`import * as __all__ from ${JSON.stringify(packageName)};`,
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import type { Plugin } from "vite";
|
|
2
2
|
import { relative } from "node:path";
|
|
3
3
|
import { createHash } from "node:crypto";
|
|
4
|
+
import { createRangoDebugger, createCounter, NS } from "../debug.js";
|
|
5
|
+
|
|
6
|
+
const debug = createRangoDebugger(NS.transform);
|
|
4
7
|
|
|
5
|
-
// Dev-mode client-reference key prefixes emitted by @vitejs/plugin-rsc
|
|
6
8
|
const CLIENT_PKG_PROXY_PREFIX =
|
|
7
9
|
"/@id/__x00__virtual:vite-rsc/client-package-proxy/";
|
|
8
10
|
const CLIENT_IN_SERVER_PKG_PROXY_PREFIX =
|
|
@@ -19,6 +21,17 @@ const FS_PREFIX = "/@fs/";
|
|
|
19
21
|
* Returns the input unchanged if it doesn't match a known dev-mode pattern
|
|
20
22
|
* (e.g., already a production hash).
|
|
21
23
|
*/
|
|
24
|
+
/**
|
|
25
|
+
* The production client-reference key hash: `sha256(relativeId).slice(0,12)`,
|
|
26
|
+
* matching @vitejs/plugin-rsc's `hashString`. Exported so the client-chunks
|
|
27
|
+
* strategy can hash a `clientChunks` callback's `meta.normalizedId` (already the
|
|
28
|
+
* project-root-relative id) and compare it against fallback hashes collected
|
|
29
|
+
* during discovery.
|
|
30
|
+
*/
|
|
31
|
+
export function hashRefKey(relativeId: string): string {
|
|
32
|
+
return createHash("sha256").update(relativeId).digest("hex").slice(0, 12);
|
|
33
|
+
}
|
|
34
|
+
|
|
22
35
|
export function computeProductionHash(
|
|
23
36
|
projectRoot: string,
|
|
24
37
|
refKey: string,
|
|
@@ -26,32 +39,24 @@ export function computeProductionHash(
|
|
|
26
39
|
let toHash: string;
|
|
27
40
|
|
|
28
41
|
if (refKey.startsWith(CLIENT_PKG_PROXY_PREFIX)) {
|
|
29
|
-
// /@id/__x00__virtual:vite-rsc/client-package-proxy/<pkg> -> hash("<pkg>")
|
|
30
42
|
toHash = refKey.slice(CLIENT_PKG_PROXY_PREFIX.length);
|
|
31
43
|
} else if (refKey.startsWith(CLIENT_IN_SERVER_PKG_PROXY_PREFIX)) {
|
|
32
|
-
// /@id/__x00__virtual:vite-rsc/client-in-server-package-proxy/<encodedAbsPath>
|
|
33
44
|
const absPath = decodeURIComponent(
|
|
34
45
|
refKey.slice(CLIENT_IN_SERVER_PKG_PROXY_PREFIX.length),
|
|
35
46
|
);
|
|
36
47
|
toHash = relative(projectRoot, absPath).replaceAll("\\", "/");
|
|
37
48
|
} else if (refKey.startsWith(FS_PREFIX)) {
|
|
38
|
-
// /@fs/abs/path.tsx -> hash(relative(root, "/abs/path.tsx"))
|
|
39
49
|
const absPath = refKey.slice(FS_PREFIX.length - 1); // keep leading /
|
|
40
50
|
toHash = relative(projectRoot, absPath).replaceAll("\\", "/");
|
|
41
51
|
} else if (refKey.startsWith("/")) {
|
|
42
|
-
// /src/Button.tsx -> hash("src/Button.tsx")
|
|
43
52
|
toHash = refKey.slice(1);
|
|
44
53
|
} else {
|
|
45
|
-
// Already hashed or unknown format — return unchanged
|
|
46
54
|
return refKey;
|
|
47
55
|
}
|
|
48
56
|
|
|
49
|
-
return
|
|
57
|
+
return hashRefKey(toHash);
|
|
50
58
|
}
|
|
51
59
|
|
|
52
|
-
// Regex to match registerClientReference() calls as emitted by @vitejs/plugin-rsc.
|
|
53
|
-
// Captures the reference key (second argument) from the call.
|
|
54
|
-
// Handles two proxy forms: parenthesized expression `(expr)` and arrow-throw `() => { ... }`.
|
|
55
60
|
const REGISTER_CLIENT_REF_RE =
|
|
56
61
|
/registerClientReference\(\s*(?:(?:\([^)]*\))|(?:\(\)[\s\S]*?\}))\s*,\s*"([^"]+)"\s*,\s*"[^"]+"\s*\)/g;
|
|
57
62
|
|
|
@@ -89,17 +94,25 @@ export function transformClientRefs(
|
|
|
89
94
|
* regex replacement of Flight payloads.
|
|
90
95
|
*/
|
|
91
96
|
export function hashClientRefs(projectRoot: string): Plugin {
|
|
97
|
+
const counter = createCounter(debug, "hash-client-refs");
|
|
92
98
|
return {
|
|
93
99
|
name: "@rangojs/router:hash-client-refs",
|
|
94
|
-
// Run after the RSC plugin's transform (default enforce is normal)
|
|
95
100
|
enforce: "post",
|
|
96
101
|
applyToEnvironment(env) {
|
|
97
102
|
return env.name === "rsc";
|
|
98
103
|
},
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
104
|
+
buildEnd() {
|
|
105
|
+
counter?.flush();
|
|
106
|
+
},
|
|
107
|
+
transform(code, id) {
|
|
108
|
+
const start = counter ? performance.now() : 0;
|
|
109
|
+
try {
|
|
110
|
+
const result = transformClientRefs(code, projectRoot);
|
|
111
|
+
if (result === null) return;
|
|
112
|
+
return { code: result, map: null };
|
|
113
|
+
} finally {
|
|
114
|
+
counter?.record(id, performance.now() - start);
|
|
115
|
+
}
|
|
103
116
|
},
|
|
104
117
|
};
|
|
105
118
|
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export interface LoaderResolveContext {
|
|
2
|
+
parentURL?: string;
|
|
3
|
+
conditions?: readonly string[];
|
|
4
|
+
importAttributes?: Record<string, string>;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export interface LoaderResolveResult {
|
|
8
|
+
shortCircuit?: boolean;
|
|
9
|
+
url: string;
|
|
10
|
+
format?: "module" | "commonjs" | "json" | "wasm" | null;
|
|
11
|
+
importAttributes?: Record<string, string>;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export type NextResolve = (
|
|
15
|
+
specifier: string,
|
|
16
|
+
context?: LoaderResolveContext,
|
|
17
|
+
) => Promise<LoaderResolveResult>;
|
|
18
|
+
|
|
19
|
+
export function resolve(
|
|
20
|
+
specifier: string,
|
|
21
|
+
context: LoaderResolveContext,
|
|
22
|
+
nextResolve: NextResolve,
|
|
23
|
+
): Promise<LoaderResolveResult>;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
// Node ESM loader hook that resolves `cloudflare:*` imports to the same
|
|
2
|
+
// stub ESM the Vite transform produces for rewritten specifiers.
|
|
3
|
+
//
|
|
4
|
+
// Why both? The Vite transform (cloudflare-protocol-stub.ts) catches
|
|
5
|
+
// imports in modules that flow through Vite's plugin pipeline — covers
|
|
6
|
+
// user source and any node_modules package Vite fetches and transforms.
|
|
7
|
+
// But Vite/Rollup externalize certain packages (e.g. `partyserver`,
|
|
8
|
+
// which has `import { DurableObject, env } from "cloudflare:workers"`
|
|
9
|
+
// at its top level, and similar "workerd-native" libraries). Externalized
|
|
10
|
+
// modules bypass the transform: Rollup hands their resolution to Node's
|
|
11
|
+
// native ESM loader, which rejects URL-scheme specifiers. This loader
|
|
12
|
+
// hook registers via `module.register()` from `createTempRscServer` and
|
|
13
|
+
// intercepts `cloudflare:*` at Node's resolve layer — before the default
|
|
14
|
+
// loader throws ERR_UNSUPPORTED_ESM_URL_SCHEME.
|
|
15
|
+
//
|
|
16
|
+
// Lifecycle: the hook runs in a dedicated worker thread (Node ESM loader
|
|
17
|
+
// architecture) with its own globalThis. It cannot see the main thread's
|
|
18
|
+
// `__rango_build_env__` bridge, so the `env` export here is always `{}`.
|
|
19
|
+
// That's fine in practice — externalized libraries don't typically touch
|
|
20
|
+
// `env` at module top level; they read it at request time in workerd
|
|
21
|
+
// where the real module exists. Build-time prerender handlers in user
|
|
22
|
+
// source DO read `env`, but they flow through the Vite transform (which
|
|
23
|
+
// does bridge `env` from `getPlatformProxy()`), not through this loader.
|
|
24
|
+
//
|
|
25
|
+
// Keep STUBS in sync with cloudflare-protocol-stub.ts — both paths need
|
|
26
|
+
// to hand out the same base classes.
|
|
27
|
+
|
|
28
|
+
const CF_PREFIX = "cloudflare:";
|
|
29
|
+
|
|
30
|
+
const STUBS = {
|
|
31
|
+
"cloudflare:workers": `
|
|
32
|
+
export class DurableObject { constructor(_ctx, _env) {} }
|
|
33
|
+
export class WorkerEntrypoint { constructor(_ctx, _env) {} }
|
|
34
|
+
export class WorkflowEntrypoint { constructor(_ctx, _env) {} }
|
|
35
|
+
export class RpcTarget {}
|
|
36
|
+
export const env = {};
|
|
37
|
+
export default {};
|
|
38
|
+
`,
|
|
39
|
+
"cloudflare:email": `
|
|
40
|
+
export class EmailMessage { constructor(_from, _to, _raw) {} }
|
|
41
|
+
export default {};
|
|
42
|
+
`,
|
|
43
|
+
"cloudflare:sockets": `
|
|
44
|
+
export function connect() { return {}; }
|
|
45
|
+
export default {};
|
|
46
|
+
`,
|
|
47
|
+
"cloudflare:workflows": `
|
|
48
|
+
export class NonRetryableError extends Error {
|
|
49
|
+
constructor(message, name) { super(message); this.name = name ?? "NonRetryableError"; }
|
|
50
|
+
}
|
|
51
|
+
export default {};
|
|
52
|
+
`,
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
// Policy: unknown `cloudflare:*` specifiers resolve permissively to an
|
|
56
|
+
// empty default export rather than throwing. Same reasoning as
|
|
57
|
+
// cloudflare-protocol-stub.ts's FALLBACK_STUB — we prioritize
|
|
58
|
+
// dependency-graph resilience over strict validation, because third-party
|
|
59
|
+
// packages can pull `cloudflare:*` modules we haven't curated.
|
|
60
|
+
const FALLBACK_STUB = `export default {};\n`;
|
|
61
|
+
|
|
62
|
+
function dataUrlFor(specifier) {
|
|
63
|
+
const body = STUBS[specifier] ?? FALLBACK_STUB;
|
|
64
|
+
return "data:text/javascript;base64," + Buffer.from(body).toString("base64");
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export async function resolve(specifier, context, nextResolve) {
|
|
68
|
+
if (specifier.startsWith(CF_PREFIX)) {
|
|
69
|
+
return {
|
|
70
|
+
shortCircuit: true,
|
|
71
|
+
url: dataUrlFor(specifier),
|
|
72
|
+
format: "module",
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
return nextResolve(specifier, context);
|
|
76
|
+
}
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import type { Plugin } from "vite";
|
|
2
|
+
|
|
3
|
+
const VIRTUAL_PREFIX = "virtual:rango-cloudflare-stub-";
|
|
4
|
+
const NULL_PREFIX = "\0" + VIRTUAL_PREFIX;
|
|
5
|
+
const CF_PREFIX = "cloudflare:";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* `globalThis` key the `cloudflare:workers` stub reads to populate its
|
|
9
|
+
* `env` export. Router discovery sets this to the resolved `buildEnv`
|
|
10
|
+
* proxy (from `wrangler.getPlatformProxy()` when `buildEnv: "auto"` is
|
|
11
|
+
* configured, or a user-supplied object otherwise) before importing the
|
|
12
|
+
* worker entry, and clears it after discovery disposes the proxy. When
|
|
13
|
+
* unset, the stub's `env` falls back to `{}`.
|
|
14
|
+
*
|
|
15
|
+
* Using `globalThis` is the only cross-module bridge that works here:
|
|
16
|
+
* the stub's `load` hook returns source text, not a live closure, but
|
|
17
|
+
* the stub module is evaluated in the same Node process as the
|
|
18
|
+
* discovery plugin — so reading a global at module-evaluation time
|
|
19
|
+
* reaches whatever the plugin assigned there. A symbol key would be
|
|
20
|
+
* cleaner in-process but awkward to name from the stub source.
|
|
21
|
+
*
|
|
22
|
+
* @internal
|
|
23
|
+
*/
|
|
24
|
+
export const BUILD_ENV_GLOBAL_KEY = "__rango_build_env__";
|
|
25
|
+
|
|
26
|
+
const SOURCE_EXT_RE = /\.[mc]?[jt]sx?$/;
|
|
27
|
+
|
|
28
|
+
const IMPORT_NODE_TYPES = new Set([
|
|
29
|
+
"ImportDeclaration",
|
|
30
|
+
"ImportExpression",
|
|
31
|
+
"ExportNamedDeclaration",
|
|
32
|
+
"ExportAllDeclaration",
|
|
33
|
+
]);
|
|
34
|
+
|
|
35
|
+
const STUBS: Record<string, string> = {
|
|
36
|
+
"cloudflare:workers": `
|
|
37
|
+
export class DurableObject { constructor(_ctx, _env) {} }
|
|
38
|
+
export class WorkerEntrypoint { constructor(_ctx, _env) {} }
|
|
39
|
+
export class WorkflowEntrypoint { constructor(_ctx, _env) {} }
|
|
40
|
+
export class RpcTarget {}
|
|
41
|
+
export const env = globalThis[${JSON.stringify(BUILD_ENV_GLOBAL_KEY)}] ?? {};
|
|
42
|
+
export default {};
|
|
43
|
+
`,
|
|
44
|
+
"cloudflare:email": `
|
|
45
|
+
export class EmailMessage { constructor(_from, _to, _raw) {} }
|
|
46
|
+
export default {};
|
|
47
|
+
`,
|
|
48
|
+
"cloudflare:sockets": `
|
|
49
|
+
export function connect() { return {}; }
|
|
50
|
+
export default {};
|
|
51
|
+
`,
|
|
52
|
+
"cloudflare:workflows": `
|
|
53
|
+
export class NonRetryableError extends Error {
|
|
54
|
+
constructor(message, name) { super(message); this.name = name ?? "NonRetryableError"; }
|
|
55
|
+
}
|
|
56
|
+
export default {};
|
|
57
|
+
`,
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const FALLBACK_STUB = `export default {};\n`;
|
|
61
|
+
|
|
62
|
+
interface AstNode {
|
|
63
|
+
type: string;
|
|
64
|
+
start?: number;
|
|
65
|
+
end?: number;
|
|
66
|
+
source?: AstNode | null;
|
|
67
|
+
value?: unknown;
|
|
68
|
+
[key: string]: unknown;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Stubs `cloudflare:*` imports for the discovery-time Node Vite server.
|
|
73
|
+
*
|
|
74
|
+
* Discovery only evaluates user module top-level code — it never invokes
|
|
75
|
+
* DurableObject / WorkerEntrypoint / Workflow handlers — so empty base
|
|
76
|
+
* classes are enough for `class X extends DurableObject {}` declarations
|
|
77
|
+
* to load in Node, where `cloudflare:*` is otherwise unresolvable.
|
|
78
|
+
*
|
|
79
|
+
* Interception point: a transform hook parses source with Rollup's
|
|
80
|
+
* plugin-context parser (`this.parse`) and rewrites only real import
|
|
81
|
+
* specifier spans (`import ... from "cloudflare:xxx"`,
|
|
82
|
+
* `import("cloudflare:xxx")`, `export ... from "cloudflare:xxx"`) to a
|
|
83
|
+
* plain virtual module name (`virtual:rango-cloudflare-stub-xxx`).
|
|
84
|
+
* This must be done in transform because Vite's module runner routes
|
|
85
|
+
* URL-scheme specifiers straight to Node's native ESM loader without
|
|
86
|
+
* consulting plugin `resolveId` hooks. Using the AST (instead of a
|
|
87
|
+
* text regex or a permissive lexer) guarantees that strings,
|
|
88
|
+
* comments, and template literals that merely contain import-like
|
|
89
|
+
* text are never mutated — the walker only looks at the four import
|
|
90
|
+
* node types.
|
|
91
|
+
*
|
|
92
|
+
* The transform runs on user source AND on compiled node_modules
|
|
93
|
+
* output: real-world CF packages (e.g. the Cloudflare Agents SDK)
|
|
94
|
+
* ship compiled JS that contains `import ... from "cloudflare:email"`
|
|
95
|
+
* and similar, so excluding node_modules would leave those imports
|
|
96
|
+
* unrewritten. Cost is small because the early exit (`code.includes`)
|
|
97
|
+
* skips files with no cloudflare: mention.
|
|
98
|
+
*
|
|
99
|
+
* The plugin intentionally runs at Vite's default ordering (no
|
|
100
|
+
* `enforce: "pre"`) so TS/JSX has already been compiled to plain JS
|
|
101
|
+
* by the time `this.parse` runs — acorn doesn't understand
|
|
102
|
+
* non-standard syntax.
|
|
103
|
+
*
|
|
104
|
+
* `cloudflare:workers`, `cloudflare:email`, `cloudflare:sockets`, and
|
|
105
|
+
* `cloudflare:workflows` each get curated stubs with the well-known
|
|
106
|
+
* symbols that appear in top-level `extends` positions. Any other
|
|
107
|
+
* `cloudflare:*` specifier falls back to an empty default export —
|
|
108
|
+
* discovery never executes the handlers, so an empty module is safe
|
|
109
|
+
* for anything the graph pulls in transitively.
|
|
110
|
+
*
|
|
111
|
+
* Only registered in the discovery temp server, not the user's runtime
|
|
112
|
+
* config.
|
|
113
|
+
* @internal
|
|
114
|
+
*/
|
|
115
|
+
export function createCloudflareProtocolStubPlugin(): Plugin {
|
|
116
|
+
return {
|
|
117
|
+
name: "@rangojs/router:cloudflare-protocol-stub",
|
|
118
|
+
transform(code, id) {
|
|
119
|
+
const cleanId = id.split("?")[0] ?? id;
|
|
120
|
+
if (!SOURCE_EXT_RE.test(cleanId)) return null;
|
|
121
|
+
if (!code.includes(CF_PREFIX)) return null;
|
|
122
|
+
|
|
123
|
+
let ast: AstNode;
|
|
124
|
+
try {
|
|
125
|
+
ast = this.parse(code, { lang: "tsx" }) as unknown as AstNode;
|
|
126
|
+
} catch {
|
|
127
|
+
// Malformed source — let a downstream plugin surface the parse error.
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const hits: Array<{ start: number; end: number; value: string }> = [];
|
|
132
|
+
walk(ast, (node) => {
|
|
133
|
+
if (!IMPORT_NODE_TYPES.has(node.type)) return;
|
|
134
|
+
const source = node.source;
|
|
135
|
+
if (!source || source.type !== "Literal") return;
|
|
136
|
+
if (typeof source.value !== "string") return;
|
|
137
|
+
if (!source.value.startsWith(CF_PREFIX)) return;
|
|
138
|
+
if (typeof source.start !== "number" || typeof source.end !== "number")
|
|
139
|
+
return;
|
|
140
|
+
hits.push({
|
|
141
|
+
start: source.start,
|
|
142
|
+
end: source.end,
|
|
143
|
+
value: source.value,
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
if (hits.length === 0) return null;
|
|
148
|
+
|
|
149
|
+
hits.sort((a, b) => b.start - a.start);
|
|
150
|
+
let out = code;
|
|
151
|
+
for (const hit of hits) {
|
|
152
|
+
const submodule = hit.value.slice(CF_PREFIX.length);
|
|
153
|
+
const quote = code[hit.start] === "'" ? "'" : '"';
|
|
154
|
+
out =
|
|
155
|
+
out.slice(0, hit.start) +
|
|
156
|
+
quote +
|
|
157
|
+
VIRTUAL_PREFIX +
|
|
158
|
+
submodule +
|
|
159
|
+
quote +
|
|
160
|
+
out.slice(hit.end);
|
|
161
|
+
}
|
|
162
|
+
return { code: out, map: null };
|
|
163
|
+
},
|
|
164
|
+
resolveId(id) {
|
|
165
|
+
if (id.startsWith(VIRTUAL_PREFIX)) {
|
|
166
|
+
return "\0" + id;
|
|
167
|
+
}
|
|
168
|
+
return null;
|
|
169
|
+
},
|
|
170
|
+
load(id) {
|
|
171
|
+
if (!id.startsWith(NULL_PREFIX)) return null;
|
|
172
|
+
const submodule = id.slice(NULL_PREFIX.length);
|
|
173
|
+
const specifier = CF_PREFIX + submodule;
|
|
174
|
+
return STUBS[specifier] ?? FALLBACK_STUB;
|
|
175
|
+
},
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function walk(node: unknown, visit: (n: AstNode) => void): void {
|
|
180
|
+
if (!node || typeof node !== "object") return;
|
|
181
|
+
if (Array.isArray(node)) {
|
|
182
|
+
for (const child of node) walk(child, visit);
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
const n = node as AstNode;
|
|
186
|
+
if (typeof n.type !== "string") return;
|
|
187
|
+
visit(n);
|
|
188
|
+
for (const key in n) {
|
|
189
|
+
if (key === "loc" || key === "start" || key === "end" || key === "range") {
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
walk(n[key], visit);
|
|
193
|
+
}
|
|
194
|
+
}
|