@rangojs/router 0.0.0-experimental.10 → 0.0.0-experimental.100

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