@rangojs/router 0.0.0-experimental.259 → 0.0.0-experimental.27

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 (225) hide show
  1. package/README.md +294 -28
  2. package/dist/bin/rango.js +355 -47
  3. package/dist/vite/index.js +1658 -1239
  4. package/package.json +3 -3
  5. package/skills/cache-guide/SKILL.md +9 -5
  6. package/skills/caching/SKILL.md +4 -4
  7. package/skills/document-cache/SKILL.md +2 -2
  8. package/skills/hooks/SKILL.md +40 -29
  9. package/skills/host-router/SKILL.md +218 -0
  10. package/skills/intercept/SKILL.md +79 -0
  11. package/skills/layout/SKILL.md +62 -2
  12. package/skills/loader/SKILL.md +229 -15
  13. package/skills/middleware/SKILL.md +109 -30
  14. package/skills/parallel/SKILL.md +57 -2
  15. package/skills/prerender/SKILL.md +189 -19
  16. package/skills/rango/SKILL.md +1 -2
  17. package/skills/response-routes/SKILL.md +3 -3
  18. package/skills/route/SKILL.md +44 -3
  19. package/skills/router-setup/SKILL.md +80 -3
  20. package/skills/theme/SKILL.md +5 -4
  21. package/skills/typesafety/SKILL.md +59 -16
  22. package/skills/use-cache/SKILL.md +16 -2
  23. package/src/__internal.ts +1 -1
  24. package/src/bin/rango.ts +56 -19
  25. package/src/browser/action-coordinator.ts +97 -0
  26. package/src/browser/event-controller.ts +29 -48
  27. package/src/browser/history-state.ts +80 -0
  28. package/src/browser/intercept-utils.ts +1 -1
  29. package/src/browser/link-interceptor.ts +19 -3
  30. package/src/browser/merge-segment-loaders.ts +9 -2
  31. package/src/browser/navigation-bridge.ts +66 -443
  32. package/src/browser/navigation-client.ts +34 -62
  33. package/src/browser/navigation-store.ts +4 -33
  34. package/src/browser/navigation-transaction.ts +295 -0
  35. package/src/browser/partial-update.ts +103 -151
  36. package/src/browser/prefetch/cache.ts +67 -0
  37. package/src/browser/prefetch/fetch.ts +137 -0
  38. package/src/browser/prefetch/observer.ts +65 -0
  39. package/src/browser/prefetch/policy.ts +42 -0
  40. package/src/browser/prefetch/queue.ts +88 -0
  41. package/src/browser/rango-state.ts +112 -0
  42. package/src/browser/react/Link.tsx +154 -44
  43. package/src/browser/react/NavigationProvider.tsx +32 -0
  44. package/src/browser/react/context.ts +6 -0
  45. package/src/browser/react/filter-segment-order.ts +11 -0
  46. package/src/browser/react/index.ts +2 -6
  47. package/src/browser/react/location-state-shared.ts +29 -11
  48. package/src/browser/react/location-state.ts +6 -4
  49. package/src/browser/react/nonce-context.ts +23 -0
  50. package/src/browser/react/shallow-equal.ts +27 -0
  51. package/src/browser/react/use-action.ts +23 -45
  52. package/src/browser/react/use-client-cache.ts +5 -3
  53. package/src/browser/react/use-handle.ts +21 -64
  54. package/src/browser/react/use-navigation.ts +7 -32
  55. package/src/browser/react/use-params.ts +5 -34
  56. package/src/browser/react/use-pathname.ts +2 -3
  57. package/src/browser/react/use-router.ts +3 -6
  58. package/src/browser/react/use-search-params.ts +2 -1
  59. package/src/browser/react/use-segments.ts +75 -114
  60. package/src/browser/response-adapter.ts +73 -0
  61. package/src/browser/rsc-router.tsx +46 -22
  62. package/src/browser/scroll-restoration.ts +10 -7
  63. package/src/browser/server-action-bridge.ts +458 -405
  64. package/src/browser/types.ts +21 -35
  65. package/src/browser/validate-redirect-origin.ts +29 -0
  66. package/src/build/generate-manifest.ts +38 -13
  67. package/src/build/generate-route-types.ts +4 -0
  68. package/src/build/index.ts +1 -0
  69. package/src/build/route-trie.ts +19 -3
  70. package/src/build/route-types/codegen.ts +13 -4
  71. package/src/build/route-types/include-resolution.ts +13 -0
  72. package/src/build/route-types/per-module-writer.ts +15 -3
  73. package/src/build/route-types/router-processing.ts +170 -18
  74. package/src/build/runtime-discovery.ts +13 -1
  75. package/src/cache/background-task.ts +34 -0
  76. package/src/cache/cache-key-utils.ts +44 -0
  77. package/src/cache/cache-policy.ts +125 -0
  78. package/src/cache/cache-runtime.ts +136 -123
  79. package/src/cache/cache-scope.ts +76 -83
  80. package/src/cache/cf/cf-cache-store.ts +12 -7
  81. package/src/cache/document-cache.ts +93 -69
  82. package/src/cache/handle-capture.ts +81 -0
  83. package/src/cache/index.ts +0 -15
  84. package/src/cache/memory-segment-store.ts +43 -69
  85. package/src/cache/profile-registry.ts +43 -8
  86. package/src/cache/read-through-swr.ts +134 -0
  87. package/src/cache/segment-codec.ts +140 -117
  88. package/src/cache/taint.ts +30 -3
  89. package/src/cache/types.ts +1 -115
  90. package/src/client.rsc.tsx +0 -1
  91. package/src/client.tsx +53 -76
  92. package/src/errors.ts +6 -1
  93. package/src/handle.ts +1 -1
  94. package/src/handles/MetaTags.tsx +5 -2
  95. package/src/host/cookie-handler.ts +8 -3
  96. package/src/host/index.ts +0 -3
  97. package/src/host/router.ts +14 -1
  98. package/src/href-client.ts +3 -1
  99. package/src/index.rsc.ts +53 -10
  100. package/src/index.ts +73 -43
  101. package/src/loader.rsc.ts +12 -4
  102. package/src/loader.ts +8 -0
  103. package/src/prerender/store.ts +60 -18
  104. package/src/prerender.ts +76 -18
  105. package/src/reverse.ts +11 -7
  106. package/src/root-error-boundary.tsx +30 -26
  107. package/src/route-definition/dsl-helpers.ts +9 -6
  108. package/src/route-definition/index.ts +0 -3
  109. package/src/route-definition/redirect.ts +15 -3
  110. package/src/route-map-builder.ts +38 -2
  111. package/src/route-name.ts +53 -0
  112. package/src/route-types.ts +7 -0
  113. package/src/router/content-negotiation.ts +1 -1
  114. package/src/router/debug-manifest.ts +16 -3
  115. package/src/router/handler-context.ts +96 -17
  116. package/src/router/intercept-resolution.ts +6 -4
  117. package/src/router/lazy-includes.ts +4 -0
  118. package/src/router/loader-resolution.ts +6 -11
  119. package/src/router/logging.ts +100 -3
  120. package/src/router/manifest.ts +32 -3
  121. package/src/router/match-api.ts +62 -54
  122. package/src/router/match-context.ts +3 -0
  123. package/src/router/match-handlers.ts +185 -11
  124. package/src/router/match-middleware/background-revalidation.ts +65 -85
  125. package/src/router/match-middleware/cache-lookup.ts +78 -10
  126. package/src/router/match-middleware/cache-store.ts +2 -0
  127. package/src/router/match-pipelines.ts +8 -43
  128. package/src/router/match-result.ts +0 -9
  129. package/src/router/metrics.ts +233 -13
  130. package/src/router/middleware-types.ts +34 -39
  131. package/src/router/middleware.ts +290 -130
  132. package/src/router/pattern-matching.ts +61 -10
  133. package/src/router/prerender-match.ts +36 -6
  134. package/src/router/preview-match.ts +7 -1
  135. package/src/router/revalidation.ts +71 -3
  136. package/src/router/router-context.ts +15 -0
  137. package/src/router/router-interfaces.ts +158 -40
  138. package/src/router/router-options.ts +223 -1
  139. package/src/router/router-registry.ts +5 -2
  140. package/src/router/segment-resolution/fresh.ts +165 -242
  141. package/src/router/segment-resolution/helpers.ts +263 -0
  142. package/src/router/segment-resolution/loader-cache.ts +102 -98
  143. package/src/router/segment-resolution/revalidation.ts +394 -272
  144. package/src/router/segment-resolution/static-store.ts +2 -2
  145. package/src/router/segment-resolution.ts +1 -3
  146. package/src/router/segment-wrappers.ts +3 -0
  147. package/src/router/telemetry-otel.ts +299 -0
  148. package/src/router/telemetry.ts +300 -0
  149. package/src/router/timeout.ts +148 -0
  150. package/src/router/trie-matching.ts +20 -2
  151. package/src/router/types.ts +7 -1
  152. package/src/router.ts +203 -18
  153. package/src/rsc/handler-context.ts +13 -2
  154. package/src/rsc/handler.ts +489 -438
  155. package/src/rsc/helpers.ts +125 -5
  156. package/src/rsc/index.ts +0 -20
  157. package/src/rsc/loader-fetch.ts +84 -42
  158. package/src/rsc/manifest-init.ts +3 -2
  159. package/src/rsc/origin-guard.ts +141 -0
  160. package/src/rsc/progressive-enhancement.ts +245 -19
  161. package/src/rsc/response-route-handler.ts +347 -0
  162. package/src/rsc/rsc-rendering.ts +47 -43
  163. package/src/rsc/runtime-warnings.ts +42 -0
  164. package/src/rsc/server-action.ts +166 -66
  165. package/src/rsc/ssr-setup.ts +128 -0
  166. package/src/rsc/types.ts +20 -2
  167. package/src/search-params.ts +38 -23
  168. package/src/server/context.ts +61 -7
  169. package/src/server/cookie-store.ts +190 -0
  170. package/src/server/fetchable-loader-store.ts +11 -6
  171. package/src/server/handle-store.ts +84 -12
  172. package/src/server/loader-registry.ts +11 -46
  173. package/src/server/request-context.ts +275 -49
  174. package/src/server.ts +6 -0
  175. package/src/ssr/index.tsx +67 -28
  176. package/src/static-handler.ts +7 -0
  177. package/src/theme/ThemeProvider.tsx +6 -1
  178. package/src/theme/index.ts +4 -18
  179. package/src/theme/theme-context.ts +1 -28
  180. package/src/theme/theme-script.ts +2 -1
  181. package/src/types/cache-types.ts +6 -1
  182. package/src/types/error-types.ts +3 -0
  183. package/src/types/global-namespace.ts +22 -0
  184. package/src/types/handler-context.ts +103 -16
  185. package/src/types/index.ts +1 -1
  186. package/src/types/loader-types.ts +9 -6
  187. package/src/types/route-config.ts +17 -26
  188. package/src/types/route-entry.ts +28 -0
  189. package/src/types/segments.ts +0 -5
  190. package/src/urls/include-helper.ts +49 -8
  191. package/src/urls/index.ts +1 -0
  192. package/src/urls/path-helper-types.ts +30 -12
  193. package/src/urls/path-helper.ts +17 -2
  194. package/src/urls/pattern-types.ts +21 -1
  195. package/src/urls/response-types.ts +29 -7
  196. package/src/urls/type-extraction.ts +23 -15
  197. package/src/use-loader.tsx +27 -9
  198. package/src/vite/discovery/bundle-postprocess.ts +32 -52
  199. package/src/vite/discovery/discover-routers.ts +52 -26
  200. package/src/vite/discovery/prerender-collection.ts +58 -41
  201. package/src/vite/discovery/route-types-writer.ts +7 -7
  202. package/src/vite/discovery/state.ts +7 -7
  203. package/src/vite/discovery/virtual-module-codegen.ts +5 -2
  204. package/src/vite/index.ts +10 -51
  205. package/src/vite/plugins/client-ref-dedup.ts +115 -0
  206. package/src/vite/plugins/client-ref-hashing.ts +3 -3
  207. package/src/vite/plugins/expose-internal-ids.ts +4 -3
  208. package/src/vite/plugins/refresh-cmd.ts +65 -0
  209. package/src/vite/plugins/use-cache-transform.ts +91 -3
  210. package/src/vite/plugins/version-plugin.ts +188 -18
  211. package/src/vite/rango.ts +61 -36
  212. package/src/vite/router-discovery.ts +173 -100
  213. package/src/vite/utils/prerender-utils.ts +81 -0
  214. package/src/vite/utils/shared-utils.ts +19 -9
  215. package/skills/testing/SKILL.md +0 -226
  216. package/src/browser/lru-cache.ts +0 -61
  217. package/src/browser/react/prefetch.ts +0 -27
  218. package/src/browser/request-controller.ts +0 -164
  219. package/src/cache/memory-store.ts +0 -253
  220. package/src/href-context.ts +0 -33
  221. package/src/route-definition/route-function.ts +0 -119
  222. package/src/router.gen.ts +0 -6
  223. package/src/static-handler.gen.ts +0 -5
  224. package/src/urls.gen.ts +0 -8
  225. /package/{CLAUDE.md → AGENTS.md} +0 -0
package/dist/bin/rango.js CHANGED
@@ -119,6 +119,21 @@ var init_ast_route_extraction = __esm({
119
119
  }
120
120
  });
121
121
 
122
+ // src/route-name.ts
123
+ function isAutoGeneratedRouteName(name) {
124
+ return name.split(".").some((segment) => {
125
+ return segment.startsWith(AUTO_GENERATED_ROUTE_PREFIX) || segment.startsWith(INTERNAL_INCLUDE_SCOPE_PREFIX);
126
+ });
127
+ }
128
+ var AUTO_GENERATED_ROUTE_PREFIX, INTERNAL_INCLUDE_SCOPE_PREFIX;
129
+ var init_route_name = __esm({
130
+ "src/route-name.ts"() {
131
+ "use strict";
132
+ AUTO_GENERATED_ROUTE_PREFIX = "$path_";
133
+ INTERNAL_INCLUDE_SCOPE_PREFIX = "$prefix_";
134
+ }
135
+ });
136
+
122
137
  // src/build/route-types/codegen.ts
123
138
  function generatePerModuleTypesSource(routes) {
124
139
  const valid = routes.filter(({ name }) => {
@@ -153,13 +168,16 @@ export type routes = typeof routes;
153
168
  `;
154
169
  }
155
170
  function generateRouteTypesSource(routeManifest, searchSchemas) {
156
- const entries = Object.entries(routeManifest).sort(
157
- ([a], [b]) => a.localeCompare(b)
158
- );
171
+ const entries = Object.entries(routeManifest).filter(([name]) => !isAutoGeneratedRouteName(name)).sort(([a], [b]) => a.localeCompare(b));
172
+ const filteredSearchSchemas = searchSchemas ? Object.fromEntries(
173
+ Object.entries(searchSchemas).filter(
174
+ ([name]) => !isAutoGeneratedRouteName(name)
175
+ )
176
+ ) : void 0;
159
177
  const objectBody = entries.map(([name, pattern]) => {
160
178
  const key = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name) ? name : `"${name}"`;
161
179
  const params = extractParamsFromPattern(pattern);
162
- const search = searchSchemas?.[name];
180
+ const search = filteredSearchSchemas?.[name];
163
181
  return formatRouteEntry(key, pattern, params, search);
164
182
  }).join("\n");
165
183
  return `// Auto-generated by @rangojs/router - do not edit
@@ -178,6 +196,7 @@ var init_codegen = __esm({
178
196
  "src/build/route-types/codegen.ts"() {
179
197
  "use strict";
180
198
  init_param_extraction();
199
+ init_route_name();
181
200
  }
182
201
  });
183
202
 
@@ -410,6 +429,9 @@ function buildRouteMapFromBlock(block, fullSource, filePath, visited, searchSche
410
429
  diagnosticsOut
411
430
  );
412
431
  }
432
+ if (namePrefix === null) {
433
+ continue;
434
+ }
413
435
  for (const [name, pattern] of Object.entries(childResult.routes)) {
414
436
  const prefixedName = namePrefix ? `${namePrefix}.${name}` : name;
415
437
  let prefixedPattern;
@@ -460,6 +482,7 @@ function buildCombinedRouteMapWithSearch(filePath, variableName, visited, diagno
460
482
  searchSchemas,
461
483
  diagnosticsOut
462
484
  );
485
+ visited.delete(key);
463
486
  return { routes, searchSchemas };
464
487
  }
465
488
  var init_include_resolution = __esm({
@@ -517,8 +540,16 @@ function writePerModuleRouteTypesForFile(filePath) {
517
540
  } else {
518
541
  routes = extractRoutesFromSource(source);
519
542
  }
520
- if (routes.length === 0) return;
521
543
  const genPath = filePath.replace(/\.(tsx?)$/, ".gen.ts");
544
+ if (routes.length === 0) {
545
+ if (varNames.length > 0 && !existsSync2(genPath)) {
546
+ writeFileSync(genPath, generatePerModuleTypesSource([]));
547
+ console.log(
548
+ `[rsc-router] Generated route types (placeholder) -> ${genPath}`
549
+ );
550
+ }
551
+ return;
552
+ }
522
553
  const genSource = generatePerModuleTypesSource(routes);
523
554
  const existing = existsSync2(genPath) ? readFileSync2(genPath, "utf-8") : null;
524
555
  if (existing !== genSource) {
@@ -543,9 +574,102 @@ var init_per_module_writer = __esm({
543
574
  });
544
575
 
545
576
  // src/build/route-types/router-processing.ts
546
- import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, existsSync as existsSync3, unlinkSync } from "node:fs";
547
- import { join as join2, dirname as dirname2, resolve as resolve2, basename as pathBasename } from "node:path";
577
+ import {
578
+ readFileSync as readFileSync3,
579
+ writeFileSync as writeFileSync2,
580
+ existsSync as existsSync3,
581
+ unlinkSync,
582
+ readdirSync as readdirSync2
583
+ } from "node:fs";
584
+ import {
585
+ join as join2,
586
+ dirname as dirname2,
587
+ resolve as resolve2,
588
+ sep,
589
+ basename as pathBasename
590
+ } from "node:path";
548
591
  import ts5 from "typescript";
592
+ function countPublicRouteEntries(source) {
593
+ const matches = source.matchAll(/^\s+(?:"([^"]+)"|([a-zA-Z_$][^:]*)):\s*["{]/gm) ?? [];
594
+ let count = 0;
595
+ for (const match of matches) {
596
+ const routeName = match[1] || match[2];
597
+ if (routeName && !isAutoGeneratedRouteName(routeName.trim())) {
598
+ count++;
599
+ }
600
+ }
601
+ return count;
602
+ }
603
+ function isRoutableSourceFile(name) {
604
+ return (name.endsWith(".ts") || name.endsWith(".tsx") || name.endsWith(".js") || name.endsWith(".jsx")) && !name.includes(".gen.");
605
+ }
606
+ function findRouterFilesRecursive(dir, filter, results) {
607
+ let entries;
608
+ try {
609
+ entries = readdirSync2(dir, { withFileTypes: true });
610
+ } catch (err) {
611
+ console.warn(
612
+ `[rsc-router] Failed to scan directory ${dir}: ${err.message}`
613
+ );
614
+ return;
615
+ }
616
+ const childDirs = [];
617
+ const routerFilesInDir = [];
618
+ for (const entry of entries) {
619
+ const fullPath = join2(dir, entry.name);
620
+ if (entry.isDirectory()) {
621
+ if (entry.name === "node_modules" || entry.name.startsWith(".")) continue;
622
+ childDirs.push(fullPath);
623
+ continue;
624
+ }
625
+ if (!isRoutableSourceFile(entry.name)) continue;
626
+ if (filter && !filter(fullPath)) continue;
627
+ try {
628
+ const source = readFileSync3(fullPath, "utf-8");
629
+ if (ROUTER_CALL_PATTERN.test(source)) {
630
+ routerFilesInDir.push(fullPath);
631
+ }
632
+ } catch {
633
+ continue;
634
+ }
635
+ }
636
+ if (routerFilesInDir.length > 0) {
637
+ results.push(...routerFilesInDir);
638
+ return;
639
+ }
640
+ for (const childDir of childDirs) {
641
+ findRouterFilesRecursive(childDir, filter, results);
642
+ }
643
+ }
644
+ function findNestedRouterConflict(routerFiles) {
645
+ const routerDirs = [
646
+ ...new Set(routerFiles.map((filePath) => dirname2(resolve2(filePath))))
647
+ ].sort((a, b) => a.length - b.length);
648
+ for (let i = 0; i < routerDirs.length; i++) {
649
+ const ancestorDir = routerDirs[i];
650
+ const prefix = ancestorDir.endsWith(sep) ? ancestorDir : `${ancestorDir}${sep}`;
651
+ for (let j = i + 1; j < routerDirs.length; j++) {
652
+ const nestedDir = routerDirs[j];
653
+ if (!nestedDir.startsWith(prefix)) continue;
654
+ const ancestorFile = routerFiles.find(
655
+ (filePath) => dirname2(resolve2(filePath)) === ancestorDir
656
+ );
657
+ const nestedFile = routerFiles.find(
658
+ (filePath) => dirname2(resolve2(filePath)) === nestedDir
659
+ );
660
+ if (ancestorFile && nestedFile) {
661
+ return { ancestor: ancestorFile, nested: nestedFile };
662
+ }
663
+ }
664
+ }
665
+ return null;
666
+ }
667
+ function formatNestedRouterConflictError(conflict, prefix = "[rsc-router]") {
668
+ return `${prefix} Nested router roots are not supported.
669
+ Router root: ${conflict.ancestor}
670
+ Nested router: ${conflict.nested}
671
+ Move the nested router into a sibling directory or configure it as a separate app root.`;
672
+ }
549
673
  function extractUrlsVariableFromRouter(code) {
550
674
  const sourceFile = ts5.createSourceFile(
551
675
  "router.tsx",
@@ -652,20 +776,25 @@ function detectUnresolvableIncludes(routerFilePath) {
652
776
  );
653
777
  return diagnostics;
654
778
  }
779
+ function detectUnresolvableIncludesForUrlsFile(filePath) {
780
+ const realPath = resolve2(filePath);
781
+ let source;
782
+ try {
783
+ source = readFileSync3(realPath, "utf-8");
784
+ } catch {
785
+ return [];
786
+ }
787
+ const varNames = findUrlsVariableNames(source);
788
+ if (varNames.length === 0) return [];
789
+ const diagnostics = [];
790
+ for (const varName of varNames) {
791
+ buildCombinedRouteMapWithSearch(realPath, varName, /* @__PURE__ */ new Set(), diagnostics);
792
+ }
793
+ return diagnostics;
794
+ }
655
795
  function findRouterFiles(root, filter) {
656
- const files = findTsFiles(root, filter);
657
796
  const result = [];
658
- for (const filePath of files) {
659
- if (filePath.includes(".gen.")) continue;
660
- try {
661
- const source = readFileSync3(filePath, "utf-8");
662
- if (/\bcreateRouter\s*[<(]/.test(source)) {
663
- result.push(filePath);
664
- }
665
- } catch {
666
- continue;
667
- }
668
- }
797
+ findRouterFilesRecursive(root, filter, result);
669
798
  return result;
670
799
  }
671
800
  function writeCombinedRouteTypes(root, knownRouterFiles, opts) {
@@ -681,6 +810,10 @@ function writeCombinedRouteTypes(root, knownRouterFiles, opts) {
681
810
  }
682
811
  const routerFilePaths = knownRouterFiles ?? findRouterFiles(root);
683
812
  if (routerFilePaths.length === 0) return;
813
+ const nestedRouterConflict = findNestedRouterConflict(routerFilePaths);
814
+ if (nestedRouterConflict) {
815
+ throw new Error(formatNestedRouterConflictError(nestedRouterConflict));
816
+ }
684
817
  for (const routerFilePath of routerFilePaths) {
685
818
  let routerSource;
686
819
  try {
@@ -725,8 +858,10 @@ function writeCombinedRouteTypes(root, knownRouterFiles, opts) {
725
858
  );
726
859
  if (existing !== source) {
727
860
  if (opts?.preserveIfLarger && existing) {
728
- const existingCount = (existing.match(/^\s+["a-zA-Z_$][^:]*:\s*["{]/gm) || []).length;
729
- const newCount = Object.keys(result.routes).length;
861
+ const existingCount = countPublicRouteEntries(existing);
862
+ const newCount = Object.keys(result.routes).filter(
863
+ (name) => !isAutoGeneratedRouteName(name)
864
+ ).length;
730
865
  if (existingCount > newCount) {
731
866
  continue;
732
867
  }
@@ -738,12 +873,15 @@ function writeCombinedRouteTypes(root, knownRouterFiles, opts) {
738
873
  }
739
874
  }
740
875
  }
876
+ var ROUTER_CALL_PATTERN;
741
877
  var init_router_processing = __esm({
742
878
  "src/build/route-types/router-processing.ts"() {
743
879
  "use strict";
744
880
  init_codegen();
745
- init_scan_filter();
746
881
  init_include_resolution();
882
+ init_per_module_writer();
883
+ init_route_name();
884
+ ROUTER_CALL_PATTERN = /\bcreateRouter\s*[<(]/;
747
885
  }
748
886
  });
749
887
 
@@ -758,6 +896,7 @@ var init_generate_route_types = __esm({
758
896
  init_per_module_writer();
759
897
  init_include_resolution();
760
898
  init_router_processing();
899
+ init_per_module_writer();
761
900
  }
762
901
  });
763
902
 
@@ -829,11 +968,111 @@ var version_plugin_exports = {};
829
968
  __export(version_plugin_exports, {
830
969
  createVersionPlugin: () => createVersionPlugin
831
970
  });
971
+ import { parseAst } from "vite";
972
+ function isCodeModule(id) {
973
+ return /\.(tsx?|jsx?)($|\?)/.test(id);
974
+ }
975
+ function normalizeModuleId(id) {
976
+ return id.split("?", 1)[0];
977
+ }
978
+ function getClientModuleSignature(source) {
979
+ let program;
980
+ try {
981
+ program = parseAst(source, { jsx: true });
982
+ } catch {
983
+ return void 0;
984
+ }
985
+ let isUseClient = false;
986
+ for (const node of program.body ?? []) {
987
+ if (node?.type === "ExpressionStatement" && node.expression?.type === "Literal" && typeof node.expression.value === "string") {
988
+ if (node.expression.value === "use client") {
989
+ isUseClient = true;
990
+ }
991
+ continue;
992
+ }
993
+ break;
994
+ }
995
+ if (!isUseClient) return void 0;
996
+ const exports = /* @__PURE__ */ new Set();
997
+ let hasDefault = false;
998
+ let hasExportAll = false;
999
+ const collectBindingNames = (pattern) => {
1000
+ if (!pattern) return;
1001
+ if (pattern.type === "Identifier") {
1002
+ exports.add(pattern.name);
1003
+ } else if (pattern.type === "ObjectPattern") {
1004
+ for (const prop of pattern.properties ?? []) {
1005
+ if (prop?.type === "RestElement") {
1006
+ collectBindingNames(prop.argument);
1007
+ } else {
1008
+ collectBindingNames(prop?.value);
1009
+ }
1010
+ }
1011
+ } else if (pattern.type === "ArrayPattern") {
1012
+ for (const el of pattern.elements ?? []) {
1013
+ if (el?.type === "RestElement") {
1014
+ collectBindingNames(el.argument);
1015
+ } else {
1016
+ collectBindingNames(el);
1017
+ }
1018
+ }
1019
+ }
1020
+ };
1021
+ const collectDeclarationNames = (declaration) => {
1022
+ if (!declaration) return;
1023
+ if (declaration.type === "VariableDeclaration") {
1024
+ for (const decl of declaration.declarations ?? []) {
1025
+ collectBindingNames(decl?.id);
1026
+ }
1027
+ return;
1028
+ }
1029
+ collectBindingNames(declaration.id);
1030
+ };
1031
+ for (const node of program.body ?? []) {
1032
+ if (node?.type === "ExportDefaultDeclaration") {
1033
+ hasDefault = true;
1034
+ continue;
1035
+ }
1036
+ if (node?.type === "ExportAllDeclaration") {
1037
+ hasExportAll = true;
1038
+ continue;
1039
+ }
1040
+ if (node?.type !== "ExportNamedDeclaration") continue;
1041
+ collectDeclarationNames(node.declaration);
1042
+ for (const specifier of node.specifiers ?? []) {
1043
+ const exportedName = specifier?.exported?.name ?? specifier?.exported?.value;
1044
+ if (exportedName === "default") {
1045
+ hasDefault = true;
1046
+ } else if (typeof exportedName === "string") {
1047
+ exports.add(exportedName);
1048
+ }
1049
+ }
1050
+ }
1051
+ return {
1052
+ key: JSON.stringify({
1053
+ default: hasDefault,
1054
+ exportAll: hasExportAll,
1055
+ exports: [...exports].sort()
1056
+ })
1057
+ };
1058
+ }
832
1059
  function createVersionPlugin() {
833
1060
  const buildVersion = Date.now().toString(16);
834
1061
  let currentVersion = buildVersion;
835
1062
  let isDev = false;
836
1063
  let server = null;
1064
+ const clientModuleSignatures = /* @__PURE__ */ new Map();
1065
+ const bumpVersion = (reason) => {
1066
+ currentVersion = Date.now().toString(16);
1067
+ console.log(`[rsc-router] ${reason}, version updated: ${currentVersion}`);
1068
+ const rscEnv = server?.environments?.rsc;
1069
+ const versionMod = rscEnv?.moduleGraph?.getModuleById(
1070
+ "\0" + VIRTUAL_IDS.version
1071
+ );
1072
+ if (versionMod) {
1073
+ rscEnv.moduleGraph.invalidateModule(versionMod);
1074
+ }
1075
+ };
837
1076
  return {
838
1077
  name: "@rangojs/router:version",
839
1078
  enforce: "pre",
@@ -842,6 +1081,12 @@ function createVersionPlugin() {
842
1081
  },
843
1082
  configureServer(devServer) {
844
1083
  server = devServer;
1084
+ devServer.watcher.on("unlink", (filePath) => {
1085
+ if (!isDev) return;
1086
+ if (!clientModuleSignatures.has(filePath)) return;
1087
+ clientModuleSignatures.delete(filePath);
1088
+ bumpVersion("Client module removed");
1089
+ });
845
1090
  },
846
1091
  resolveId(id) {
847
1092
  if (id === VIRTUAL_IDS.version) {
@@ -855,27 +1100,48 @@ function createVersionPlugin() {
855
1100
  }
856
1101
  return null;
857
1102
  },
1103
+ transform(code, id) {
1104
+ if (!isDev || !isCodeModule(id)) return null;
1105
+ const normalizedId = normalizeModuleId(id);
1106
+ if (!code.includes("use client") && !clientModuleSignatures.has(normalizedId)) {
1107
+ return null;
1108
+ }
1109
+ const signature = getClientModuleSignature(code);
1110
+ if (signature) {
1111
+ clientModuleSignatures.set(normalizedId, signature);
1112
+ } else {
1113
+ clientModuleSignatures.delete(normalizedId);
1114
+ }
1115
+ return null;
1116
+ },
858
1117
  // Track RSC module changes and update version
859
- hotUpdate(ctx) {
1118
+ async hotUpdate(ctx) {
860
1119
  if (!isDev) return;
861
1120
  const isRscModule = this.environment?.name === "rsc";
862
- if (isRscModule && ctx.modules.length > 0) {
863
- currentVersion = Date.now().toString(16);
864
- console.log(
865
- `[rsc-router] RSC module changed, version updated: ${currentVersion}`
866
- );
867
- if (server) {
868
- const rscEnv = server.environments?.rsc;
869
- if (rscEnv?.moduleGraph) {
870
- const versionMod = rscEnv.moduleGraph.getModuleById(
871
- "\0" + VIRTUAL_IDS.version
872
- );
873
- if (versionMod) {
874
- rscEnv.moduleGraph.invalidateModule(versionMod);
1121
+ if (!isRscModule) return;
1122
+ if (isCodeModule(ctx.file)) {
1123
+ const filePath = normalizeModuleId(ctx.file);
1124
+ const previousSignature = clientModuleSignatures.get(filePath);
1125
+ try {
1126
+ const source = await ctx.read();
1127
+ const nextSignature = getClientModuleSignature(source);
1128
+ if (nextSignature) {
1129
+ clientModuleSignatures.set(filePath, nextSignature);
1130
+ if (previousSignature && previousSignature.key === nextSignature.key) {
1131
+ return;
1132
+ }
1133
+ } else {
1134
+ clientModuleSignatures.delete(filePath);
1135
+ if (!previousSignature) {
1136
+ if (ctx.modules.length === 0) return;
875
1137
  }
876
1138
  }
1139
+ } catch {
877
1140
  }
1141
+ } else {
1142
+ if (ctx.modules.length === 0) return;
878
1143
  }
1144
+ bumpVersion("RSC module changed");
879
1145
  }
880
1146
  };
881
1147
  }
@@ -1012,7 +1278,13 @@ async function discoverAndWriteRouteTypes(opts) {
1012
1278
  if (!router.urlpatterns) continue;
1013
1279
  const manifest = generateManifest(router.urlpatterns, routerMountIndex);
1014
1280
  routerMountIndex++;
1015
- const routeManifest = manifest.routeManifest;
1281
+ const rawManifest = manifest.routeManifest;
1282
+ const routeManifest = {};
1283
+ for (const [name, pattern] of Object.entries(rawManifest)) {
1284
+ if (!isAutoGeneratedRouteName(name)) {
1285
+ routeManifest[name] = pattern;
1286
+ }
1287
+ }
1016
1288
  let routeSearchSchemas = manifest.routeSearchSchemas;
1017
1289
  const sourceFile = router.__sourceFile;
1018
1290
  if (!sourceFile) {
@@ -1074,6 +1346,7 @@ var init_runtime_discovery = __esm({
1074
1346
  "src/build/runtime-discovery.ts"() {
1075
1347
  "use strict";
1076
1348
  init_generate_route_types();
1349
+ init_route_name();
1077
1350
  }
1078
1351
  });
1079
1352
 
@@ -1178,16 +1451,15 @@ function runStaticGeneration(args, mode) {
1178
1451
  process.exit(0);
1179
1452
  }
1180
1453
  const routerFiles = [];
1454
+ const urlsFiles = [];
1181
1455
  for (const filePath of files) {
1182
1456
  try {
1183
1457
  const source = readFileSync5(filePath, "utf-8");
1184
- const isRouter = /\bcreateRouter\s*[<(]/.test(source);
1185
- const isUrls = source.includes("urls(");
1186
- if (isRouter) {
1458
+ if (/\bcreateRouter\s*[<(]/.test(source)) {
1187
1459
  routerFiles.push(filePath);
1188
1460
  }
1189
- if (isUrls) {
1190
- writePerModuleRouteTypesForFile(filePath);
1461
+ if (source.includes("urls(")) {
1462
+ urlsFiles.push(filePath);
1191
1463
  }
1192
1464
  } catch (err) {
1193
1465
  console.warn(
@@ -1202,21 +1474,48 @@ function runStaticGeneration(args, mode) {
1202
1474
  allDiagnostics.push({ ...d, routerFile });
1203
1475
  }
1204
1476
  }
1205
- if (allDiagnostics.length > 0 && mode === "default") {
1477
+ const routerFileSet = new Set(routerFiles);
1478
+ for (const urlsFile of urlsFiles) {
1479
+ if (routerFileSet.has(urlsFile)) continue;
1480
+ const diagnostics = detectUnresolvableIncludesForUrlsFile(urlsFile);
1481
+ for (const d of diagnostics) {
1482
+ allDiagnostics.push({ ...d, routerFile: urlsFile });
1483
+ }
1484
+ }
1485
+ const seen = /* @__PURE__ */ new Set();
1486
+ const uniqueDiagnostics = allDiagnostics.filter((d) => {
1487
+ const key = `${d.sourceFile}:${d.pathPrefix}:${d.reason}`;
1488
+ if (seen.has(key)) return false;
1489
+ seen.add(key);
1490
+ return true;
1491
+ });
1492
+ if (uniqueDiagnostics.length > 0 && mode === "default") {
1206
1493
  console.error("\n[rango] Unresolvable includes detected:\n");
1207
- formatDiagnostics(allDiagnostics);
1494
+ formatDiagnostics(uniqueDiagnostics);
1208
1495
  console.error(
1209
1496
  "\nThe static parser cannot resolve these includes because they use factory functions or dynamic expressions.\n\nOptions:\n rango generate <path> --runtime Use Vite-based discovery (requires vite)\n rango generate <path> --static Accept partial output (missing routes above)\n"
1210
1497
  );
1211
1498
  process.exit(1);
1212
1499
  }
1213
- if (allDiagnostics.length > 0 && mode === "static") {
1500
+ if (uniqueDiagnostics.length > 0 && mode === "static") {
1214
1501
  console.warn(
1215
1502
  "\n[rango] Warning: partial output (unresolvable includes):\n"
1216
1503
  );
1217
- formatDiagnostics(allDiagnostics);
1504
+ formatDiagnostics(uniqueDiagnostics);
1218
1505
  console.warn("");
1219
1506
  }
1507
+ const nestedRouterConflict = findNestedRouterConflict(routerFiles);
1508
+ if (nestedRouterConflict) {
1509
+ console.error(
1510
+ `
1511
+ ${formatNestedRouterConflictError(nestedRouterConflict, "[rango]")}
1512
+ `
1513
+ );
1514
+ process.exit(1);
1515
+ }
1516
+ for (const urlsFile of urlsFiles) {
1517
+ writePerModuleRouteTypesForFile(urlsFile);
1518
+ }
1220
1519
  for (const routerFile of routerFiles) {
1221
1520
  const projectRoot = findProjectRoot(routerFile);
1222
1521
  writeCombinedRouteTypes(projectRoot, [routerFile]);
@@ -1257,6 +1556,15 @@ async function runRuntimeDiscovery(args, configFile) {
1257
1556
  console.error("[rango] No router files found in the provided paths");
1258
1557
  process.exit(1);
1259
1558
  }
1559
+ const nestedRouterConflict = findNestedRouterConflict(routerEntries);
1560
+ if (nestedRouterConflict) {
1561
+ console.error(
1562
+ `
1563
+ ${formatNestedRouterConflictError(nestedRouterConflict, "[rango]")}
1564
+ `
1565
+ );
1566
+ process.exit(1);
1567
+ }
1260
1568
  let discoverAndWriteRouteTypes2;
1261
1569
  try {
1262
1570
  const mod = await Promise.resolve().then(() => (init_runtime_discovery(), runtime_discovery_exports));
@@ -1271,8 +1579,8 @@ async function runRuntimeDiscovery(args, configFile) {
1271
1579
  }
1272
1580
  process.exit(1);
1273
1581
  }
1274
- const projectRoot = findProjectRoot(routerEntries[0]);
1275
1582
  for (const entry of routerEntries) {
1583
+ const projectRoot = findProjectRoot(entry);
1276
1584
  const result = await discoverAndWriteRouteTypes2({
1277
1585
  root: projectRoot,
1278
1586
  configFile,