@rangojs/router 0.0.0-experimental.20 → 0.0.0-experimental.20dbba0c

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 (189) hide show
  1. package/AGENTS.md +4 -0
  2. package/README.md +172 -50
  3. package/dist/bin/rango.js +138 -50
  4. package/dist/vite/index.js +1160 -508
  5. package/dist/vite/index.js.bak +5448 -0
  6. package/dist/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
  7. package/package.json +17 -16
  8. package/skills/breadcrumbs/SKILL.md +252 -0
  9. package/skills/cache-guide/SKILL.md +32 -0
  10. package/skills/caching/SKILL.md +49 -8
  11. package/skills/document-cache/SKILL.md +2 -2
  12. package/skills/handler-use/SKILL.md +362 -0
  13. package/skills/hooks/SKILL.md +61 -51
  14. package/skills/host-router/SKILL.md +218 -0
  15. package/skills/intercept/SKILL.md +20 -0
  16. package/skills/layout/SKILL.md +22 -0
  17. package/skills/links/SKILL.md +91 -17
  18. package/skills/loader/SKILL.md +107 -24
  19. package/skills/middleware/SKILL.md +34 -3
  20. package/skills/migrate-nextjs/SKILL.md +560 -0
  21. package/skills/migrate-react-router/SKILL.md +765 -0
  22. package/skills/parallel/SKILL.md +185 -0
  23. package/skills/prerender/SKILL.md +112 -70
  24. package/skills/rango/SKILL.md +24 -23
  25. package/skills/response-routes/SKILL.md +8 -0
  26. package/skills/route/SKILL.md +58 -4
  27. package/skills/router-setup/SKILL.md +95 -5
  28. package/skills/streams-and-websockets/SKILL.md +283 -0
  29. package/skills/typesafety/SKILL.md +38 -24
  30. package/src/__internal.ts +92 -0
  31. package/src/browser/app-shell.ts +52 -0
  32. package/src/browser/app-version.ts +14 -0
  33. package/src/browser/event-controller.ts +5 -0
  34. package/src/browser/link-interceptor.ts +4 -0
  35. package/src/browser/navigation-bridge.ts +175 -17
  36. package/src/browser/navigation-client.ts +177 -44
  37. package/src/browser/navigation-store.ts +68 -9
  38. package/src/browser/navigation-transaction.ts +11 -9
  39. package/src/browser/partial-update.ts +113 -17
  40. package/src/browser/prefetch/cache.ts +275 -28
  41. package/src/browser/prefetch/fetch.ts +191 -46
  42. package/src/browser/prefetch/policy.ts +6 -0
  43. package/src/browser/prefetch/queue.ts +123 -20
  44. package/src/browser/prefetch/resource-ready.ts +77 -0
  45. package/src/browser/rango-state.ts +53 -13
  46. package/src/browser/react/Link.tsx +98 -14
  47. package/src/browser/react/NavigationProvider.tsx +89 -14
  48. package/src/browser/react/context.ts +7 -2
  49. package/src/browser/react/use-handle.ts +9 -58
  50. package/src/browser/react/use-navigation.ts +22 -2
  51. package/src/browser/react/use-params.ts +11 -1
  52. package/src/browser/react/use-router.ts +29 -9
  53. package/src/browser/rsc-router.tsx +177 -66
  54. package/src/browser/scroll-restoration.ts +41 -42
  55. package/src/browser/segment-reconciler.ts +36 -9
  56. package/src/browser/server-action-bridge.ts +8 -6
  57. package/src/browser/types.ts +73 -5
  58. package/src/build/generate-manifest.ts +6 -6
  59. package/src/build/generate-route-types.ts +3 -0
  60. package/src/build/route-trie.ts +67 -25
  61. package/src/build/route-types/include-resolution.ts +8 -1
  62. package/src/build/route-types/router-processing.ts +223 -74
  63. package/src/build/route-types/scan-filter.ts +8 -1
  64. package/src/cache/cache-runtime.ts +15 -11
  65. package/src/cache/cache-scope.ts +48 -7
  66. package/src/cache/cf/cf-cache-store.ts +455 -15
  67. package/src/cache/cf/index.ts +5 -1
  68. package/src/cache/document-cache.ts +17 -7
  69. package/src/cache/index.ts +1 -0
  70. package/src/cache/taint.ts +55 -0
  71. package/src/client.rsc.tsx +2 -1
  72. package/src/client.tsx +85 -276
  73. package/src/context-var.ts +72 -2
  74. package/src/debug.ts +2 -2
  75. package/src/handle.ts +40 -0
  76. package/src/handles/breadcrumbs.ts +66 -0
  77. package/src/handles/index.ts +1 -0
  78. package/src/host/index.ts +0 -3
  79. package/src/index.rsc.ts +9 -36
  80. package/src/index.ts +79 -70
  81. package/src/outlet-context.ts +1 -1
  82. package/src/prerender/store.ts +57 -15
  83. package/src/prerender.ts +138 -77
  84. package/src/response-utils.ts +28 -0
  85. package/src/reverse.ts +27 -2
  86. package/src/route-definition/dsl-helpers.ts +240 -40
  87. package/src/route-definition/helpers-types.ts +67 -19
  88. package/src/route-definition/index.ts +3 -3
  89. package/src/route-definition/redirect.ts +11 -3
  90. package/src/route-definition/resolve-handler-use.ts +155 -0
  91. package/src/route-map-builder.ts +7 -1
  92. package/src/route-types.ts +18 -0
  93. package/src/router/content-negotiation.ts +100 -1
  94. package/src/router/find-match.ts +4 -2
  95. package/src/router/handler-context.ts +129 -26
  96. package/src/router/intercept-resolution.ts +11 -4
  97. package/src/router/lazy-includes.ts +10 -7
  98. package/src/router/loader-resolution.ts +160 -22
  99. package/src/router/logging.ts +5 -2
  100. package/src/router/manifest.ts +31 -16
  101. package/src/router/match-api.ts +128 -193
  102. package/src/router/match-middleware/background-revalidation.ts +30 -2
  103. package/src/router/match-middleware/cache-lookup.ts +94 -17
  104. package/src/router/match-middleware/cache-store.ts +53 -10
  105. package/src/router/match-middleware/intercept-resolution.ts +9 -7
  106. package/src/router/match-middleware/segment-resolution.ts +61 -5
  107. package/src/router/match-result.ts +103 -18
  108. package/src/router/metrics.ts +238 -13
  109. package/src/router/middleware-types.ts +48 -27
  110. package/src/router/middleware.ts +201 -86
  111. package/src/router/navigation-snapshot.ts +182 -0
  112. package/src/router/pattern-matching.ts +77 -11
  113. package/src/router/prerender-match.ts +114 -10
  114. package/src/router/preview-match.ts +30 -102
  115. package/src/router/request-classification.ts +310 -0
  116. package/src/router/revalidation.ts +27 -7
  117. package/src/router/route-snapshot.ts +245 -0
  118. package/src/router/router-context.ts +6 -1
  119. package/src/router/router-interfaces.ts +50 -5
  120. package/src/router/router-options.ts +50 -19
  121. package/src/router/segment-resolution/fresh.ts +215 -19
  122. package/src/router/segment-resolution/helpers.ts +30 -25
  123. package/src/router/segment-resolution/loader-cache.ts +1 -0
  124. package/src/router/segment-resolution/revalidation.ts +454 -301
  125. package/src/router/segment-wrappers.ts +2 -0
  126. package/src/router/trie-matching.ts +30 -6
  127. package/src/router/types.ts +1 -0
  128. package/src/router/url-params.ts +49 -0
  129. package/src/router.ts +89 -17
  130. package/src/rsc/handler.ts +563 -364
  131. package/src/rsc/helpers.ts +69 -41
  132. package/src/rsc/index.ts +0 -20
  133. package/src/rsc/loader-fetch.ts +23 -3
  134. package/src/rsc/manifest-init.ts +5 -1
  135. package/src/rsc/progressive-enhancement.ts +37 -10
  136. package/src/rsc/response-route-handler.ts +14 -1
  137. package/src/rsc/rsc-rendering.ts +47 -44
  138. package/src/rsc/server-action.ts +24 -10
  139. package/src/rsc/ssr-setup.ts +128 -0
  140. package/src/rsc/types.ts +11 -1
  141. package/src/search-params.ts +16 -13
  142. package/src/segment-content-promise.ts +67 -0
  143. package/src/segment-loader-promise.ts +122 -0
  144. package/src/segment-system.tsx +109 -23
  145. package/src/server/context.ts +174 -19
  146. package/src/server/handle-store.ts +19 -0
  147. package/src/server/loader-registry.ts +9 -8
  148. package/src/server/request-context.ts +218 -65
  149. package/src/server.ts +6 -0
  150. package/src/ssr/index.tsx +4 -0
  151. package/src/static-handler.ts +18 -6
  152. package/src/theme/index.ts +4 -13
  153. package/src/types/cache-types.ts +4 -4
  154. package/src/types/handler-context.ts +140 -72
  155. package/src/types/loader-types.ts +41 -15
  156. package/src/types/request-scope.ts +126 -0
  157. package/src/types/route-config.ts +17 -8
  158. package/src/types/route-entry.ts +19 -1
  159. package/src/types/segments.ts +2 -5
  160. package/src/urls/include-helper.ts +24 -14
  161. package/src/urls/path-helper-types.ts +39 -6
  162. package/src/urls/path-helper.ts +48 -13
  163. package/src/urls/pattern-types.ts +12 -0
  164. package/src/urls/response-types.ts +18 -16
  165. package/src/use-loader.tsx +77 -5
  166. package/src/vite/discovery/bundle-postprocess.ts +61 -89
  167. package/src/vite/discovery/discover-routers.ts +7 -4
  168. package/src/vite/discovery/prerender-collection.ts +162 -88
  169. package/src/vite/discovery/state.ts +17 -13
  170. package/src/vite/index.ts +8 -3
  171. package/src/vite/plugin-types.ts +51 -79
  172. package/src/vite/plugins/cloudflare-protocol-loader-hook.d.mts +23 -0
  173. package/src/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
  174. package/src/vite/plugins/cloudflare-protocol-stub.ts +214 -0
  175. package/src/vite/plugins/expose-action-id.ts +1 -3
  176. package/src/vite/plugins/expose-id-utils.ts +12 -0
  177. package/src/vite/plugins/expose-ids/handler-transform.ts +30 -0
  178. package/src/vite/plugins/expose-internal-ids.ts +257 -40
  179. package/src/vite/plugins/performance-tracks.ts +88 -0
  180. package/src/vite/plugins/refresh-cmd.ts +127 -0
  181. package/src/vite/plugins/version-plugin.ts +13 -1
  182. package/src/vite/rango.ts +190 -217
  183. package/src/vite/router-discovery.ts +241 -45
  184. package/src/vite/utils/banner.ts +4 -4
  185. package/src/vite/utils/package-resolution.ts +34 -1
  186. package/src/vite/utils/prerender-utils.ts +97 -5
  187. package/src/vite/utils/shared-utils.ts +3 -2
  188. package/skills/testing/SKILL.md +0 -226
  189. package/src/route-definition/route-function.ts +0 -119
package/src/vite/rango.ts CHANGED
@@ -12,11 +12,10 @@ import { VIRTUAL_IDS } from "./plugins/virtual-entries.js";
12
12
  import {
13
13
  getExcludeDeps,
14
14
  getPackageAliases,
15
+ getPublishedPackageName,
16
+ getVendorAliases,
15
17
  } from "./utils/package-resolution.js";
16
- import {
17
- createScanFilter,
18
- findRouterFiles,
19
- } from "../build/generate-route-types.js";
18
+ import { findRouterFiles } from "../build/generate-route-types.js";
20
19
  import { createVersionPlugin } from "./plugins/version-plugin.js";
21
20
  import {
22
21
  sharedEsbuildOptions,
@@ -24,15 +23,12 @@ import {
24
23
  onwarn,
25
24
  getManualChunks,
26
25
  } from "./utils/shared-utils.js";
27
- import type {
28
- RangoOptions,
29
- RangoNodeOptions,
30
- RscPluginOptions,
31
- } from "./plugin-types.js";
26
+ import type { RangoOptions } from "./plugin-types.js";
32
27
  import { printBanner, rangoVersion } from "./utils/banner.js";
33
28
  import { createVersionInjectorPlugin } from "./plugins/version-injector.js";
34
29
  import { createCjsToEsmPlugin } from "./plugins/cjs-to-esm.js";
35
30
  import { createRouterDiscoveryPlugin } from "./router-discovery.js";
31
+ import { performanceTracksPlugin } from "./plugins/performance-tracks.js";
36
32
 
37
33
  /**
38
34
  * Vite plugin for @rangojs/router.
@@ -43,7 +39,7 @@ import { createRouterDiscoveryPlugin } from "./router-discovery.js";
43
39
  * @example Node.js (default)
44
40
  * ```ts
45
41
  * export default defineConfig({
46
- * plugins: [react(), rango({ router: './src/router.tsx' })],
42
+ * plugins: [react(), rango()],
47
43
  * });
48
44
  * ```
49
45
  *
@@ -65,12 +61,29 @@ export async function rango(options?: RangoOptions): Promise<PluginOption[]> {
65
61
 
66
62
  const plugins: PluginOption[] = [];
67
63
 
68
- // Get package resolution info (workspace vs npm install)
69
- const rangoAliases = getPackageAliases();
70
- const excludeDeps = getExcludeDeps();
71
-
72
- // Track RSC entry path for version injection
73
- let rscEntryPath: string | null = null;
64
+ // Get package resolution info (workspace vs npm install).
65
+ // Vendor aliases redirect the bare plugin-rsc vendor specs (which plugin-rsc
66
+ // itself injects into optimizeDeps.include) to absolute paths resolved from
67
+ // this package — so strict-pnpm consumers don't hit "Failed to resolve
68
+ // dependency" warnings when those deps aren't hoisted to their app root.
69
+ const rangoAliases = { ...getPackageAliases(), ...getVendorAliases() };
70
+ const excludeDeps = [
71
+ ...getExcludeDeps(),
72
+ // The public browser entry re-exports the RSDW browser client.
73
+ // Excluding both keeps Vite from freezing the unpatched bundle into
74
+ // .vite/deps before our source transforms run.
75
+ "@vitejs/plugin-rsc/browser",
76
+ // Keep the browser RSDW client out of Vite's dep optimizer so our
77
+ // cjs-to-esm transform can patch the real file.
78
+ "@vitejs/plugin-rsc/vendor/react-server-dom/client.browser",
79
+ ];
80
+
81
+ // Vite supports a nested `A > B` syntax in optimizeDeps.include that resolves
82
+ // B from A's location. We anchor transitive deps (rsc-html-stream,
83
+ // @vitejs/plugin-rsc/vendor/*) to @rangojs/router so pnpm consumers — where
84
+ // these aren't visible at the app root — can still pre-bundle them.
85
+ const pkg = getPublishedPackageName();
86
+ const nested = (spec: string) => `${pkg} > ${spec}`;
74
87
 
75
88
  // Mutable ref for router path (node preset only).
76
89
  // Set immediately when user-specified, or populated by the auto-discover
@@ -126,7 +139,7 @@ export async function rango(options?: RangoOptions): Promise<PluginOption[]> {
126
139
  // Pre-bundle rsc-html-stream to prevent discovery during first request
127
140
  // Exclude rsc-router modules to ensure same Context instance
128
141
  optimizeDeps: {
129
- include: ["rsc-html-stream/client"],
142
+ include: [nested("rsc-html-stream/client")],
130
143
  exclude: excludeDeps,
131
144
  esbuildOptions: sharedEsbuildOptions,
132
145
  },
@@ -151,8 +164,10 @@ export async function rango(options?: RangoOptions): Promise<PluginOption[]> {
151
164
  "react-dom/static.edge",
152
165
  "react/jsx-runtime",
153
166
  "react/jsx-dev-runtime",
154
- "rsc-html-stream/server",
155
- "@vitejs/plugin-rsc/vendor/react-server-dom/client.edge",
167
+ nested("rsc-html-stream/server"),
168
+ nested(
169
+ "@vitejs/plugin-rsc/vendor/react-server-dom/client.edge",
170
+ ),
156
171
  ],
157
172
  exclude: excludeDeps,
158
173
  esbuildOptions: sharedEsbuildOptions,
@@ -167,7 +182,9 @@ export async function rango(options?: RangoOptions): Promise<PluginOption[]> {
167
182
  "react",
168
183
  "react/jsx-runtime",
169
184
  "react/jsx-dev-runtime",
170
- "@vitejs/plugin-rsc/vendor/react-server-dom/server.edge",
185
+ nested(
186
+ "@vitejs/plugin-rsc/vendor/react-server-dom/server.edge",
187
+ ),
171
188
  ],
172
189
  exclude: excludeDeps,
173
190
  esbuildOptions: sharedEsbuildOptions,
@@ -192,6 +209,9 @@ export async function rango(options?: RangoOptions): Promise<PluginOption[]> {
192
209
 
193
210
  plugins.push(createVirtualEntriesPlugin(finalEntries));
194
211
 
212
+ // Dev-only: RSDW client patch for React Performance Tracks
213
+ plugins.push(performanceTracksPlugin());
214
+
195
215
  // Add RSC plugin with cloudflare-specific options
196
216
  // Note: loadModuleDevProxy should NOT be used with childEnvironments
197
217
  // since SSR runs in workerd alongside RSC
@@ -207,198 +227,155 @@ export async function rango(options?: RangoOptions): Promise<PluginOption[]> {
207
227
  // packages that are also imported directly by client components.
208
228
  plugins.push(clientRefDedup());
209
229
  } else {
210
- // Node preset: full RSC plugin integration
211
- const nodeOptions = resolvedOptions as RangoNodeOptions;
230
+ // Auto-discover router using Vite's resolved root (not process.cwd())
231
+ plugins.push({
232
+ name: "@rangojs/router:auto-discover",
233
+ config(userConfig) {
234
+ if (routerRef.path) return;
235
+ const root = userConfig.root
236
+ ? resolve(process.cwd(), userConfig.root)
237
+ : process.cwd();
238
+ const candidates = findRouterFiles(root);
239
+ if (candidates.length === 1) {
240
+ const abs = candidates[0];
241
+ routerRef.path = (
242
+ abs.startsWith(root) ? "./" + abs.slice(root.length + 1) : abs
243
+ ).replaceAll("\\", "/");
244
+ } else if (candidates.length > 1) {
245
+ const list = candidates
246
+ .map(
247
+ (f) =>
248
+ " - " + (f.startsWith(root) ? f.slice(root.length + 1) : f),
249
+ )
250
+ .join("\n");
251
+ throw new Error(`[rsc-router] Multiple routers found:\n${list}`);
252
+ }
253
+ // 0 found: routerRef.path stays undefined, warn at startup via discovery plugin
254
+ },
255
+ });
212
256
 
213
- routerRef.path = nodeOptions.router;
257
+ // Always use virtual entries for client, ssr, and rsc
258
+ const finalEntries = {
259
+ client: VIRTUAL_IDS.browser,
260
+ ssr: VIRTUAL_IDS.ssr,
261
+ rsc: VIRTUAL_IDS.rsc,
262
+ };
214
263
 
215
- // Auto-discover router using Vite's resolved root (not process.cwd())
216
- if (!routerRef.path) {
217
- plugins.push({
218
- name: "@rangojs/router:auto-discover",
219
- config(userConfig) {
220
- if (routerRef.path) return;
221
- const root = userConfig.root
222
- ? resolve(process.cwd(), userConfig.root)
223
- : process.cwd();
224
- const filter = createScanFilter(root, {
225
- include: resolvedOptions.include,
226
- exclude: resolvedOptions.exclude,
227
- });
228
- const candidates = findRouterFiles(root, filter);
229
- if (candidates.length === 1) {
230
- const abs = candidates[0];
231
- routerRef.path = (
232
- abs.startsWith(root) ? "./" + abs.slice(root.length + 1) : abs
233
- ).replaceAll("\\", "/");
234
- } else if (candidates.length > 1) {
235
- const list = candidates
236
- .map(
237
- (f) =>
238
- " - " + (f.startsWith(root) ? f.slice(root.length + 1) : f),
239
- )
240
- .join("\n");
241
- throw new Error(
242
- `[rsc-router] Multiple routers found. Specify \`router\` to choose one:\n${list}`,
243
- );
244
- }
245
- // 0 found: routerRef.path stays undefined, warn at startup via discovery plugin
246
- },
247
- });
248
- }
249
-
250
- const rscOption = nodeOptions.rsc ?? true;
251
-
252
- // Add RSC plugin by default (can be disabled with rsc: false)
253
- if (rscOption !== false) {
254
- // Dynamically import @vitejs/plugin-rsc
255
- const { default: rsc } = await import("@vitejs/plugin-rsc");
256
-
257
- // Resolve entry paths: use explicit config or virtual modules
258
- const userEntries =
259
- typeof rscOption === "boolean" ? {} : rscOption.entries || {};
260
- const finalEntries = {
261
- client: userEntries.client ?? VIRTUAL_IDS.browser,
262
- ssr: userEntries.ssr ?? VIRTUAL_IDS.ssr,
263
- rsc: userEntries.rsc ?? VIRTUAL_IDS.rsc,
264
- };
265
-
266
- // Track RSC entry for version injection (only if custom entry provided)
267
- rscEntryPath = userEntries.rsc ?? null;
268
-
269
- // Create wrapper plugin that checks for duplicates
270
- let hasWarnedDuplicate = false;
271
-
272
- plugins.push({
273
- name: "@rangojs/router:rsc-integration",
274
- enforce: "pre",
275
-
276
- config() {
277
- // Configure environments for RSC
278
- // When using virtual entries, we need to explicitly configure optimizeDeps
279
- // so Vite pre-bundles React before processing the virtual modules.
280
- // Without this, the dep optimizer may run multiple times with different hashes,
281
- // causing React instance mismatches.
282
- const useVirtualClient = finalEntries.client === VIRTUAL_IDS.browser;
283
- const useVirtualSSR = finalEntries.ssr === VIRTUAL_IDS.ssr;
284
- const useVirtualRSC = finalEntries.rsc === VIRTUAL_IDS.rsc;
285
-
286
- return {
287
- // Exclude rsc-router modules from optimization to prevent module duplication
288
- // This ensures the same Context instance is used by both browser entry and RSC proxy modules
289
- optimizeDeps: {
290
- exclude: excludeDeps,
291
- esbuildOptions: sharedEsbuildOptions,
292
- },
293
- build: {
294
- rollupOptions: { onwarn },
295
- },
296
- resolve: {
297
- alias: rangoAliases,
298
- },
299
- environments: {
300
- client: {
301
- build: {
302
- rollupOptions: {
303
- output: {
304
- manualChunks: getManualChunks,
305
- },
264
+ // Dynamically import @vitejs/plugin-rsc
265
+ const { default: rsc } = await import("@vitejs/plugin-rsc");
266
+
267
+ let hasWarnedDuplicate = false;
268
+
269
+ plugins.push({
270
+ name: "@rangojs/router:rsc-integration",
271
+ enforce: "pre",
272
+
273
+ config() {
274
+ return {
275
+ optimizeDeps: {
276
+ exclude: excludeDeps,
277
+ esbuildOptions: sharedEsbuildOptions,
278
+ },
279
+ build: {
280
+ rollupOptions: { onwarn },
281
+ },
282
+ resolve: {
283
+ alias: rangoAliases,
284
+ },
285
+ environments: {
286
+ client: {
287
+ build: {
288
+ rollupOptions: {
289
+ output: {
290
+ manualChunks: getManualChunks,
306
291
  },
307
292
  },
308
- // Always exclude rsc-router modules, conditionally add virtual entry
309
- optimizeDeps: {
310
- // Pre-bundle React and rsc-html-stream to prevent late discovery
311
- // triggering ERR_OUTDATED_OPTIMIZED_DEP on cold starts
312
- include: [
313
- "react",
314
- "react-dom",
315
- "react/jsx-runtime",
316
- "react/jsx-dev-runtime",
317
- "rsc-html-stream/client",
318
- ],
319
- exclude: excludeDeps,
320
- esbuildOptions: sharedEsbuildOptions,
321
- ...(useVirtualClient && {
322
- // Tell Vite to scan the virtual entry for dependencies
323
- entries: [VIRTUAL_IDS.browser],
324
- }),
325
- },
326
293
  },
327
- ...(useVirtualSSR && {
328
- ssr: {
329
- optimizeDeps: {
330
- entries: [VIRTUAL_IDS.ssr],
331
- // Pre-bundle all SSR deps to prevent late discovery triggering ERR_OUTDATED_OPTIMIZED_DEP
332
- include: [
333
- "react",
334
- "react-dom",
335
- "react-dom/server.edge",
336
- "react-dom/static.edge",
337
- "react/jsx-runtime",
338
- "react/jsx-dev-runtime",
339
- "@vitejs/plugin-rsc/vendor/react-server-dom/client.edge",
340
- ],
341
- exclude: excludeDeps,
342
- esbuildOptions: sharedEsbuildOptions,
343
- },
344
- },
345
- }),
346
- ...(useVirtualRSC && {
347
- rsc: {
348
- optimizeDeps: {
349
- entries: [VIRTUAL_IDS.rsc],
350
- // Pre-bundle all RSC deps to prevent late discovery triggering ERR_OUTDATED_OPTIMIZED_DEP
351
- include: [
352
- "react",
353
- "react/jsx-runtime",
354
- "react/jsx-dev-runtime",
355
- "@vitejs/plugin-rsc/vendor/react-server-dom/server.edge",
356
- ],
357
- esbuildOptions: sharedEsbuildOptions,
358
- },
359
- },
360
- }),
294
+ optimizeDeps: {
295
+ include: [
296
+ "react",
297
+ "react-dom",
298
+ "react/jsx-runtime",
299
+ "react/jsx-dev-runtime",
300
+ nested("rsc-html-stream/client"),
301
+ ],
302
+ exclude: excludeDeps,
303
+ esbuildOptions: sharedEsbuildOptions,
304
+ entries: [VIRTUAL_IDS.browser],
305
+ },
361
306
  },
362
- };
363
- },
364
-
365
- configResolved(config) {
366
- if (showBanner) {
367
- const mode =
368
- config.command === "serve"
369
- ? process.argv.includes("preview")
370
- ? "preview"
371
- : "dev"
372
- : "build";
373
- printBanner(mode, "node", rangoVersion);
374
- }
375
-
376
- // Count how many RSC base plugins there are (rsc:minimal is the main one)
377
- const rscMinimalCount = config.plugins.filter(
378
- (p) => p.name === "rsc:minimal",
379
- ).length;
380
-
381
- if (rscMinimalCount > 1 && !hasWarnedDuplicate) {
382
- hasWarnedDuplicate = true;
383
- console.warn(
384
- "[rsc-router] Duplicate @vitejs/plugin-rsc detected. " +
385
- "Remove rsc() from your config or use rango({ rsc: false }) for manual configuration.",
386
- );
387
- }
388
- },
389
- });
390
-
391
- // Add virtual entries plugin (RSC entry generated lazily from routerRef)
392
- plugins.push(createVirtualEntriesPlugin(finalEntries, routerRef));
393
-
394
- // Add the RSC plugin directly
395
- // Cast to PluginOption to handle type differences between bundled vite types
396
- plugins.push(
397
- rsc({
398
- entries: finalEntries,
399
- }) as PluginOption,
400
- );
401
- }
307
+ ssr: {
308
+ optimizeDeps: {
309
+ entries: [VIRTUAL_IDS.ssr],
310
+ include: [
311
+ "react",
312
+ "react-dom",
313
+ "react-dom/server.edge",
314
+ "react-dom/static.edge",
315
+ "react/jsx-runtime",
316
+ "react/jsx-dev-runtime",
317
+ nested(
318
+ "@vitejs/plugin-rsc/vendor/react-server-dom/client.edge",
319
+ ),
320
+ ],
321
+ exclude: excludeDeps,
322
+ esbuildOptions: sharedEsbuildOptions,
323
+ },
324
+ },
325
+ rsc: {
326
+ optimizeDeps: {
327
+ entries: [VIRTUAL_IDS.rsc],
328
+ include: [
329
+ "react",
330
+ "react/jsx-runtime",
331
+ "react/jsx-dev-runtime",
332
+ nested(
333
+ "@vitejs/plugin-rsc/vendor/react-server-dom/server.edge",
334
+ ),
335
+ ],
336
+ esbuildOptions: sharedEsbuildOptions,
337
+ },
338
+ },
339
+ },
340
+ };
341
+ },
342
+
343
+ configResolved(config) {
344
+ if (showBanner) {
345
+ const mode =
346
+ config.command === "serve"
347
+ ? process.argv.includes("preview")
348
+ ? "preview"
349
+ : "dev"
350
+ : "build";
351
+ printBanner(mode, "node", rangoVersion);
352
+ }
353
+
354
+ const rscMinimalCount = config.plugins.filter(
355
+ (p) => p.name === "rsc:minimal",
356
+ ).length;
357
+
358
+ if (rscMinimalCount > 1 && !hasWarnedDuplicate) {
359
+ hasWarnedDuplicate = true;
360
+ console.warn(
361
+ "[rsc-router] Duplicate @vitejs/plugin-rsc detected. " +
362
+ "Remove rsc() from your vite config — rango() includes it automatically.",
363
+ );
364
+ }
365
+ },
366
+ });
367
+
368
+ // Add virtual entries plugin (RSC entry generated lazily from routerRef)
369
+ plugins.push(createVirtualEntriesPlugin(finalEntries, routerRef));
370
+
371
+ // Dev-only: RSDW client patch for React Performance Tracks
372
+ plugins.push(performanceTracksPlugin());
373
+
374
+ plugins.push(
375
+ rsc({
376
+ entries: finalEntries,
377
+ }) as PluginOption,
378
+ );
402
379
 
403
380
  // Deduplicate client references from third-party packages in dev mode.
404
381
  // Prevents module duplication when server components import "use client"
@@ -479,14 +456,11 @@ export async function rango(options?: RangoOptions): Promise<PluginOption[]> {
479
456
  // Ref for deferred auto-discovery (node preset only, undefined for cloudflare)
480
457
  const discoveryRouterRef = preset !== "cloudflare" ? routerRef : undefined;
481
458
 
482
- // Version injector: auto-injects VERSION and routes-manifest into custom entry.rsc files.
483
- // Only applies when there's an explicit rscEntryPath or for cloudflare preset (resolved
484
- // lazily in configResolved). For node preset without a custom entry, the router file
485
- // must NOT be transformed — injecting routes-manifest there creates a circular dependency.
486
- const injectorEntryPath =
487
- rscEntryPath ?? (preset === "cloudflare" ? undefined : null);
488
- if (injectorEntryPath !== null) {
489
- plugins.push(createVersionInjectorPlugin(injectorEntryPath));
459
+ // Version injector: auto-injects VERSION and routes-manifest into the RSC entry.
460
+ // For cloudflare preset, the entry is resolved lazily in configResolved.
461
+ // For node preset, the virtual entry already includes these imports.
462
+ if (preset === "cloudflare") {
463
+ plugins.push(createVersionInjectorPlugin(undefined));
490
464
  }
491
465
 
492
466
  // Transform CJS vendor files to ESM for browser compatibility
@@ -500,9 +474,8 @@ export async function rango(options?: RangoOptions): Promise<PluginOption[]> {
500
474
  createRouterDiscoveryPlugin(discoveryEntryPath, {
501
475
  routerPathRef: discoveryRouterRef,
502
476
  enableBuildPrerender: prerenderEnabled,
503
- staticRouteTypesGeneration: resolvedOptions.staticRouteTypesGeneration,
504
- include: resolvedOptions.include,
505
- exclude: resolvedOptions.exclude,
477
+ buildEnv: options?.buildEnv,
478
+ preset,
506
479
  }),
507
480
  );
508
481