@rangojs/router 0.5.2 → 0.6.0

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 (173) hide show
  1. package/dist/bin/rango.js +343 -125
  2. package/dist/types/browser/react/use-router.d.ts +10 -3
  3. package/dist/types/browser/react/use-search-params.d.ts +57 -10
  4. package/dist/types/browser/types.d.ts +22 -0
  5. package/dist/types/build/merge-full-manifests.d.ts +3 -0
  6. package/dist/types/build/route-trie.d.ts +4 -73
  7. package/dist/types/build/route-types/per-module-writer.d.ts +6 -4
  8. package/dist/types/build/route-types/router-processing.d.ts +2 -3
  9. package/dist/types/cache/cache-exec-scope.d.ts +31 -0
  10. package/dist/types/cache/taint.d.ts +12 -6
  11. package/dist/types/client-urls/client-root.d.ts +38 -0
  12. package/dist/types/client-urls/client-urls.d.ts +5 -0
  13. package/dist/types/client-urls/navigation.d.ts +38 -0
  14. package/dist/types/client-urls/revalidation-protocol.d.ts +25 -0
  15. package/dist/types/client-urls/server-projection.d.ts +62 -0
  16. package/dist/types/client-urls/types.d.ts +144 -0
  17. package/dist/types/client.d.ts +12 -4
  18. package/dist/types/client.rsc.d.ts +4 -1
  19. package/dist/types/decode-loader-results.d.ts +37 -0
  20. package/dist/types/errors.d.ts +1 -0
  21. package/dist/types/index.d.ts +1 -1
  22. package/dist/types/loader-redirect.d.ts +27 -0
  23. package/dist/types/outlet-context.d.ts +12 -0
  24. package/dist/types/outlet-provider.d.ts +3 -1
  25. package/dist/types/redirect-origin.d.ts +4 -0
  26. package/dist/types/route-content-wrapper.d.ts +42 -1
  27. package/dist/types/route-definition/helpers-types.d.ts +13 -2
  28. package/dist/types/router/error-handling.d.ts +35 -1
  29. package/dist/types/router/intercept-resolution.d.ts +12 -0
  30. package/dist/types/router/loader-resolution.d.ts +24 -2
  31. package/dist/types/router/revalidation.d.ts +7 -0
  32. package/dist/types/router/route-trie-builder.d.ts +77 -0
  33. package/dist/types/router/router-interfaces.d.ts +20 -0
  34. package/dist/types/router/segment-resolution/helpers.d.ts +1 -1
  35. package/dist/types/router/trie-matching.d.ts +1 -1
  36. package/dist/types/rsc/manifest-init.d.ts +5 -5
  37. package/dist/types/rsc/shell-capture.d.ts +9 -0
  38. package/dist/types/rsc/shell-serve.d.ts +11 -0
  39. package/dist/types/rsc/types.d.ts +30 -0
  40. package/dist/types/segment-system.d.ts +2 -0
  41. package/dist/types/server/context.d.ts +10 -0
  42. package/dist/types/server/handle-store.d.ts +34 -3
  43. package/dist/types/server/request-context.d.ts +11 -1
  44. package/dist/types/server.d.ts +1 -0
  45. package/dist/types/ssr/index.d.ts +22 -0
  46. package/dist/types/ssr/ssr-root.d.ts +10 -0
  47. package/dist/types/testing/dom.entry.d.ts +1 -1
  48. package/dist/types/testing/render-route.d.ts +16 -6
  49. package/dist/types/testing/run-loader.d.ts +9 -0
  50. package/dist/types/types/boundaries.d.ts +22 -0
  51. package/dist/types/types/index.d.ts +1 -1
  52. package/dist/types/types/loader-types.d.ts +57 -5
  53. package/dist/types/types/segments.d.ts +7 -0
  54. package/dist/types/urls/path-helper-types.d.ts +10 -4
  55. package/dist/types/vite/discovery/client-urls-projection.d.ts +53 -0
  56. package/dist/types/vite/discovery/discover-routers.d.ts +1 -1
  57. package/dist/types/vite/discovery/state.d.ts +8 -1
  58. package/dist/vite/index.js +5313 -2365
  59. package/package.json +1 -1
  60. package/skills/breadcrumbs/SKILL.md +39 -9
  61. package/skills/catalog.json +7 -1
  62. package/skills/client-urls/SKILL.md +338 -0
  63. package/skills/comparison/references/framework-comparison.md +23 -9
  64. package/skills/hooks/SKILL.md +2 -2
  65. package/skills/hooks/data.md +11 -2
  66. package/skills/hooks/handle-and-actions.md +7 -0
  67. package/skills/hooks/outlets.md +26 -5
  68. package/skills/hooks/urls.md +40 -3
  69. package/skills/loader/SKILL.md +132 -20
  70. package/skills/migrate-nextjs/SKILL.md +70 -10
  71. package/skills/migrate-react-router/SKILL.md +49 -13
  72. package/skills/migrate-react-router/component-migration.md +18 -13
  73. package/skills/migrate-react-router/data-and-actions.md +14 -3
  74. package/skills/migrate-react-router/route-mapping.md +15 -2
  75. package/skills/parallel/SKILL.md +32 -1
  76. package/skills/ppr/SKILL.md +16 -6
  77. package/skills/prerender/SKILL.md +8 -4
  78. package/skills/rango/SKILL.md +21 -17
  79. package/skills/react-compiler/SKILL.md +3 -3
  80. package/skills/route/SKILL.md +5 -2
  81. package/skills/router-setup/SKILL.md +16 -2
  82. package/skills/scripts/SKILL.md +16 -6
  83. package/skills/shell-manifest/SKILL.md +16 -7
  84. package/skills/testing/SKILL.md +2 -2
  85. package/skills/testing/client-components.md +6 -0
  86. package/skills/testing/handles.md +30 -8
  87. package/skills/testing/loader.md +51 -49
  88. package/skills/testing/middleware.md +1 -1
  89. package/skills/theme/SKILL.md +8 -5
  90. package/src/bin/rango.ts +7 -3
  91. package/src/browser/navigation-bridge.ts +6 -0
  92. package/src/browser/navigation-client.ts +5 -0
  93. package/src/browser/partial-update.ts +65 -13
  94. package/src/browser/react/use-router.ts +40 -11
  95. package/src/browser/react/use-search-params.ts +140 -17
  96. package/src/browser/rsc-router.tsx +59 -0
  97. package/src/browser/server-action-bridge.ts +26 -0
  98. package/src/browser/types.ts +22 -0
  99. package/src/build/merge-full-manifests.ts +161 -0
  100. package/src/build/route-trie.ts +9 -332
  101. package/src/build/route-types/include-resolution.ts +66 -11
  102. package/src/build/route-types/per-module-writer.ts +11 -6
  103. package/src/build/route-types/router-processing.ts +184 -153
  104. package/src/build/runtime-discovery.ts +23 -12
  105. package/src/cache/cache-exec-scope.ts +47 -0
  106. package/src/cache/cache-runtime.ts +24 -25
  107. package/src/cache/taint.ts +28 -9
  108. package/src/client-urls/client-root.tsx +168 -0
  109. package/src/client-urls/client-urls.ts +698 -0
  110. package/src/client-urls/navigation.ts +237 -0
  111. package/src/client-urls/revalidation-protocol.ts +56 -0
  112. package/src/client-urls/server-projection.ts +579 -0
  113. package/src/client-urls/types.ts +195 -0
  114. package/src/client.rsc.tsx +12 -0
  115. package/src/client.tsx +49 -6
  116. package/src/decode-loader-results.ts +113 -0
  117. package/src/errors.ts +14 -0
  118. package/src/handles/deferred-resolution.ts +14 -7
  119. package/src/index.ts +1 -0
  120. package/src/loader-redirect.tsx +64 -0
  121. package/src/outlet-context.ts +12 -0
  122. package/src/outlet-provider.tsx +15 -1
  123. package/src/redirect-origin.ts +29 -0
  124. package/src/route-content-wrapper.tsx +96 -3
  125. package/src/route-definition/dsl-helpers.ts +28 -3
  126. package/src/route-definition/helpers-types.ts +13 -0
  127. package/src/route-definition/redirect.ts +17 -18
  128. package/src/router/error-handling.ts +65 -11
  129. package/src/router/intercept-resolution.ts +29 -0
  130. package/src/router/loader-resolution.ts +261 -28
  131. package/src/router/match-result.ts +7 -0
  132. package/src/router/revalidation.ts +24 -11
  133. package/src/router/route-trie-builder.ts +334 -0
  134. package/src/router/router-interfaces.ts +38 -0
  135. package/src/router/segment-resolution/fresh.ts +47 -0
  136. package/src/router/segment-resolution/helpers.ts +9 -11
  137. package/src/router/segment-resolution/loader-cache.ts +14 -24
  138. package/src/router/segment-resolution/revalidation.ts +20 -1
  139. package/src/router/trie-matching.ts +3 -3
  140. package/src/router.ts +46 -1
  141. package/src/rsc/full-payload.ts +6 -0
  142. package/src/rsc/handler.ts +10 -7
  143. package/src/rsc/loader-fetch.ts +2 -2
  144. package/src/rsc/manifest-init.ts +28 -9
  145. package/src/rsc/rsc-rendering.ts +15 -1
  146. package/src/rsc/shell-capture.ts +12 -0
  147. package/src/rsc/shell-serve.ts +15 -2
  148. package/src/rsc/ssr-setup.ts +10 -1
  149. package/src/rsc/types.ts +31 -2
  150. package/src/segment-system.tsx +83 -26
  151. package/src/server/context.ts +10 -0
  152. package/src/server/cookie-store.ts +19 -19
  153. package/src/server/handle-store.ts +185 -48
  154. package/src/server/request-context.ts +30 -6
  155. package/src/server.ts +7 -0
  156. package/src/ssr/index.tsx +37 -2
  157. package/src/ssr/ssr-root.tsx +29 -2
  158. package/src/testing/dom.entry.ts +1 -1
  159. package/src/testing/render-route.tsx +22 -8
  160. package/src/testing/run-loader.ts +51 -13
  161. package/src/types/boundaries.ts +19 -0
  162. package/src/types/index.ts +1 -0
  163. package/src/types/loader-types.ts +60 -5
  164. package/src/types/segments.ts +7 -0
  165. package/src/urls/include-helper.ts +22 -4
  166. package/src/urls/path-helper-types.ts +14 -1
  167. package/src/use-loader.tsx +67 -6
  168. package/src/vite/discovery/client-urls-projection.ts +322 -0
  169. package/src/vite/discovery/discover-routers.ts +43 -17
  170. package/src/vite/discovery/state.ts +11 -1
  171. package/src/vite/discovery/virtual-module-codegen.ts +20 -0
  172. package/src/vite/plugins/virtual-entries.ts +12 -3
  173. package/src/vite/router-discovery.ts +163 -12
@@ -59,6 +59,10 @@ import {
59
59
  peekSelfGenWrite,
60
60
  } from "./discovery/self-gen-tracking.js";
61
61
  import { discoverRouters } from "./discovery/discover-routers.js";
62
+ import {
63
+ recordClientUrlsModule,
64
+ refreshRecordedClientUrlProjections,
65
+ } from "./discovery/client-urls-projection.js";
62
66
  import { runShellPrerenderPhase } from "./discovery/shell-prerender-phase.js";
63
67
  import { describeDiscoveryFailure } from "./discovery/discovery-errors.js";
64
68
  import {
@@ -273,6 +277,13 @@ async function createTempRscServer(
273
277
  // runtime. forceBuild produces hashed IDs for production bundle consistency.
274
278
  exposeInternalIds(options.forceBuild ? { forceBuild: true } : undefined),
275
279
  exposeRouterId(),
280
+ {
281
+ name: "@rangojs/router:client-urls-source-tracking",
282
+ enforce: "pre",
283
+ transform(code, id) {
284
+ recordClientUrlsModule(state, code, id);
285
+ },
286
+ },
276
287
  // Forwarded user resolution plugins (e.g. vite-tsconfig-paths). Stripped
277
288
  // to resolveId/load and placed last so framework resolution runs first;
278
289
  // Vite re-sorts by `enforce`, so `enforce: "pre"` resolvers still lead.
@@ -427,8 +438,12 @@ export function createRouterDiscoveryPlugin(
427
438
  // internal-debug module so FE debug no longer depends on Vite delivering the
428
439
  // `__RANGO_DEBUG__` define to the client (which it does only as an injected
429
440
  // global whose presence varies across consumer setups). Runs in dev and build.
430
- transform(_code, id) {
431
- return injectClientDebugFlag(id);
441
+ transform: {
442
+ order: "pre",
443
+ handler(code, id) {
444
+ recordClientUrlsModule(s, code, id);
445
+ return injectClientDebugFlag(id);
446
+ },
432
447
  },
433
448
 
434
449
  configResolved(config) {
@@ -880,7 +895,11 @@ export function createRouterDiscoveryPlugin(
880
895
  optimizerHashBefore =
881
896
  tempRscEnv.depsOptimizer?.metadata?.browserHash;
882
897
  await timed(debugDiscovery, "discoverRouters (cloudflare)", () =>
883
- discoverRouters(s, tempRscEnv),
898
+ discoverRouters(
899
+ s,
900
+ tempRscEnv,
901
+ (prerenderTempServer?.environments as any)?.ssr,
902
+ ),
884
903
  );
885
904
  timedSync(debugDiscovery, "writeRouteTypesFiles", () =>
886
905
  writeRouteTypesFiles(s),
@@ -935,7 +954,7 @@ export function createRouterDiscoveryPlugin(
935
954
  // requests during discovery on the Node path, so arming
936
955
  // manifestReadyPromise after discovery is sufficient here.
937
956
  const serverMod = await timed(debugDiscovery, "discoverRouters", () =>
938
- discoverRouters(s, rscEnv),
957
+ discoverRouters(s, rscEnv, (server.environments as any)?.ssr),
939
958
  );
940
959
  if (serverMod?.setManifestReadyPromise) {
941
960
  serverMod.setManifestReadyPromise(discoveryPromise);
@@ -1552,7 +1571,7 @@ export function createRouterDiscoveryPlugin(
1552
1571
  try {
1553
1572
  if (hasMainRunner) {
1554
1573
  await timed(debugDiscovery, "hmr discoverRouters", () =>
1555
- discoverRouters(s, rscEnv),
1574
+ discoverRouters(s, rscEnv, (server.environments as any)?.ssr),
1556
1575
  );
1557
1576
  timedSync(debugDiscovery, "hmr writeRouteTypesFiles", () =>
1558
1577
  writeRouteTypesFiles(s),
@@ -1579,7 +1598,12 @@ export function createRouterDiscoveryPlugin(
1579
1598
  await timed(
1580
1599
  debugDiscovery,
1581
1600
  "hmr discoverRouters (cloudflare)",
1582
- () => discoverRouters(s, tempRscEnv),
1601
+ () =>
1602
+ discoverRouters(
1603
+ s,
1604
+ tempRscEnv,
1605
+ (prerenderTempServer?.environments as any)?.ssr,
1606
+ ),
1583
1607
  );
1584
1608
  timedSync(debugDiscovery, "hmr writeRouteTypesFiles", () =>
1585
1609
  writeRouteTypesFiles(s),
@@ -1724,12 +1748,54 @@ export function createRouterDiscoveryPlugin(
1724
1748
 
1725
1749
  const scheduleRouteRegeneration = () => {
1726
1750
  clearTimeout(routeChangeTimer);
1727
- routeChangeTimer = setTimeout(() => {
1751
+ routeChangeTimer = setTimeout(async () => {
1728
1752
  routeChangeTimer = undefined;
1729
1753
  const regenStart = debugDiscovery ? performance.now() : 0;
1730
1754
  const rscEnv = (server.environments as any)?.rsc;
1731
1755
  const skipStaticWrite =
1732
1756
  !rscEnv?.runner && s.perRouterManifests.length > 0;
1757
+ // Refresh clientUrls projections + state BEFORE any gen-file
1758
+ // write below: the write invalidates the routes-manifest virtual
1759
+ // module, whose regenerated code replays state.clientUrlProjectionMap
1760
+ // (clear + set). A stale map at that moment bakes stale literals
1761
+ // that clobber the runtime registry before the re-discovery entry
1762
+ // import materializes the client mount (node HMR served old
1763
+ // client-urls patterns until restart). Lenient: import failures
1764
+ // keep last-known state; discoverRouters' strict pass reports.
1765
+ if (rscEnv?.runner && s.clientUrlSourceByReferenceId?.size) {
1766
+ try {
1767
+ const serverMod = await rscEnv.runner.import(
1768
+ "@rangojs/router/server",
1769
+ );
1770
+ await refreshRecordedClientUrlProjections(
1771
+ s,
1772
+ (server.environments as any)?.ssr,
1773
+ serverMod,
1774
+ );
1775
+ // Force the routes-manifest virtual module to re-transform:
1776
+ // its generated code REPLAYS projections (clear + set) on
1777
+ // every rsc program reload, and self-gen-write suppression
1778
+ // keeps its cached transform alive through this cycle — a
1779
+ // stale replay after propagateDiscoveryState would clobber
1780
+ // the refreshed registry as the realm's last write and the
1781
+ // next router evaluation would materialize the old mount.
1782
+ for (const virtualId of [
1783
+ VIRTUAL_ROUTES_MANIFEST_ID,
1784
+ `\0${VIRTUAL_ROUTES_MANIFEST_ID}`,
1785
+ ]) {
1786
+ const virtualMod =
1787
+ rscEnv.moduleGraph?.getModuleById?.(virtualId);
1788
+ if (virtualMod) {
1789
+ rscEnv.moduleGraph.invalidateModule(virtualMod);
1790
+ }
1791
+ }
1792
+ } catch (err: any) {
1793
+ debugDiscovery?.(
1794
+ "watcher: clientUrls projection pre-refresh failed: %s",
1795
+ err?.message,
1796
+ );
1797
+ }
1798
+ }
1733
1799
  try {
1734
1800
  // In cloudflare dev with a populated runtime manifest, the
1735
1801
  // static parser produces a strictly smaller (and actively
@@ -1818,7 +1884,19 @@ export function createRouterDiscoveryPlugin(
1818
1884
  const isUseClient =
1819
1885
  trimmed.startsWith('"use client"') ||
1820
1886
  trimmed.startsWith("'use client'");
1821
- if (!inRecoveryMode && isUseClient) return;
1887
+ // clientUrls() modules are "use client" by contract yet define
1888
+ // routes: their edits must re-run discovery (server projection +
1889
+ // generated types), so only bail on use-client files WITHOUT a
1890
+ // clientUrls() definition. Scar: before this carve-out, editing a
1891
+ // clientUrls module's route shape in dev left the serving router on
1892
+ // the stale projection — the old pattern kept matching and the new
1893
+ // one 404ed until a full restart.
1894
+ let hasClientUrls = source.includes("clientUrls(");
1895
+ if (hasClientUrls) {
1896
+ hasClientUrls =
1897
+ firstCodeMatchIndex(source, /\bclientUrls\(/g) >= 0;
1898
+ }
1899
+ if (!inRecoveryMode && isUseClient && !hasClientUrls) return;
1822
1900
  // Cheap raw pre-check first; only when a candidate token is present
1823
1901
  // do we confirm it occurs in real code (not a comment/string) via a
1824
1902
  // single allocation-free code-region scan. Most saved files contain
@@ -1832,24 +1910,97 @@ export function createRouterDiscoveryPlugin(
1832
1910
  hasCreateRouter =
1833
1911
  firstCodeMatchIndex(source, /\bcreateRouter\s*[<(]/g) >= 0;
1834
1912
  }
1835
- if (!inRecoveryMode && !hasUrls && !hasCreateRouter) return;
1913
+ // hasClientUrls counts here too: in a clientUrls() module the only
1914
+ // `urls(` token sits INSIDE the `clientUrls(` identifier, which the
1915
+ // code scan correctly rejects as a sub-identifier match — so
1916
+ // without this the file would silently fail the sniff.
1917
+ if (
1918
+ !inRecoveryMode &&
1919
+ !hasUrls &&
1920
+ !hasCreateRouter &&
1921
+ !hasClientUrls
1922
+ ) {
1923
+ return;
1924
+ }
1836
1925
  if (inRecoveryMode) {
1837
1926
  debugDiscovery?.(
1838
- "watcher: recovery rediscovery for %s (urls=%s, router=%s, useClient=%s) [LASTERR %s]",
1927
+ "watcher: recovery rediscovery for %s (urls=%s, router=%s, clientUrls=%s, useClient=%s) [LASTERR %s]",
1839
1928
  filePath,
1840
1929
  hasUrls,
1841
1930
  hasCreateRouter,
1931
+ hasClientUrls,
1842
1932
  isUseClient,
1843
1933
  s.lastDiscoveryError!.message,
1844
1934
  );
1845
1935
  } else {
1846
1936
  debugDiscovery?.(
1847
- "watcher: %s matches (urls=%s, router=%s)",
1937
+ "watcher: %s matches (urls=%s, router=%s, clientUrls=%s)",
1848
1938
  filePath,
1849
1939
  hasUrls,
1850
1940
  hasCreateRouter,
1941
+ hasClientUrls,
1851
1942
  );
1852
1943
  }
1944
+ // A "use client" clientUrls module is an HMR-accepted client
1945
+ // boundary in the rsc graph: its edit never invalidates the router
1946
+ // module that materialized its projection, so the re-discovery
1947
+ // entry import would cache-hit and keep serving the stale mount
1948
+ // (old patterns 200, new patterns 404 until restart). Invalidate
1949
+ // the entry + router sources so the import re-creates the routers
1950
+ // against the refreshed projection (installed by the pre-entry
1951
+ // refresh in discover-routers.ts).
1952
+ if (isUseClient && hasClientUrls) {
1953
+ const rscGraph = (server.environments as any)?.rsc?.moduleGraph;
1954
+ if (rscGraph?.getModulesByFile) {
1955
+ // Importers must be invalidated too: the virtual RSC entry
1956
+ // holds a live `import { router }` binding, and re-evaluating
1957
+ // router.tsx alone leaves that binding on the OLD instance —
1958
+ // the request pipeline would keep serving the stale mount.
1959
+ // Same blast radius as a server urls edit (Vite's own
1960
+ // file-change invalidation propagates upward identically).
1961
+ const invalidateWithImporters = (
1962
+ mod: any,
1963
+ seen: Set<any>,
1964
+ ): void => {
1965
+ if (!mod || seen.has(mod)) return;
1966
+ seen.add(mod);
1967
+ rscGraph.invalidateModule(mod);
1968
+ for (const importer of mod.importers ?? []) {
1969
+ invalidateWithImporters(importer, seen);
1970
+ }
1971
+ };
1972
+ const routerSourceFiles = new Set<string>();
1973
+ if (s.resolvedEntryPath) {
1974
+ routerSourceFiles.add(resolve(s.resolvedEntryPath));
1975
+ }
1976
+ for (const entry of s.perRouterManifests) {
1977
+ if (entry.sourceFile) {
1978
+ routerSourceFiles.add(resolve(entry.sourceFile));
1979
+ }
1980
+ }
1981
+ const seen = new Set<any>();
1982
+ for (const file of routerSourceFiles) {
1983
+ const mods = rscGraph.getModulesByFile(
1984
+ file.replaceAll("\\", "/"),
1985
+ );
1986
+ if (!mods) {
1987
+ debugDiscovery?.(
1988
+ "watcher: clientUrls invalidation found no rsc modules for %s",
1989
+ file,
1990
+ );
1991
+ continue;
1992
+ }
1993
+ for (const mod of mods) {
1994
+ invalidateWithImporters(mod, seen);
1995
+ }
1996
+ }
1997
+ debugDiscovery?.(
1998
+ "watcher: clientUrls edit invalidated %d rsc module(s) for %d router file(s)",
1999
+ seen.size,
2000
+ routerSourceFiles.size,
2001
+ );
2002
+ }
2003
+ }
1853
2004
  // Invalidate cache when a router file changes (new router added/removed)
1854
2005
  if (hasCreateRouter) {
1855
2006
  const nestedRouterConflict = findNestedRouterConflict([
@@ -1962,7 +2113,7 @@ export function createRouterDiscoveryPlugin(
1962
2113
  }
1963
2114
 
1964
2115
  await timed(debugDiscovery, "build discoverRouters", () =>
1965
- discoverRouters(s, rscEnv),
2116
+ discoverRouters(s, rscEnv, (tempServer.environments as any)?.ssr),
1966
2117
  );
1967
2118
  // Update named-routes.gen.ts from runtime discovery.
1968
2119
  // The runtime manifest includes dynamically generated routes