@rangojs/router 0.0.0-experimental.7 → 0.0.0-experimental.70

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.
Files changed (307) hide show
  1. package/AGENTS.md +9 -0
  2. package/README.md +942 -4
  3. package/dist/bin/rango.js +1689 -0
  4. package/dist/vite/index.js +4951 -930
  5. package/package.json +70 -60
  6. package/skills/breadcrumbs/SKILL.md +250 -0
  7. package/skills/cache-guide/SKILL.md +294 -0
  8. package/skills/caching/SKILL.md +93 -23
  9. package/skills/composability/SKILL.md +172 -0
  10. package/skills/debug-manifest/SKILL.md +12 -8
  11. package/skills/document-cache/SKILL.md +18 -16
  12. package/skills/fonts/SKILL.md +167 -0
  13. package/skills/hooks/SKILL.md +334 -72
  14. package/skills/host-router/SKILL.md +218 -0
  15. package/skills/intercept/SKILL.md +131 -8
  16. package/skills/layout/SKILL.md +100 -3
  17. package/skills/links/SKILL.md +92 -31
  18. package/skills/loader/SKILL.md +404 -44
  19. package/skills/middleware/SKILL.md +173 -34
  20. package/skills/mime-routes/SKILL.md +128 -0
  21. package/skills/parallel/SKILL.md +204 -1
  22. package/skills/prerender/SKILL.md +685 -0
  23. package/skills/rango/SKILL.md +85 -16
  24. package/skills/response-routes/SKILL.md +411 -0
  25. package/skills/route/SKILL.md +257 -14
  26. package/skills/router-setup/SKILL.md +210 -32
  27. package/skills/tailwind/SKILL.md +129 -0
  28. package/skills/theme/SKILL.md +9 -8
  29. package/skills/typesafety/SKILL.md +328 -89
  30. package/skills/use-cache/SKILL.md +324 -0
  31. package/src/__internal.ts +102 -4
  32. package/src/bin/rango.ts +321 -0
  33. package/src/browser/action-coordinator.ts +97 -0
  34. package/src/browser/action-response-classifier.ts +99 -0
  35. package/src/browser/app-version.ts +14 -0
  36. package/src/browser/event-controller.ts +92 -64
  37. package/src/browser/history-state.ts +80 -0
  38. package/src/browser/intercept-utils.ts +52 -0
  39. package/src/browser/link-interceptor.ts +24 -4
  40. package/src/browser/logging.ts +55 -0
  41. package/src/browser/merge-segment-loaders.ts +20 -12
  42. package/src/browser/navigation-bridge.ts +296 -558
  43. package/src/browser/navigation-client.ts +179 -69
  44. package/src/browser/navigation-store.ts +73 -55
  45. package/src/browser/navigation-transaction.ts +297 -0
  46. package/src/browser/network-error-handler.ts +61 -0
  47. package/src/browser/partial-update.ts +328 -313
  48. package/src/browser/prefetch/cache.ts +206 -0
  49. package/src/browser/prefetch/fetch.ts +150 -0
  50. package/src/browser/prefetch/observer.ts +65 -0
  51. package/src/browser/prefetch/policy.ts +48 -0
  52. package/src/browser/prefetch/queue.ts +160 -0
  53. package/src/browser/prefetch/resource-ready.ts +77 -0
  54. package/src/browser/rango-state.ts +112 -0
  55. package/src/browser/react/Link.tsx +230 -74
  56. package/src/browser/react/NavigationProvider.tsx +87 -11
  57. package/src/browser/react/context.ts +11 -0
  58. package/src/browser/react/filter-segment-order.ts +11 -0
  59. package/src/browser/react/index.ts +12 -12
  60. package/src/browser/react/location-state-shared.ts +95 -53
  61. package/src/browser/react/location-state.ts +60 -15
  62. package/src/browser/react/mount-context.ts +6 -1
  63. package/src/browser/react/nonce-context.ts +23 -0
  64. package/src/browser/react/shallow-equal.ts +27 -0
  65. package/src/browser/react/use-action.ts +29 -51
  66. package/src/browser/react/use-client-cache.ts +5 -3
  67. package/src/browser/react/use-handle.ts +30 -126
  68. package/src/browser/react/use-href.tsx +2 -2
  69. package/src/browser/react/use-link-status.ts +6 -5
  70. package/src/browser/react/use-navigation.ts +22 -63
  71. package/src/browser/react/use-params.ts +65 -0
  72. package/src/browser/react/use-pathname.ts +47 -0
  73. package/src/browser/react/use-router.ts +76 -0
  74. package/src/browser/react/use-search-params.ts +56 -0
  75. package/src/browser/react/use-segments.ts +80 -97
  76. package/src/browser/response-adapter.ts +73 -0
  77. package/src/browser/rsc-router.tsx +214 -58
  78. package/src/browser/scroll-restoration.ts +127 -52
  79. package/src/browser/segment-reconciler.ts +221 -0
  80. package/src/browser/segment-structure-assert.ts +16 -0
  81. package/src/browser/server-action-bridge.ts +510 -603
  82. package/src/browser/shallow.ts +6 -1
  83. package/src/browser/types.ts +141 -48
  84. package/src/browser/validate-redirect-origin.ts +29 -0
  85. package/src/build/generate-manifest.ts +235 -24
  86. package/src/build/generate-route-types.ts +39 -0
  87. package/src/build/index.ts +13 -0
  88. package/src/build/route-trie.ts +265 -0
  89. package/src/build/route-types/ast-helpers.ts +25 -0
  90. package/src/build/route-types/ast-route-extraction.ts +98 -0
  91. package/src/build/route-types/codegen.ts +102 -0
  92. package/src/build/route-types/include-resolution.ts +418 -0
  93. package/src/build/route-types/param-extraction.ts +48 -0
  94. package/src/build/route-types/per-module-writer.ts +128 -0
  95. package/src/build/route-types/router-processing.ts +618 -0
  96. package/src/build/route-types/scan-filter.ts +85 -0
  97. package/src/build/runtime-discovery.ts +231 -0
  98. package/src/cache/background-task.ts +34 -0
  99. package/src/cache/cache-key-utils.ts +44 -0
  100. package/src/cache/cache-policy.ts +125 -0
  101. package/src/cache/cache-runtime.ts +342 -0
  102. package/src/cache/cache-scope.ts +167 -309
  103. package/src/cache/cf/cf-cache-store.ts +571 -17
  104. package/src/cache/cf/index.ts +13 -3
  105. package/src/cache/document-cache.ts +116 -77
  106. package/src/cache/handle-capture.ts +81 -0
  107. package/src/cache/handle-snapshot.ts +41 -0
  108. package/src/cache/index.ts +1 -15
  109. package/src/cache/memory-segment-store.ts +191 -13
  110. package/src/cache/profile-registry.ts +73 -0
  111. package/src/cache/read-through-swr.ts +134 -0
  112. package/src/cache/segment-codec.ts +256 -0
  113. package/src/cache/taint.ts +153 -0
  114. package/src/cache/types.ts +72 -122
  115. package/src/client.rsc.tsx +3 -1
  116. package/src/client.tsx +105 -179
  117. package/src/component-utils.ts +4 -4
  118. package/src/components/DefaultDocument.tsx +5 -1
  119. package/src/context-var.ts +156 -0
  120. package/src/debug.ts +19 -9
  121. package/src/errors.ts +108 -2
  122. package/src/handle.ts +55 -29
  123. package/src/handles/MetaTags.tsx +73 -20
  124. package/src/handles/breadcrumbs.ts +66 -0
  125. package/src/handles/index.ts +1 -0
  126. package/src/handles/meta.ts +30 -13
  127. package/src/host/cookie-handler.ts +21 -15
  128. package/src/host/errors.ts +8 -8
  129. package/src/host/index.ts +4 -7
  130. package/src/host/pattern-matcher.ts +27 -27
  131. package/src/host/router.ts +61 -39
  132. package/src/host/testing.ts +8 -8
  133. package/src/host/types.ts +15 -7
  134. package/src/host/utils.ts +1 -1
  135. package/src/href-client.ts +119 -29
  136. package/src/index.rsc.ts +155 -19
  137. package/src/index.ts +223 -30
  138. package/src/internal-debug.ts +11 -0
  139. package/src/loader.rsc.ts +26 -157
  140. package/src/loader.ts +27 -10
  141. package/src/network-error-thrower.tsx +3 -1
  142. package/src/outlet-provider.tsx +45 -0
  143. package/src/prerender/param-hash.ts +37 -0
  144. package/src/prerender/store.ts +186 -0
  145. package/src/prerender.ts +524 -0
  146. package/src/reverse.ts +351 -0
  147. package/src/root-error-boundary.tsx +41 -29
  148. package/src/route-content-wrapper.tsx +7 -4
  149. package/src/route-definition/dsl-helpers.ts +982 -0
  150. package/src/route-definition/helper-factories.ts +200 -0
  151. package/src/route-definition/helpers-types.ts +434 -0
  152. package/src/route-definition/index.ts +55 -0
  153. package/src/route-definition/redirect.ts +101 -0
  154. package/src/route-definition/resolve-handler-use.ts +149 -0
  155. package/src/route-definition.ts +1 -1428
  156. package/src/route-map-builder.ts +217 -123
  157. package/src/route-name.ts +53 -0
  158. package/src/route-types.ts +70 -8
  159. package/src/router/content-negotiation.ts +215 -0
  160. package/src/router/debug-manifest.ts +72 -0
  161. package/src/router/error-handling.ts +9 -9
  162. package/src/router/find-match.ts +160 -0
  163. package/src/router/handler-context.ts +435 -86
  164. package/src/router/intercept-resolution.ts +402 -0
  165. package/src/router/lazy-includes.ts +237 -0
  166. package/src/router/loader-resolution.ts +356 -128
  167. package/src/router/logging.ts +251 -0
  168. package/src/router/manifest.ts +154 -35
  169. package/src/router/match-api.ts +555 -0
  170. package/src/router/match-context.ts +5 -3
  171. package/src/router/match-handlers.ts +440 -0
  172. package/src/router/match-middleware/background-revalidation.ts +108 -93
  173. package/src/router/match-middleware/cache-lookup.ts +459 -10
  174. package/src/router/match-middleware/cache-store.ts +98 -26
  175. package/src/router/match-middleware/intercept-resolution.ts +57 -17
  176. package/src/router/match-middleware/segment-resolution.ts +80 -6
  177. package/src/router/match-pipelines.ts +10 -45
  178. package/src/router/match-result.ts +55 -33
  179. package/src/router/metrics.ts +240 -15
  180. package/src/router/middleware-cookies.ts +55 -0
  181. package/src/router/middleware-types.ts +220 -0
  182. package/src/router/middleware.ts +324 -369
  183. package/src/router/navigation-snapshot.ts +182 -0
  184. package/src/router/pattern-matching.ts +211 -43
  185. package/src/router/prerender-match.ts +502 -0
  186. package/src/router/preview-match.ts +98 -0
  187. package/src/router/request-classification.ts +310 -0
  188. package/src/router/revalidation.ts +137 -38
  189. package/src/router/route-snapshot.ts +245 -0
  190. package/src/router/router-context.ts +41 -21
  191. package/src/router/router-interfaces.ts +484 -0
  192. package/src/router/router-options.ts +618 -0
  193. package/src/router/router-registry.ts +24 -0
  194. package/src/router/segment-resolution/fresh.ts +743 -0
  195. package/src/router/segment-resolution/helpers.ts +268 -0
  196. package/src/router/segment-resolution/loader-cache.ts +199 -0
  197. package/src/router/segment-resolution/revalidation.ts +1373 -0
  198. package/src/router/segment-resolution/static-store.ts +67 -0
  199. package/src/router/segment-resolution.ts +21 -0
  200. package/src/router/segment-wrappers.ts +291 -0
  201. package/src/router/telemetry-otel.ts +299 -0
  202. package/src/router/telemetry.ts +300 -0
  203. package/src/router/timeout.ts +148 -0
  204. package/src/router/trie-matching.ts +239 -0
  205. package/src/router/types.ts +78 -3
  206. package/src/router.ts +740 -4252
  207. package/src/rsc/handler-context.ts +45 -0
  208. package/src/rsc/handler.ts +907 -797
  209. package/src/rsc/helpers.ts +140 -6
  210. package/src/rsc/index.ts +0 -20
  211. package/src/rsc/loader-fetch.ts +229 -0
  212. package/src/rsc/manifest-init.ts +90 -0
  213. package/src/rsc/nonce.ts +14 -0
  214. package/src/rsc/origin-guard.ts +141 -0
  215. package/src/rsc/progressive-enhancement.ts +391 -0
  216. package/src/rsc/response-error.ts +37 -0
  217. package/src/rsc/response-route-handler.ts +347 -0
  218. package/src/rsc/rsc-rendering.ts +246 -0
  219. package/src/rsc/runtime-warnings.ts +42 -0
  220. package/src/rsc/server-action.ts +356 -0
  221. package/src/rsc/ssr-setup.ts +128 -0
  222. package/src/rsc/types.ts +46 -11
  223. package/src/search-params.ts +230 -0
  224. package/src/segment-system.tsx +165 -17
  225. package/src/server/context.ts +315 -58
  226. package/src/server/cookie-store.ts +190 -0
  227. package/src/server/fetchable-loader-store.ts +37 -0
  228. package/src/server/handle-store.ts +113 -15
  229. package/src/server/loader-registry.ts +24 -64
  230. package/src/server/request-context.ts +607 -81
  231. package/src/server.ts +35 -130
  232. package/src/ssr/index.tsx +103 -30
  233. package/src/static-handler.ts +126 -0
  234. package/src/theme/ThemeProvider.tsx +21 -15
  235. package/src/theme/ThemeScript.tsx +5 -5
  236. package/src/theme/constants.ts +5 -2
  237. package/src/theme/index.ts +4 -14
  238. package/src/theme/theme-context.ts +4 -30
  239. package/src/theme/theme-script.ts +21 -18
  240. package/src/types/boundaries.ts +158 -0
  241. package/src/types/cache-types.ts +198 -0
  242. package/src/types/error-types.ts +192 -0
  243. package/src/types/global-namespace.ts +100 -0
  244. package/src/types/handler-context.ts +791 -0
  245. package/src/types/index.ts +88 -0
  246. package/src/types/loader-types.ts +210 -0
  247. package/src/types/route-config.ts +170 -0
  248. package/src/types/route-entry.ts +109 -0
  249. package/src/types/segments.ts +150 -0
  250. package/src/types.ts +1 -1623
  251. package/src/urls/include-helper.ts +197 -0
  252. package/src/urls/index.ts +53 -0
  253. package/src/urls/path-helper-types.ts +346 -0
  254. package/src/urls/path-helper.ts +364 -0
  255. package/src/urls/pattern-types.ts +107 -0
  256. package/src/urls/response-types.ts +116 -0
  257. package/src/urls/type-extraction.ts +372 -0
  258. package/src/urls/urls-function.ts +98 -0
  259. package/src/urls.ts +1 -802
  260. package/src/use-loader.tsx +161 -81
  261. package/src/vite/discovery/bundle-postprocess.ts +181 -0
  262. package/src/vite/discovery/discover-routers.ts +348 -0
  263. package/src/vite/discovery/prerender-collection.ts +439 -0
  264. package/src/vite/discovery/route-types-writer.ts +258 -0
  265. package/src/vite/discovery/self-gen-tracking.ts +47 -0
  266. package/src/vite/discovery/state.ts +117 -0
  267. package/src/vite/discovery/virtual-module-codegen.ts +203 -0
  268. package/src/vite/index.ts +15 -1129
  269. package/src/vite/plugin-types.ts +103 -0
  270. package/src/vite/plugins/cjs-to-esm.ts +93 -0
  271. package/src/vite/plugins/client-ref-dedup.ts +115 -0
  272. package/src/vite/plugins/client-ref-hashing.ts +105 -0
  273. package/src/vite/{expose-action-id.ts → plugins/expose-action-id.ts} +72 -53
  274. package/src/vite/plugins/expose-id-utils.ts +299 -0
  275. package/src/vite/plugins/expose-ids/export-analysis.ts +296 -0
  276. package/src/vite/plugins/expose-ids/handler-transform.ts +209 -0
  277. package/src/vite/plugins/expose-ids/loader-transform.ts +74 -0
  278. package/src/vite/plugins/expose-ids/router-transform.ts +110 -0
  279. package/src/vite/plugins/expose-ids/types.ts +45 -0
  280. package/src/vite/plugins/expose-internal-ids.ts +786 -0
  281. package/src/vite/plugins/performance-tracks.ts +88 -0
  282. package/src/vite/plugins/refresh-cmd.ts +127 -0
  283. package/src/vite/plugins/use-cache-transform.ts +323 -0
  284. package/src/vite/plugins/version-injector.ts +83 -0
  285. package/src/vite/plugins/version-plugin.ts +266 -0
  286. package/src/vite/{virtual-entries.ts → plugins/virtual-entries.ts} +23 -14
  287. package/src/vite/plugins/virtual-stub-plugin.ts +29 -0
  288. package/src/vite/rango.ts +462 -0
  289. package/src/vite/router-discovery.ts +918 -0
  290. package/src/vite/utils/ast-handler-extract.ts +517 -0
  291. package/src/vite/utils/banner.ts +36 -0
  292. package/src/vite/utils/bundle-analysis.ts +137 -0
  293. package/src/vite/utils/manifest-utils.ts +70 -0
  294. package/src/vite/{package-resolution.ts → utils/package-resolution.ts} +25 -29
  295. package/src/vite/utils/prerender-utils.ts +207 -0
  296. package/src/vite/utils/shared-utils.ts +170 -0
  297. package/CLAUDE.md +0 -43
  298. package/src/browser/lru-cache.ts +0 -69
  299. package/src/browser/request-controller.ts +0 -164
  300. package/src/cache/memory-store.ts +0 -253
  301. package/src/href-context.ts +0 -33
  302. package/src/href.ts +0 -255
  303. package/src/server/route-manifest-cache.ts +0 -173
  304. package/src/vite/expose-handle-id.ts +0 -209
  305. package/src/vite/expose-loader-id.ts +0 -426
  306. package/src/vite/expose-location-state-id.ts +0 -177
  307. /package/src/vite/{version.d.ts → plugins/version.d.ts} +0 -0
@@ -0,0 +1,462 @@
1
+ import type { PluginOption } from "vite";
2
+ import { readFileSync } from "node:fs";
3
+ import { resolve } from "node:path";
4
+ import { exposeActionId } from "./plugins/expose-action-id.js";
5
+ import {
6
+ exposeInternalIds,
7
+ exposeRouterId,
8
+ } from "./plugins/expose-internal-ids.js";
9
+ import { useCacheTransform } from "./plugins/use-cache-transform.js";
10
+ import { clientRefDedup } from "./plugins/client-ref-dedup.js";
11
+ import { VIRTUAL_IDS } from "./plugins/virtual-entries.js";
12
+ import {
13
+ getExcludeDeps,
14
+ getPackageAliases,
15
+ } from "./utils/package-resolution.js";
16
+ import { findRouterFiles } from "../build/generate-route-types.js";
17
+ import { createVersionPlugin } from "./plugins/version-plugin.js";
18
+ import {
19
+ sharedEsbuildOptions,
20
+ createVirtualEntriesPlugin,
21
+ onwarn,
22
+ getManualChunks,
23
+ } from "./utils/shared-utils.js";
24
+ import type { RangoOptions } from "./plugin-types.js";
25
+ import { printBanner, rangoVersion } from "./utils/banner.js";
26
+ import { createVersionInjectorPlugin } from "./plugins/version-injector.js";
27
+ import { createCjsToEsmPlugin } from "./plugins/cjs-to-esm.js";
28
+ import { createRouterDiscoveryPlugin } from "./router-discovery.js";
29
+ import { performanceTracksPlugin } from "./plugins/performance-tracks.js";
30
+
31
+ /**
32
+ * Vite plugin for @rangojs/router.
33
+ *
34
+ * Includes @vitejs/plugin-rsc and all necessary transforms for the router
35
+ * to function correctly with React Server Components.
36
+ *
37
+ * @example Node.js (default)
38
+ * ```ts
39
+ * export default defineConfig({
40
+ * plugins: [react(), rango()],
41
+ * });
42
+ * ```
43
+ *
44
+ * @example Cloudflare Workers
45
+ * ```ts
46
+ * export default defineConfig({
47
+ * plugins: [
48
+ * react(),
49
+ * rango({ preset: 'cloudflare' }),
50
+ * cloudflare({ viteEnvironment: { name: 'rsc' } }),
51
+ * ],
52
+ * });
53
+ * ```
54
+ */
55
+ export async function rango(options?: RangoOptions): Promise<PluginOption[]> {
56
+ const resolvedOptions: RangoOptions = options ?? { preset: "node" };
57
+ const preset = resolvedOptions.preset ?? "node";
58
+ const showBanner = resolvedOptions.banner ?? true;
59
+
60
+ const plugins: PluginOption[] = [];
61
+
62
+ // Get package resolution info (workspace vs npm install)
63
+ const rangoAliases = getPackageAliases();
64
+ const excludeDeps = [
65
+ ...getExcludeDeps(),
66
+ // The public browser entry re-exports the RSDW browser client.
67
+ // Excluding both keeps Vite from freezing the unpatched bundle into
68
+ // .vite/deps before our source transforms run.
69
+ "@vitejs/plugin-rsc/browser",
70
+ // Keep the browser RSDW client out of Vite's dep optimizer so our
71
+ // cjs-to-esm transform can patch the real file.
72
+ "@vitejs/plugin-rsc/vendor/react-server-dom/client.browser",
73
+ ];
74
+
75
+ // Mutable ref for router path (node preset only).
76
+ // Set immediately when user-specified, or populated by the auto-discover
77
+ // config() hook using Vite's resolved root.
78
+ const routerRef: { path: string | undefined } = { path: undefined };
79
+
80
+ // Build-time prerendering is enabled for both presets.
81
+ // Collection runs in-process via the RSC dev environment runner during discoverRouters().
82
+ const prerenderEnabled = true;
83
+
84
+ if (preset === "cloudflare") {
85
+ // Cloudflare preset: configure entries for cloudflare worker setup
86
+ // Router is not needed here - worker.rsc.tsx imports it directly
87
+
88
+ // Dynamically import @vitejs/plugin-rsc
89
+ const { default: rsc } = await import("@vitejs/plugin-rsc");
90
+
91
+ // Only client and ssr entries - rsc entry is handled by cloudflare plugin
92
+ // Always use virtual modules for cloudflare preset
93
+ const finalEntries: { client: string; ssr: string } = {
94
+ client: VIRTUAL_IDS.browser,
95
+ ssr: VIRTUAL_IDS.ssr,
96
+ };
97
+
98
+ plugins.push({
99
+ name: "@rangojs/router:cloudflare-integration",
100
+ enforce: "pre",
101
+
102
+ config() {
103
+ // Configure environments for cloudflare deployment
104
+ return {
105
+ // Exclude rsc-router modules from optimization to prevent module duplication
106
+ // This ensures the same Context instance is used by both browser entry and RSC proxy modules
107
+ optimizeDeps: {
108
+ exclude: excludeDeps,
109
+ esbuildOptions: sharedEsbuildOptions,
110
+ },
111
+ resolve: {
112
+ alias: rangoAliases,
113
+ },
114
+ build: {
115
+ rollupOptions: { onwarn },
116
+ },
117
+ environments: {
118
+ client: {
119
+ build: {
120
+ rollupOptions: {
121
+ output: {
122
+ manualChunks: getManualChunks,
123
+ },
124
+ },
125
+ },
126
+ // Pre-bundle rsc-html-stream to prevent discovery during first request
127
+ // Exclude rsc-router modules to ensure same Context instance
128
+ optimizeDeps: {
129
+ include: ["rsc-html-stream/client"],
130
+ exclude: excludeDeps,
131
+ esbuildOptions: sharedEsbuildOptions,
132
+ },
133
+ },
134
+ ssr: {
135
+ // Build SSR inside RSC directory so wrangler can deploy self-contained dist/rsc
136
+ build: {
137
+ outDir: "./dist/rsc/ssr",
138
+ },
139
+ resolve: {
140
+ // Ensure single React instance in SSR child environment
141
+ dedupe: ["react", "react-dom"],
142
+ },
143
+ // Pre-bundle SSR entry and React for proper module linking with childEnvironments
144
+ // All deps must be listed to avoid late discovery triggering ERR_OUTDATED_OPTIMIZED_DEP
145
+ optimizeDeps: {
146
+ entries: [finalEntries.ssr],
147
+ include: [
148
+ "react",
149
+ "react-dom",
150
+ "react-dom/server.edge",
151
+ "react-dom/static.edge",
152
+ "react/jsx-runtime",
153
+ "react/jsx-dev-runtime",
154
+ "rsc-html-stream/server",
155
+ "@vitejs/plugin-rsc/vendor/react-server-dom/client.edge",
156
+ ],
157
+ exclude: excludeDeps,
158
+ esbuildOptions: sharedEsbuildOptions,
159
+ },
160
+ },
161
+ rsc: {
162
+ // RSC environment needs exclude list and esbuild options
163
+ // Exclude rsc-router modules to prevent createContext in RSC environment
164
+ optimizeDeps: {
165
+ // Pre-bundle all RSC deps to prevent late discovery triggering ERR_OUTDATED_OPTIMIZED_DEP
166
+ include: [
167
+ "react",
168
+ "react/jsx-runtime",
169
+ "react/jsx-dev-runtime",
170
+ "@vitejs/plugin-rsc/vendor/react-server-dom/server.edge",
171
+ ],
172
+ exclude: excludeDeps,
173
+ esbuildOptions: sharedEsbuildOptions,
174
+ },
175
+ },
176
+ },
177
+ };
178
+ },
179
+
180
+ configResolved(config) {
181
+ if (showBanner) {
182
+ const mode =
183
+ config.command === "serve"
184
+ ? process.argv.includes("preview")
185
+ ? "preview"
186
+ : "dev"
187
+ : "build";
188
+ printBanner(mode, "cloudflare", rangoVersion);
189
+ }
190
+ },
191
+ });
192
+
193
+ plugins.push(createVirtualEntriesPlugin(finalEntries));
194
+
195
+ // Dev-only: RSDW client patch for React Performance Tracks
196
+ plugins.push(performanceTracksPlugin());
197
+
198
+ // Add RSC plugin with cloudflare-specific options
199
+ // Note: loadModuleDevProxy should NOT be used with childEnvironments
200
+ // since SSR runs in workerd alongside RSC
201
+ plugins.push(
202
+ rsc({
203
+ entries: finalEntries,
204
+ serverHandler: false,
205
+ }) as PluginOption,
206
+ );
207
+
208
+ // Deduplicate client references from third-party packages in dev mode.
209
+ // Prevents module duplication when server components import "use client"
210
+ // packages that are also imported directly by client components.
211
+ plugins.push(clientRefDedup());
212
+ } else {
213
+ // Auto-discover router using Vite's resolved root (not process.cwd())
214
+ plugins.push({
215
+ name: "@rangojs/router:auto-discover",
216
+ config(userConfig) {
217
+ if (routerRef.path) return;
218
+ const root = userConfig.root
219
+ ? resolve(process.cwd(), userConfig.root)
220
+ : process.cwd();
221
+ const candidates = findRouterFiles(root);
222
+ if (candidates.length === 1) {
223
+ const abs = candidates[0];
224
+ routerRef.path = (
225
+ abs.startsWith(root) ? "./" + abs.slice(root.length + 1) : abs
226
+ ).replaceAll("\\", "/");
227
+ } else if (candidates.length > 1) {
228
+ const list = candidates
229
+ .map(
230
+ (f) =>
231
+ " - " + (f.startsWith(root) ? f.slice(root.length + 1) : f),
232
+ )
233
+ .join("\n");
234
+ throw new Error(`[rsc-router] Multiple routers found:\n${list}`);
235
+ }
236
+ // 0 found: routerRef.path stays undefined, warn at startup via discovery plugin
237
+ },
238
+ });
239
+
240
+ // Always use virtual entries for client, ssr, and rsc
241
+ const finalEntries = {
242
+ client: VIRTUAL_IDS.browser,
243
+ ssr: VIRTUAL_IDS.ssr,
244
+ rsc: VIRTUAL_IDS.rsc,
245
+ };
246
+
247
+ // Dynamically import @vitejs/plugin-rsc
248
+ const { default: rsc } = await import("@vitejs/plugin-rsc");
249
+
250
+ let hasWarnedDuplicate = false;
251
+
252
+ plugins.push({
253
+ name: "@rangojs/router:rsc-integration",
254
+ enforce: "pre",
255
+
256
+ config() {
257
+ return {
258
+ optimizeDeps: {
259
+ exclude: excludeDeps,
260
+ esbuildOptions: sharedEsbuildOptions,
261
+ },
262
+ build: {
263
+ rollupOptions: { onwarn },
264
+ },
265
+ resolve: {
266
+ alias: rangoAliases,
267
+ },
268
+ environments: {
269
+ client: {
270
+ build: {
271
+ rollupOptions: {
272
+ output: {
273
+ manualChunks: getManualChunks,
274
+ },
275
+ },
276
+ },
277
+ optimizeDeps: {
278
+ include: [
279
+ "react",
280
+ "react-dom",
281
+ "react/jsx-runtime",
282
+ "react/jsx-dev-runtime",
283
+ "rsc-html-stream/client",
284
+ ],
285
+ exclude: excludeDeps,
286
+ esbuildOptions: sharedEsbuildOptions,
287
+ entries: [VIRTUAL_IDS.browser],
288
+ },
289
+ },
290
+ ssr: {
291
+ optimizeDeps: {
292
+ entries: [VIRTUAL_IDS.ssr],
293
+ include: [
294
+ "react",
295
+ "react-dom",
296
+ "react-dom/server.edge",
297
+ "react-dom/static.edge",
298
+ "react/jsx-runtime",
299
+ "react/jsx-dev-runtime",
300
+ "@vitejs/plugin-rsc/vendor/react-server-dom/client.edge",
301
+ ],
302
+ exclude: excludeDeps,
303
+ esbuildOptions: sharedEsbuildOptions,
304
+ },
305
+ },
306
+ rsc: {
307
+ optimizeDeps: {
308
+ entries: [VIRTUAL_IDS.rsc],
309
+ include: [
310
+ "react",
311
+ "react/jsx-runtime",
312
+ "react/jsx-dev-runtime",
313
+ "@vitejs/plugin-rsc/vendor/react-server-dom/server.edge",
314
+ ],
315
+ esbuildOptions: sharedEsbuildOptions,
316
+ },
317
+ },
318
+ },
319
+ };
320
+ },
321
+
322
+ configResolved(config) {
323
+ if (showBanner) {
324
+ const mode =
325
+ config.command === "serve"
326
+ ? process.argv.includes("preview")
327
+ ? "preview"
328
+ : "dev"
329
+ : "build";
330
+ printBanner(mode, "node", rangoVersion);
331
+ }
332
+
333
+ const rscMinimalCount = config.plugins.filter(
334
+ (p) => p.name === "rsc:minimal",
335
+ ).length;
336
+
337
+ if (rscMinimalCount > 1 && !hasWarnedDuplicate) {
338
+ hasWarnedDuplicate = true;
339
+ console.warn(
340
+ "[rsc-router] Duplicate @vitejs/plugin-rsc detected. " +
341
+ "Remove rsc() from your vite config — rango() includes it automatically.",
342
+ );
343
+ }
344
+ },
345
+ });
346
+
347
+ // Add virtual entries plugin (RSC entry generated lazily from routerRef)
348
+ plugins.push(createVirtualEntriesPlugin(finalEntries, routerRef));
349
+
350
+ // Dev-only: RSDW client patch for React Performance Tracks
351
+ plugins.push(performanceTracksPlugin());
352
+
353
+ plugins.push(
354
+ rsc({
355
+ entries: finalEntries,
356
+ }) as PluginOption,
357
+ );
358
+
359
+ // Deduplicate client references from third-party packages in dev mode.
360
+ // Prevents module duplication when server components import "use client"
361
+ // packages that are also imported directly by client components.
362
+ plugins.push(clientRefDedup());
363
+ }
364
+
365
+ // Fix HMR for "use client" components.
366
+ //
367
+ // @vitejs/plugin-rsc's hotUpdate returns undefined for "use client" files
368
+ // in the RSC environment. Vite then tries to propagate through the RSC
369
+ // module graph, but the proxy module has no import.meta.hot.accept()
370
+ // boundary, causing a full page reload. The client env would handle it
371
+ // fine via React Refresh, but the RSC env's full-reload arrives first.
372
+ //
373
+ // Fix: in the RSC env, return [] for "use client" files to signal
374
+ // "handled, nothing to propagate". The client env is left alone so
375
+ // React Refresh processes the update normally.
376
+ plugins.push({
377
+ name: "@rangojs/router:client-component-hmr",
378
+ hotUpdate(ctx) {
379
+ const envName = this.environment?.name;
380
+ if (envName !== "rsc" && envName !== "ssr") return;
381
+
382
+ // Check if the changed file is a "use client" module
383
+ const file = ctx.file;
384
+ if (
385
+ !file.endsWith(".tsx") &&
386
+ !file.endsWith(".ts") &&
387
+ !file.endsWith(".jsx") &&
388
+ !file.endsWith(".js")
389
+ )
390
+ return;
391
+
392
+ try {
393
+ const source = readFileSync(file, "utf-8");
394
+ const trimmed = source.trimStart();
395
+ if (
396
+ trimmed.startsWith('"use client"') ||
397
+ trimmed.startsWith("'use client'")
398
+ ) {
399
+ // Consume the update in RSC/SSR envs. The proxy module was already
400
+ // re-transformed by the RSC plugin's hotUpdate. Without this, Vite
401
+ // tries to propagate through the RSC/SSR module graph where the proxy
402
+ // has no import.meta.hot.accept() boundary, triggering a full reload.
403
+ // The actual component update is handled by React Refresh in the
404
+ // client environment.
405
+ return [];
406
+ }
407
+ } catch {
408
+ // File deleted/moved during HMR, let default handling proceed
409
+ }
410
+ },
411
+ });
412
+
413
+ plugins.push(exposeActionId());
414
+
415
+ // "use cache" directive transform (enforce: "post"):
416
+ // Wraps exports with registerCachedFunction() for function-level caching.
417
+ plugins.push(useCacheTransform());
418
+
419
+ // Consolidated plugin for create* ID injection (enforce: "post"):
420
+ // loaders, handles, location state, and prerender handlers.
421
+ plugins.push(exposeInternalIds());
422
+
423
+ // Router ID injection runs at normal priority (no enforce) to avoid
424
+ // changing Vite's dep optimization timing.
425
+ plugins.push(exposeRouterId());
426
+
427
+ // Add version virtual module plugin for cache invalidation
428
+ plugins.push(createVersionPlugin());
429
+
430
+ // Entry path for discovery: user-specified value (if any) or undefined.
431
+ // Auto-discovered path is passed separately via routerRef.
432
+ // Cloudflare preset: deferred to configResolved (read from resolved Vite env config).
433
+ const discoveryEntryPath =
434
+ preset !== "cloudflare" ? routerRef.path : undefined;
435
+ // Ref for deferred auto-discovery (node preset only, undefined for cloudflare)
436
+ const discoveryRouterRef = preset !== "cloudflare" ? routerRef : undefined;
437
+
438
+ // Version injector: auto-injects VERSION and routes-manifest into the RSC entry.
439
+ // For cloudflare preset, the entry is resolved lazily in configResolved.
440
+ // For node preset, the virtual entry already includes these imports.
441
+ if (preset === "cloudflare") {
442
+ plugins.push(createVersionInjectorPlugin(undefined));
443
+ }
444
+
445
+ // Transform CJS vendor files to ESM for browser compatibility
446
+ // optimizeDeps.include doesn't work because the file is loaded after initial optimization
447
+ plugins.push(createCjsToEsmPlugin());
448
+
449
+ // Router discovery plugin for build-time manifest generation.
450
+ // For cloudflare, the entry is resolved lazily in configResolved from the RSC environment.
451
+ // For node, discoveryRouterRef provides the auto-discovered path when not user-specified.
452
+ plugins.push(
453
+ createRouterDiscoveryPlugin(discoveryEntryPath, {
454
+ routerPathRef: discoveryRouterRef,
455
+ enableBuildPrerender: prerenderEnabled,
456
+ buildEnv: options?.buildEnv,
457
+ preset,
458
+ }),
459
+ );
460
+
461
+ return plugins;
462
+ }