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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (307) hide show
  1. package/AGENTS.md +9 -0
  2. package/README.md +942 -4
  3. package/dist/bin/rango.js +1689 -0
  4. package/dist/vite/index.js +4951 -930
  5. package/package.json +70 -60
  6. package/skills/breadcrumbs/SKILL.md +250 -0
  7. package/skills/cache-guide/SKILL.md +294 -0
  8. package/skills/caching/SKILL.md +93 -23
  9. package/skills/composability/SKILL.md +172 -0
  10. package/skills/debug-manifest/SKILL.md +12 -8
  11. package/skills/document-cache/SKILL.md +18 -16
  12. package/skills/fonts/SKILL.md +167 -0
  13. package/skills/hooks/SKILL.md +334 -72
  14. package/skills/host-router/SKILL.md +218 -0
  15. package/skills/intercept/SKILL.md +131 -8
  16. package/skills/layout/SKILL.md +100 -3
  17. package/skills/links/SKILL.md +92 -31
  18. package/skills/loader/SKILL.md +404 -44
  19. package/skills/middleware/SKILL.md +173 -34
  20. package/skills/mime-routes/SKILL.md +128 -0
  21. package/skills/parallel/SKILL.md +204 -1
  22. package/skills/prerender/SKILL.md +685 -0
  23. package/skills/rango/SKILL.md +85 -16
  24. package/skills/response-routes/SKILL.md +411 -0
  25. package/skills/route/SKILL.md +257 -14
  26. package/skills/router-setup/SKILL.md +210 -32
  27. package/skills/tailwind/SKILL.md +129 -0
  28. package/skills/theme/SKILL.md +9 -8
  29. package/skills/typesafety/SKILL.md +328 -89
  30. package/skills/use-cache/SKILL.md +324 -0
  31. package/src/__internal.ts +102 -4
  32. package/src/bin/rango.ts +321 -0
  33. package/src/browser/action-coordinator.ts +97 -0
  34. package/src/browser/action-response-classifier.ts +99 -0
  35. package/src/browser/app-version.ts +14 -0
  36. package/src/browser/event-controller.ts +92 -64
  37. package/src/browser/history-state.ts +80 -0
  38. package/src/browser/intercept-utils.ts +52 -0
  39. package/src/browser/link-interceptor.ts +24 -4
  40. package/src/browser/logging.ts +55 -0
  41. package/src/browser/merge-segment-loaders.ts +20 -12
  42. package/src/browser/navigation-bridge.ts +296 -558
  43. package/src/browser/navigation-client.ts +179 -69
  44. package/src/browser/navigation-store.ts +73 -55
  45. package/src/browser/navigation-transaction.ts +297 -0
  46. package/src/browser/network-error-handler.ts +61 -0
  47. package/src/browser/partial-update.ts +328 -313
  48. package/src/browser/prefetch/cache.ts +206 -0
  49. package/src/browser/prefetch/fetch.ts +150 -0
  50. package/src/browser/prefetch/observer.ts +65 -0
  51. package/src/browser/prefetch/policy.ts +48 -0
  52. package/src/browser/prefetch/queue.ts +160 -0
  53. package/src/browser/prefetch/resource-ready.ts +77 -0
  54. package/src/browser/rango-state.ts +112 -0
  55. package/src/browser/react/Link.tsx +230 -74
  56. package/src/browser/react/NavigationProvider.tsx +87 -11
  57. package/src/browser/react/context.ts +11 -0
  58. package/src/browser/react/filter-segment-order.ts +11 -0
  59. package/src/browser/react/index.ts +12 -12
  60. package/src/browser/react/location-state-shared.ts +95 -53
  61. package/src/browser/react/location-state.ts +60 -15
  62. package/src/browser/react/mount-context.ts +6 -1
  63. package/src/browser/react/nonce-context.ts +23 -0
  64. package/src/browser/react/shallow-equal.ts +27 -0
  65. package/src/browser/react/use-action.ts +29 -51
  66. package/src/browser/react/use-client-cache.ts +5 -3
  67. package/src/browser/react/use-handle.ts +30 -126
  68. package/src/browser/react/use-href.tsx +2 -2
  69. package/src/browser/react/use-link-status.ts +6 -5
  70. package/src/browser/react/use-navigation.ts +22 -63
  71. package/src/browser/react/use-params.ts +65 -0
  72. package/src/browser/react/use-pathname.ts +47 -0
  73. package/src/browser/react/use-router.ts +76 -0
  74. package/src/browser/react/use-search-params.ts +56 -0
  75. package/src/browser/react/use-segments.ts +80 -97
  76. package/src/browser/response-adapter.ts +73 -0
  77. package/src/browser/rsc-router.tsx +214 -58
  78. package/src/browser/scroll-restoration.ts +127 -52
  79. package/src/browser/segment-reconciler.ts +221 -0
  80. package/src/browser/segment-structure-assert.ts +16 -0
  81. package/src/browser/server-action-bridge.ts +510 -603
  82. package/src/browser/shallow.ts +6 -1
  83. package/src/browser/types.ts +141 -48
  84. package/src/browser/validate-redirect-origin.ts +29 -0
  85. package/src/build/generate-manifest.ts +235 -24
  86. package/src/build/generate-route-types.ts +39 -0
  87. package/src/build/index.ts +13 -0
  88. package/src/build/route-trie.ts +265 -0
  89. package/src/build/route-types/ast-helpers.ts +25 -0
  90. package/src/build/route-types/ast-route-extraction.ts +98 -0
  91. package/src/build/route-types/codegen.ts +102 -0
  92. package/src/build/route-types/include-resolution.ts +418 -0
  93. package/src/build/route-types/param-extraction.ts +48 -0
  94. package/src/build/route-types/per-module-writer.ts +128 -0
  95. package/src/build/route-types/router-processing.ts +618 -0
  96. package/src/build/route-types/scan-filter.ts +85 -0
  97. package/src/build/runtime-discovery.ts +231 -0
  98. package/src/cache/background-task.ts +34 -0
  99. package/src/cache/cache-key-utils.ts +44 -0
  100. package/src/cache/cache-policy.ts +125 -0
  101. package/src/cache/cache-runtime.ts +342 -0
  102. package/src/cache/cache-scope.ts +167 -309
  103. package/src/cache/cf/cf-cache-store.ts +571 -17
  104. package/src/cache/cf/index.ts +13 -3
  105. package/src/cache/document-cache.ts +116 -77
  106. package/src/cache/handle-capture.ts +81 -0
  107. package/src/cache/handle-snapshot.ts +41 -0
  108. package/src/cache/index.ts +1 -15
  109. package/src/cache/memory-segment-store.ts +191 -13
  110. package/src/cache/profile-registry.ts +73 -0
  111. package/src/cache/read-through-swr.ts +134 -0
  112. package/src/cache/segment-codec.ts +256 -0
  113. package/src/cache/taint.ts +153 -0
  114. package/src/cache/types.ts +72 -122
  115. package/src/client.rsc.tsx +3 -1
  116. package/src/client.tsx +105 -179
  117. package/src/component-utils.ts +4 -4
  118. package/src/components/DefaultDocument.tsx +5 -1
  119. package/src/context-var.ts +156 -0
  120. package/src/debug.ts +19 -9
  121. package/src/errors.ts +108 -2
  122. package/src/handle.ts +55 -29
  123. package/src/handles/MetaTags.tsx +73 -20
  124. package/src/handles/breadcrumbs.ts +66 -0
  125. package/src/handles/index.ts +1 -0
  126. package/src/handles/meta.ts +30 -13
  127. package/src/host/cookie-handler.ts +21 -15
  128. package/src/host/errors.ts +8 -8
  129. package/src/host/index.ts +4 -7
  130. package/src/host/pattern-matcher.ts +27 -27
  131. package/src/host/router.ts +61 -39
  132. package/src/host/testing.ts +8 -8
  133. package/src/host/types.ts +15 -7
  134. package/src/host/utils.ts +1 -1
  135. package/src/href-client.ts +119 -29
  136. package/src/index.rsc.ts +155 -19
  137. package/src/index.ts +223 -30
  138. package/src/internal-debug.ts +11 -0
  139. package/src/loader.rsc.ts +26 -157
  140. package/src/loader.ts +27 -10
  141. package/src/network-error-thrower.tsx +3 -1
  142. package/src/outlet-provider.tsx +45 -0
  143. package/src/prerender/param-hash.ts +37 -0
  144. package/src/prerender/store.ts +186 -0
  145. package/src/prerender.ts +524 -0
  146. package/src/reverse.ts +351 -0
  147. package/src/root-error-boundary.tsx +41 -29
  148. package/src/route-content-wrapper.tsx +7 -4
  149. package/src/route-definition/dsl-helpers.ts +982 -0
  150. package/src/route-definition/helper-factories.ts +200 -0
  151. package/src/route-definition/helpers-types.ts +434 -0
  152. package/src/route-definition/index.ts +55 -0
  153. package/src/route-definition/redirect.ts +101 -0
  154. package/src/route-definition/resolve-handler-use.ts +149 -0
  155. package/src/route-definition.ts +1 -1428
  156. package/src/route-map-builder.ts +217 -123
  157. package/src/route-name.ts +53 -0
  158. package/src/route-types.ts +70 -8
  159. package/src/router/content-negotiation.ts +215 -0
  160. package/src/router/debug-manifest.ts +72 -0
  161. package/src/router/error-handling.ts +9 -9
  162. package/src/router/find-match.ts +160 -0
  163. package/src/router/handler-context.ts +435 -86
  164. package/src/router/intercept-resolution.ts +402 -0
  165. package/src/router/lazy-includes.ts +237 -0
  166. package/src/router/loader-resolution.ts +356 -128
  167. package/src/router/logging.ts +251 -0
  168. package/src/router/manifest.ts +154 -35
  169. package/src/router/match-api.ts +555 -0
  170. package/src/router/match-context.ts +5 -3
  171. package/src/router/match-handlers.ts +440 -0
  172. package/src/router/match-middleware/background-revalidation.ts +108 -93
  173. package/src/router/match-middleware/cache-lookup.ts +459 -10
  174. package/src/router/match-middleware/cache-store.ts +98 -26
  175. package/src/router/match-middleware/intercept-resolution.ts +57 -17
  176. package/src/router/match-middleware/segment-resolution.ts +80 -6
  177. package/src/router/match-pipelines.ts +10 -45
  178. package/src/router/match-result.ts +135 -35
  179. package/src/router/metrics.ts +240 -15
  180. package/src/router/middleware-cookies.ts +55 -0
  181. package/src/router/middleware-types.ts +220 -0
  182. package/src/router/middleware.ts +324 -369
  183. package/src/router/navigation-snapshot.ts +182 -0
  184. package/src/router/pattern-matching.ts +211 -43
  185. package/src/router/prerender-match.ts +502 -0
  186. package/src/router/preview-match.ts +98 -0
  187. package/src/router/request-classification.ts +310 -0
  188. package/src/router/revalidation.ts +137 -38
  189. package/src/router/route-snapshot.ts +245 -0
  190. package/src/router/router-context.ts +41 -21
  191. package/src/router/router-interfaces.ts +484 -0
  192. package/src/router/router-options.ts +618 -0
  193. package/src/router/router-registry.ts +24 -0
  194. package/src/router/segment-resolution/fresh.ts +748 -0
  195. package/src/router/segment-resolution/helpers.ts +268 -0
  196. package/src/router/segment-resolution/loader-cache.ts +199 -0
  197. package/src/router/segment-resolution/revalidation.ts +1379 -0
  198. package/src/router/segment-resolution/static-store.ts +67 -0
  199. package/src/router/segment-resolution.ts +21 -0
  200. package/src/router/segment-wrappers.ts +291 -0
  201. package/src/router/telemetry-otel.ts +299 -0
  202. package/src/router/telemetry.ts +300 -0
  203. package/src/router/timeout.ts +148 -0
  204. package/src/router/trie-matching.ts +239 -0
  205. package/src/router/types.ts +78 -3
  206. package/src/router.ts +740 -4252
  207. package/src/rsc/handler-context.ts +45 -0
  208. package/src/rsc/handler.ts +907 -797
  209. package/src/rsc/helpers.ts +140 -6
  210. package/src/rsc/index.ts +0 -20
  211. package/src/rsc/loader-fetch.ts +229 -0
  212. package/src/rsc/manifest-init.ts +90 -0
  213. package/src/rsc/nonce.ts +14 -0
  214. package/src/rsc/origin-guard.ts +141 -0
  215. package/src/rsc/progressive-enhancement.ts +391 -0
  216. package/src/rsc/response-error.ts +37 -0
  217. package/src/rsc/response-route-handler.ts +347 -0
  218. package/src/rsc/rsc-rendering.ts +246 -0
  219. package/src/rsc/runtime-warnings.ts +42 -0
  220. package/src/rsc/server-action.ts +356 -0
  221. package/src/rsc/ssr-setup.ts +128 -0
  222. package/src/rsc/types.ts +46 -11
  223. package/src/search-params.ts +230 -0
  224. package/src/segment-system.tsx +165 -17
  225. package/src/server/context.ts +315 -58
  226. package/src/server/cookie-store.ts +190 -0
  227. package/src/server/fetchable-loader-store.ts +37 -0
  228. package/src/server/handle-store.ts +113 -15
  229. package/src/server/loader-registry.ts +24 -64
  230. package/src/server/request-context.ts +607 -81
  231. package/src/server.ts +35 -130
  232. package/src/ssr/index.tsx +103 -30
  233. package/src/static-handler.ts +126 -0
  234. package/src/theme/ThemeProvider.tsx +21 -15
  235. package/src/theme/ThemeScript.tsx +5 -5
  236. package/src/theme/constants.ts +5 -2
  237. package/src/theme/index.ts +4 -14
  238. package/src/theme/theme-context.ts +4 -30
  239. package/src/theme/theme-script.ts +21 -18
  240. package/src/types/boundaries.ts +158 -0
  241. package/src/types/cache-types.ts +198 -0
  242. package/src/types/error-types.ts +192 -0
  243. package/src/types/global-namespace.ts +100 -0
  244. package/src/types/handler-context.ts +791 -0
  245. package/src/types/index.ts +88 -0
  246. package/src/types/loader-types.ts +210 -0
  247. package/src/types/route-config.ts +170 -0
  248. package/src/types/route-entry.ts +109 -0
  249. package/src/types/segments.ts +151 -0
  250. package/src/types.ts +1 -1623
  251. package/src/urls/include-helper.ts +197 -0
  252. package/src/urls/index.ts +53 -0
  253. package/src/urls/path-helper-types.ts +346 -0
  254. package/src/urls/path-helper.ts +364 -0
  255. package/src/urls/pattern-types.ts +107 -0
  256. package/src/urls/response-types.ts +116 -0
  257. package/src/urls/type-extraction.ts +372 -0
  258. package/src/urls/urls-function.ts +98 -0
  259. package/src/urls.ts +1 -802
  260. package/src/use-loader.tsx +161 -81
  261. package/src/vite/discovery/bundle-postprocess.ts +181 -0
  262. package/src/vite/discovery/discover-routers.ts +348 -0
  263. package/src/vite/discovery/prerender-collection.ts +439 -0
  264. package/src/vite/discovery/route-types-writer.ts +258 -0
  265. package/src/vite/discovery/self-gen-tracking.ts +47 -0
  266. package/src/vite/discovery/state.ts +117 -0
  267. package/src/vite/discovery/virtual-module-codegen.ts +203 -0
  268. package/src/vite/index.ts +15 -1129
  269. package/src/vite/plugin-types.ts +103 -0
  270. package/src/vite/plugins/cjs-to-esm.ts +93 -0
  271. package/src/vite/plugins/client-ref-dedup.ts +115 -0
  272. package/src/vite/plugins/client-ref-hashing.ts +105 -0
  273. package/src/vite/{expose-action-id.ts → plugins/expose-action-id.ts} +72 -53
  274. package/src/vite/plugins/expose-id-utils.ts +299 -0
  275. package/src/vite/plugins/expose-ids/export-analysis.ts +296 -0
  276. package/src/vite/plugins/expose-ids/handler-transform.ts +209 -0
  277. package/src/vite/plugins/expose-ids/loader-transform.ts +74 -0
  278. package/src/vite/plugins/expose-ids/router-transform.ts +110 -0
  279. package/src/vite/plugins/expose-ids/types.ts +45 -0
  280. package/src/vite/plugins/expose-internal-ids.ts +786 -0
  281. package/src/vite/plugins/performance-tracks.ts +88 -0
  282. package/src/vite/plugins/refresh-cmd.ts +127 -0
  283. package/src/vite/plugins/use-cache-transform.ts +323 -0
  284. package/src/vite/plugins/version-injector.ts +83 -0
  285. package/src/vite/plugins/version-plugin.ts +266 -0
  286. package/src/vite/{virtual-entries.ts → plugins/virtual-entries.ts} +23 -14
  287. package/src/vite/plugins/virtual-stub-plugin.ts +29 -0
  288. package/src/vite/rango.ts +462 -0
  289. package/src/vite/router-discovery.ts +918 -0
  290. package/src/vite/utils/ast-handler-extract.ts +517 -0
  291. package/src/vite/utils/banner.ts +36 -0
  292. package/src/vite/utils/bundle-analysis.ts +137 -0
  293. package/src/vite/utils/manifest-utils.ts +70 -0
  294. package/src/vite/{package-resolution.ts → utils/package-resolution.ts} +25 -29
  295. package/src/vite/utils/prerender-utils.ts +207 -0
  296. package/src/vite/utils/shared-utils.ts +170 -0
  297. package/CLAUDE.md +0 -43
  298. package/src/browser/lru-cache.ts +0 -69
  299. package/src/browser/request-controller.ts +0 -164
  300. package/src/cache/memory-store.ts +0 -253
  301. package/src/href-context.ts +0 -33
  302. package/src/href.ts +0 -255
  303. package/src/server/route-manifest-cache.ts +0 -173
  304. package/src/vite/expose-handle-id.ts +0 -209
  305. package/src/vite/expose-loader-id.ts +0 -426
  306. package/src/vite/expose-location-state-id.ts +0 -177
  307. /package/src/vite/{version.d.ts → plugins/version.d.ts} +0 -0
@@ -1,173 +0,0 @@
1
- /**
2
- * Route Manifest Cache
3
- *
4
- * Three-tier caching strategy for route manifest data:
5
- * 1. In-memory (same isolate) - instant
6
- * 2. SegmentCacheStore (caches.default on Cloudflare) - ~1-2ms
7
- * 3. Generate on-demand (cache miss) - ~98ms
8
- *
9
- * Benefits:
10
- * - Removes 725KB bundled manifest from worker code
11
- * - Typical cold start: 0-2ms (cache hit)
12
- * - Worst case: ~98ms (first request per colo)
13
- */
14
-
15
- import type { SegmentCacheStore, CachedEntryData } from "../cache/types.js";
16
- import type { GeneratedManifest } from "../build/generate-manifest.js";
17
- import { setCachedManifest, hasCachedManifest } from "../route-map-builder.js";
18
-
19
- /**
20
- * Cached route data structure
21
- */
22
- interface CachedRouteData {
23
- /** Route name → pattern mapping for href() */
24
- routeManifest: Record<string, string>;
25
- /** Version string for cache invalidation */
26
- version: string;
27
- }
28
-
29
- // ============================================================================
30
- // Tier 1: In-memory singleton (same isolate - instant)
31
- // ============================================================================
32
-
33
- let memoryManifest: CachedRouteData | null = null;
34
-
35
- /**
36
- * Options for getRouteManifestData
37
- */
38
- export interface GetRouteManifestOptions {
39
- /** Cache store implementation (e.g., CFCacheStore). If omitted, memory-only caching is used. */
40
- store?: SegmentCacheStore;
41
- /** Optional function to schedule non-blocking cache write (e.g., ctx.waitUntil) */
42
- waitUntil?: (promise: Promise<void>) => void;
43
- }
44
-
45
- /**
46
- * Get route manifest data with caching:
47
- * 1. In-memory (same isolate) - instant
48
- * 2. SegmentCacheStore (if provided, e.g., CFCacheStore on Cloudflare) - ~1-2ms
49
- * 3. Generate on-demand (cache miss) - ~98ms
50
- *
51
- * When no store is provided, only in-memory caching is used (memory-only mode).
52
- * This is suitable for development or when external cache is not available.
53
- *
54
- * @param generateFn - Function to generate manifest on cache miss
55
- * @param version - Version string for cache invalidation
56
- * @param options - Optional cache store and waitUntil function
57
- * @returns Cached or freshly generated route data
58
- */
59
- export async function getRouteManifestData(
60
- generateFn: () => GeneratedManifest,
61
- version: string,
62
- options?: GetRouteManifestOptions
63
- ): Promise<CachedRouteData> {
64
- const { store, waitUntil } = options ?? {};
65
- const cacheKey = `route-manifest:${version}`;
66
-
67
- const startTime = performance.now();
68
-
69
- // 1. In-memory check (same isolate) - instant
70
- if (memoryManifest?.version === version) {
71
- console.log("[route-manifest] HIT memory cache (same isolate)");
72
- return memoryManifest;
73
- }
74
-
75
- // 2. Cache store check (if store provided) - ~1-2ms
76
- if (store) {
77
- try {
78
- const cached = await store.get(cacheKey);
79
- if (cached?.data) {
80
- // Extract manifest from the CachedEntryData wrapper
81
- const manifest = (cached.data as unknown as { manifest: CachedRouteData }).manifest;
82
- if (manifest?.version === version) {
83
- memoryManifest = manifest;
84
- setCachedManifest(memoryManifest.routeManifest);
85
- const duration = (performance.now() - startTime).toFixed(2);
86
- console.log(`[route-manifest] HIT edge cache (${duration}ms, ${Object.keys(manifest.routeManifest).length} routes)`);
87
- return memoryManifest;
88
- }
89
- }
90
- } catch (error) {
91
- // Cache miss or error - fall through to generation
92
- console.warn("[route-manifest] Edge cache read failed:", error);
93
- }
94
- }
95
-
96
- // 3. Generate on cache miss - ~98ms
97
- const generated = generateFn();
98
- memoryManifest = {
99
- routeManifest: generated.routeManifest,
100
- version,
101
- };
102
- // Make available to getGlobalRouteMap() for href()
103
- setCachedManifest(memoryManifest.routeManifest);
104
- const duration = (performance.now() - startTime).toFixed(2);
105
- console.log(`[route-manifest] MISS - generated fresh (${duration}ms, ${Object.keys(generated.routeManifest).length} routes)`);
106
-
107
- // Store in cache for other isolates (only if store provided)
108
- // OFF RENDERING PATH via waitUntil
109
- if (store) {
110
- console.log("[route-manifest] Writing to edge cache (via waitUntil)...");
111
- const cachePromise = (async () => {
112
- try {
113
- // Wrap in CachedEntryData format expected by SegmentCacheStore
114
- const data: CachedEntryData = {
115
- segments: [],
116
- handles: {},
117
- expiresAt: Date.now() + 31536000 * 1000, // 1 year
118
- };
119
- // Store manifest in a custom field
120
- (data as unknown as { manifest: CachedRouteData }).manifest = memoryManifest!;
121
- await store.set(cacheKey, data, 31536000); // 1 year TTL
122
- console.log("[route-manifest] Edge cache write complete");
123
- } catch (error) {
124
- console.warn("[route-manifest] Edge cache write failed:", error);
125
- }
126
- })();
127
-
128
- if (waitUntil) {
129
- // Non-blocking: cache write happens after response is sent
130
- waitUntil(cachePromise);
131
- } else {
132
- // Fallback: blocking write (dev mode or no waitUntil available)
133
- await cachePromise;
134
- }
135
- }
136
-
137
- return memoryManifest;
138
- }
139
-
140
- /**
141
- * Sync access to in-memory manifest (for href())
142
- * Returns null if not yet loaded
143
- *
144
- * @returns The route manifest or null if not loaded
145
- */
146
- export function getRouteManifestSync(): Record<string, string> | null {
147
- return memoryManifest?.routeManifest ?? null;
148
- }
149
-
150
- /**
151
- * Clear in-memory cache (for testing)
152
- */
153
- export function clearRouteManifestCache(): void {
154
- memoryManifest = null;
155
- }
156
-
157
- /**
158
- * Check if manifest is loaded in memory
159
- *
160
- * @returns true if manifest is available synchronously
161
- */
162
- export function isManifestLoaded(): boolean {
163
- return memoryManifest !== null;
164
- }
165
-
166
- /**
167
- * Get the current cached version (for debugging)
168
- *
169
- * @returns The version string or null if not loaded
170
- */
171
- export function getManifestVersion(): string | null {
172
- return memoryManifest?.version ?? null;
173
- }
@@ -1,209 +0,0 @@
1
- import type { Plugin, ResolvedConfig } from "vite";
2
- import MagicString from "magic-string";
3
- import path from "node:path";
4
- import crypto from "node:crypto";
5
-
6
- /**
7
- * Normalize path to forward slashes
8
- */
9
- function normalizePath(p: string): string {
10
- return p.split(path.sep).join("/");
11
- }
12
-
13
- /**
14
- * Generate a short hash for a handle ID
15
- * Uses first 8 chars of SHA-256 hash for uniqueness while keeping IDs short
16
- * Appends export name for easier debugging: "abc123#Breadcrumbs"
17
- */
18
- function hashHandleId(filePath: string, exportName: string): string {
19
- const input = `${filePath}#${exportName}`;
20
- const hash = crypto.createHash("sha256").update(input).digest("hex");
21
- return `${hash.slice(0, 8)}#${exportName}`;
22
- }
23
-
24
- /**
25
- * Check if file imports createHandle from rsc-router
26
- */
27
- function hasCreateHandleImport(code: string): boolean {
28
- // Match: import { createHandle } from "@rangojs/router" or "@rangojs/router/..."
29
- const pattern =
30
- /import\s*\{[^}]*\bcreateHandle\b[^}]*\}\s*from\s*["']@rangojs\/router(?:\/[^"']+)?["']/;
31
- return pattern.test(code);
32
- }
33
-
34
- /**
35
- * Analyze createHandle arguments to determine injection strategy
36
- * Returns: { hasArgs: boolean, firstArgIsString: boolean, firstArgIsFunction: boolean }
37
- */
38
- function analyzeCreateHandleArgs(
39
- code: string,
40
- startPos: number,
41
- endPos: number
42
- ): { hasArgs: boolean; firstArgIsString: boolean; firstArgIsFunction: boolean } {
43
- // Extract the content between parentheses
44
- const content = code.slice(startPos, endPos).trim();
45
-
46
- if (!content) {
47
- return { hasArgs: false, firstArgIsString: false, firstArgIsFunction: false };
48
- }
49
-
50
- // Check if first arg starts with a quote (string literal)
51
- const firstArgIsString = /^["']/.test(content);
52
-
53
- // Check if first arg starts with ( for arrow function or function keyword
54
- const firstArgIsFunction =
55
- content.startsWith("(") ||
56
- content.startsWith("function") ||
57
- // Check for identifier that could be a collect function reference
58
- /^[a-zA-Z_$][a-zA-Z0-9_$]*\s*(?:,|$)/.test(content);
59
-
60
- return { hasArgs: true, firstArgIsString, firstArgIsFunction };
61
- }
62
-
63
- /**
64
- * Transform export const X = createHandle(...) patterns to inject $$id
65
- *
66
- * Handles these cases:
67
- * 1. createHandle() - no args -> inject (undefined, "id")
68
- * 2. createHandle("name") - string name -> inject (, "id") after existing arg
69
- * 3. createHandle(collectFn) - collect function -> inject (collectFn, "id")
70
- * 4. createHandle("name", collectFn) - both -> inject (, "id") after existing args
71
- */
72
- function transformHandleExports(
73
- code: string,
74
- filePath: string,
75
- sourceId?: string,
76
- isBuild: boolean = false
77
- ): { code: string; map: ReturnType<MagicString["generateMap"]> } | null {
78
- // Quick bail-out
79
- if (!code.includes("createHandle")) {
80
- return null;
81
- }
82
-
83
- // Must have direct import from rsc-router
84
- if (!hasCreateHandleImport(code)) {
85
- return null;
86
- }
87
-
88
- // Match: export const X = createHandle<...>(
89
- // Captures the export name (X)
90
- const pattern = /export\s+const\s+(\w+)\s*=\s*createHandle\s*(?:<[^>]*>)?\s*\(/g;
91
-
92
- const s = new MagicString(code);
93
- let hasChanges = false;
94
- let match: RegExpExecArray | null;
95
-
96
- while ((match = pattern.exec(code)) !== null) {
97
- const exportName = match[1];
98
- const matchEnd = match.index + match[0].length;
99
-
100
- // Find the end of the createHandle(...) call
101
- let parenDepth = 1;
102
- let i = matchEnd;
103
- while (i < code.length && parenDepth > 0) {
104
- if (code[i] === "(") parenDepth++;
105
- if (code[i] === ")") parenDepth--;
106
- i++;
107
- }
108
-
109
- // i now points just after the closing )
110
- const closeParenPos = i - 1;
111
-
112
- // Analyze what arguments exist
113
- const args = analyzeCreateHandleArgs(code, matchEnd, closeParenPos);
114
-
115
- // Find the semicolon or end of statement
116
- let statementEnd = i;
117
- while (statementEnd < code.length && /\s/.test(code[statementEnd])) {
118
- statementEnd++;
119
- }
120
- if (code[statementEnd] === ";") {
121
- statementEnd++;
122
- }
123
-
124
- // Generate ID: hashed in production, readable in dev
125
- const handleId = isBuild
126
- ? hashHandleId(filePath, exportName)
127
- : `${filePath}#${exportName}`;
128
-
129
- // Inject $$id as the last parameter
130
- let paramInjection: string;
131
- if (!args.hasArgs) {
132
- // No args: createHandle() -> createHandle(undefined, "id")
133
- paramInjection = `undefined, "${handleId}"`;
134
- } else {
135
- // Has args: createHandle(x) -> createHandle(x, "id")
136
- paramInjection = `, "${handleId}"`;
137
- }
138
- s.appendLeft(closeParenPos, paramInjection);
139
-
140
- // Also set $$id property for external access
141
- const propInjection = `\n${exportName}.$$id = "${handleId}";`;
142
- s.appendRight(statementEnd, propInjection);
143
- hasChanges = true;
144
- }
145
-
146
- if (!hasChanges) {
147
- return null;
148
- }
149
-
150
- return {
151
- code: s.toString(),
152
- map: s.generateMap({ source: sourceId, includeContent: true }),
153
- };
154
- }
155
-
156
- /**
157
- * Vite plugin that exposes $$id on createHandle calls.
158
- *
159
- * When users create handles with createHandle(), this plugin:
160
- * 1. Injects a $$id as the last parameter (used as the handle name)
161
- * 2. Sets $$id property on the exported constant for external access
162
- *
163
- * This allows handles to be created without explicit names:
164
- * - Before: export const Breadcrumbs = createHandle<Item>("breadcrumbs")
165
- * - After: export const Breadcrumbs = createHandle<Item>()
166
- *
167
- * The name is auto-generated from file path + export name.
168
- *
169
- * Requirements:
170
- * - Must use direct import: import { createHandle } from "@rangojs/router"
171
- * - Must use named export: export const MyHandle = createHandle(...)
172
- */
173
- export function exposeHandleId(): Plugin {
174
- let config: ResolvedConfig;
175
- let isBuild = false;
176
-
177
- return {
178
- name: "@rangojs/router:expose-handle-id",
179
- enforce: "post",
180
-
181
- configResolved(resolvedConfig) {
182
- config = resolvedConfig;
183
- isBuild = config.command === "build";
184
- },
185
-
186
- transform(code, id) {
187
- // Skip node_modules
188
- if (id.includes("/node_modules/")) {
189
- return;
190
- }
191
-
192
- // Quick bail-out
193
- if (!code.includes("createHandle")) {
194
- return;
195
- }
196
-
197
- // Must have direct import from rsc-router
198
- if (!hasCreateHandleImport(code)) {
199
- return;
200
- }
201
-
202
- // Get relative path for the ID
203
- const relativePath = normalizePath(path.relative(config.root, id));
204
-
205
- // Transform: inject $$id
206
- return transformHandleExports(code, relativePath, id, isBuild);
207
- },
208
- };
209
- }