@rangojs/router 0.0.0-experimental.8874d8d2 → 0.0.0-experimental.89

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 (178) hide show
  1. package/AGENTS.md +4 -0
  2. package/README.md +126 -38
  3. package/dist/bin/rango.js +138 -50
  4. package/dist/vite/index.js +1705 -701
  5. package/dist/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
  6. package/package.json +19 -16
  7. package/skills/breadcrumbs/SKILL.md +3 -1
  8. package/skills/cache-guide/SKILL.md +32 -0
  9. package/skills/caching/SKILL.md +45 -4
  10. package/skills/handler-use/SKILL.md +362 -0
  11. package/skills/hooks/SKILL.md +28 -20
  12. package/skills/intercept/SKILL.md +20 -0
  13. package/skills/layout/SKILL.md +22 -0
  14. package/skills/links/SKILL.md +91 -17
  15. package/skills/loader/SKILL.md +88 -45
  16. package/skills/middleware/SKILL.md +34 -3
  17. package/skills/migrate-nextjs/SKILL.md +560 -0
  18. package/skills/migrate-react-router/SKILL.md +765 -0
  19. package/skills/parallel/SKILL.md +185 -0
  20. package/skills/prerender/SKILL.md +110 -68
  21. package/skills/rango/SKILL.md +24 -22
  22. package/skills/response-routes/SKILL.md +8 -0
  23. package/skills/route/SKILL.md +55 -0
  24. package/skills/router-setup/SKILL.md +87 -2
  25. package/skills/streams-and-websockets/SKILL.md +283 -0
  26. package/skills/typesafety/SKILL.md +13 -1
  27. package/src/__internal.ts +1 -1
  28. package/src/browser/app-shell.ts +52 -0
  29. package/src/browser/app-version.ts +14 -0
  30. package/src/browser/event-controller.ts +5 -0
  31. package/src/browser/navigation-bridge.ts +90 -16
  32. package/src/browser/navigation-client.ts +167 -59
  33. package/src/browser/navigation-store.ts +68 -9
  34. package/src/browser/navigation-transaction.ts +11 -9
  35. package/src/browser/partial-update.ts +113 -17
  36. package/src/browser/prefetch/cache.ts +175 -15
  37. package/src/browser/prefetch/fetch.ts +180 -33
  38. package/src/browser/prefetch/queue.ts +123 -20
  39. package/src/browser/prefetch/resource-ready.ts +77 -0
  40. package/src/browser/rango-state.ts +53 -13
  41. package/src/browser/react/Link.tsx +81 -9
  42. package/src/browser/react/NavigationProvider.tsx +89 -14
  43. package/src/browser/react/context.ts +7 -2
  44. package/src/browser/react/use-handle.ts +9 -58
  45. package/src/browser/react/use-navigation.ts +22 -2
  46. package/src/browser/react/use-params.ts +11 -1
  47. package/src/browser/react/use-router.ts +29 -9
  48. package/src/browser/rsc-router.tsx +168 -65
  49. package/src/browser/scroll-restoration.ts +41 -42
  50. package/src/browser/segment-reconciler.ts +36 -9
  51. package/src/browser/server-action-bridge.ts +8 -6
  52. package/src/browser/types.ts +49 -5
  53. package/src/build/generate-manifest.ts +6 -6
  54. package/src/build/generate-route-types.ts +3 -0
  55. package/src/build/route-trie.ts +50 -24
  56. package/src/build/route-types/include-resolution.ts +8 -1
  57. package/src/build/route-types/router-processing.ts +223 -74
  58. package/src/build/route-types/scan-filter.ts +8 -1
  59. package/src/cache/cache-runtime.ts +15 -11
  60. package/src/cache/cache-scope.ts +48 -7
  61. package/src/cache/cf/cf-cache-store.ts +455 -15
  62. package/src/cache/cf/index.ts +5 -1
  63. package/src/cache/document-cache.ts +17 -7
  64. package/src/cache/index.ts +1 -0
  65. package/src/cache/taint.ts +55 -0
  66. package/src/client.tsx +84 -230
  67. package/src/context-var.ts +72 -2
  68. package/src/debug.ts +2 -2
  69. package/src/handle.ts +40 -0
  70. package/src/index.rsc.ts +6 -1
  71. package/src/index.ts +49 -6
  72. package/src/outlet-context.ts +1 -1
  73. package/src/prerender/store.ts +5 -4
  74. package/src/prerender.ts +138 -77
  75. package/src/response-utils.ts +28 -0
  76. package/src/reverse.ts +27 -2
  77. package/src/route-definition/dsl-helpers.ts +240 -40
  78. package/src/route-definition/helpers-types.ts +67 -19
  79. package/src/route-definition/index.ts +3 -0
  80. package/src/route-definition/redirect.ts +11 -3
  81. package/src/route-definition/resolve-handler-use.ts +155 -0
  82. package/src/route-map-builder.ts +7 -1
  83. package/src/route-types.ts +18 -0
  84. package/src/router/content-negotiation.ts +100 -1
  85. package/src/router/find-match.ts +4 -2
  86. package/src/router/handler-context.ts +101 -25
  87. package/src/router/intercept-resolution.ts +11 -4
  88. package/src/router/lazy-includes.ts +10 -7
  89. package/src/router/loader-resolution.ts +159 -21
  90. package/src/router/logging.ts +5 -2
  91. package/src/router/manifest.ts +31 -16
  92. package/src/router/match-api.ts +127 -192
  93. package/src/router/match-middleware/background-revalidation.ts +30 -2
  94. package/src/router/match-middleware/cache-lookup.ts +94 -17
  95. package/src/router/match-middleware/cache-store.ts +53 -10
  96. package/src/router/match-middleware/intercept-resolution.ts +9 -7
  97. package/src/router/match-middleware/segment-resolution.ts +61 -5
  98. package/src/router/match-result.ts +104 -10
  99. package/src/router/metrics.ts +6 -1
  100. package/src/router/middleware-types.ts +8 -30
  101. package/src/router/middleware.ts +36 -10
  102. package/src/router/navigation-snapshot.ts +182 -0
  103. package/src/router/pattern-matching.ts +60 -9
  104. package/src/router/prerender-match.ts +110 -10
  105. package/src/router/preview-match.ts +30 -102
  106. package/src/router/request-classification.ts +310 -0
  107. package/src/router/route-snapshot.ts +245 -0
  108. package/src/router/router-context.ts +6 -1
  109. package/src/router/router-interfaces.ts +36 -4
  110. package/src/router/router-options.ts +37 -11
  111. package/src/router/segment-resolution/fresh.ts +198 -20
  112. package/src/router/segment-resolution/helpers.ts +29 -24
  113. package/src/router/segment-resolution/loader-cache.ts +1 -0
  114. package/src/router/segment-resolution/revalidation.ts +437 -297
  115. package/src/router/segment-wrappers.ts +2 -0
  116. package/src/router/trie-matching.ts +10 -4
  117. package/src/router/types.ts +1 -0
  118. package/src/router/url-params.ts +49 -0
  119. package/src/router.ts +60 -8
  120. package/src/rsc/handler.ts +478 -374
  121. package/src/rsc/helpers.ts +69 -41
  122. package/src/rsc/loader-fetch.ts +23 -3
  123. package/src/rsc/manifest-init.ts +5 -1
  124. package/src/rsc/progressive-enhancement.ts +16 -2
  125. package/src/rsc/response-route-handler.ts +14 -1
  126. package/src/rsc/rsc-rendering.ts +17 -1
  127. package/src/rsc/server-action.ts +10 -0
  128. package/src/rsc/ssr-setup.ts +2 -2
  129. package/src/rsc/types.ts +9 -1
  130. package/src/segment-content-promise.ts +67 -0
  131. package/src/segment-loader-promise.ts +122 -0
  132. package/src/segment-system.tsx +109 -23
  133. package/src/server/context.ts +166 -17
  134. package/src/server/handle-store.ts +19 -0
  135. package/src/server/loader-registry.ts +9 -8
  136. package/src/server/request-context.ts +194 -60
  137. package/src/ssr/index.tsx +4 -0
  138. package/src/static-handler.ts +18 -6
  139. package/src/types/cache-types.ts +4 -4
  140. package/src/types/handler-context.ts +137 -65
  141. package/src/types/loader-types.ts +41 -15
  142. package/src/types/request-scope.ts +126 -0
  143. package/src/types/route-entry.ts +19 -1
  144. package/src/types/segments.ts +2 -0
  145. package/src/urls/include-helper.ts +24 -14
  146. package/src/urls/path-helper-types.ts +39 -6
  147. package/src/urls/path-helper.ts +48 -13
  148. package/src/urls/pattern-types.ts +12 -0
  149. package/src/urls/response-types.ts +18 -16
  150. package/src/use-loader.tsx +77 -5
  151. package/src/vite/debug.ts +184 -0
  152. package/src/vite/discovery/bundle-postprocess.ts +30 -33
  153. package/src/vite/discovery/discover-routers.ts +36 -4
  154. package/src/vite/discovery/prerender-collection.ts +175 -74
  155. package/src/vite/discovery/state.ts +13 -6
  156. package/src/vite/index.ts +4 -0
  157. package/src/vite/plugin-types.ts +51 -79
  158. package/src/vite/plugins/cjs-to-esm.ts +5 -0
  159. package/src/vite/plugins/client-ref-dedup.ts +16 -0
  160. package/src/vite/plugins/client-ref-hashing.ts +16 -4
  161. package/src/vite/plugins/cloudflare-protocol-loader-hook.d.mts +23 -0
  162. package/src/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
  163. package/src/vite/plugins/cloudflare-protocol-stub.ts +214 -0
  164. package/src/vite/plugins/expose-action-id.ts +53 -31
  165. package/src/vite/plugins/expose-id-utils.ts +12 -0
  166. package/src/vite/plugins/expose-ids/handler-transform.ts +30 -0
  167. package/src/vite/plugins/expose-ids/router-transform.ts +20 -3
  168. package/src/vite/plugins/expose-internal-ids.ts +563 -316
  169. package/src/vite/plugins/performance-tracks.ts +96 -0
  170. package/src/vite/plugins/refresh-cmd.ts +88 -26
  171. package/src/vite/plugins/use-cache-transform.ts +56 -43
  172. package/src/vite/plugins/version-plugin.ts +13 -1
  173. package/src/vite/rango.ts +204 -217
  174. package/src/vite/router-discovery.ts +393 -67
  175. package/src/vite/utils/banner.ts +4 -4
  176. package/src/vite/utils/package-resolution.ts +41 -1
  177. package/src/vite/utils/prerender-utils.ts +37 -5
  178. package/src/vite/utils/shared-utils.ts +3 -2
@@ -3,6 +3,9 @@ import MagicString from "magic-string";
3
3
  import path from "node:path";
4
4
  import fs from "node:fs";
5
5
  import { normalizePath } from "./expose-id-utils.js";
6
+ import { createRangoDebugger, createCounter, NS } from "../debug.js";
7
+
8
+ const debug = createRangoDebugger(NS.transform);
6
9
 
7
10
  /**
8
11
  * Type for the RSC plugin's manager API
@@ -254,6 +257,8 @@ export function exposeActionId(): Plugin {
254
257
  let isBuild = false;
255
258
  let hashToFileMap: Map<string, string> | undefined;
256
259
  let rscPluginApi: RscPluginApi | undefined;
260
+ const counterTransform = createCounter(debug, "expose-action-id transform");
261
+ const counterRender = createCounter(debug, "expose-action-id renderChunk");
257
262
 
258
263
  return {
259
264
  name: "@rangojs/router:expose-action-id",
@@ -268,6 +273,11 @@ export function exposeActionId(): Plugin {
268
273
  rscPluginApi = getRscPluginApi(config);
269
274
  },
270
275
 
276
+ buildEnd() {
277
+ counterTransform?.flush();
278
+ counterRender?.flush();
279
+ },
280
+
271
281
  buildStart() {
272
282
  // Verify RSC plugin is present at build start (after all config hooks have run)
273
283
  // This allows rsc-router:rsc-integration to dynamically add the RSC plugin
@@ -278,9 +288,7 @@ export function exposeActionId(): Plugin {
278
288
  if (!rscPluginApi) {
279
289
  throw new Error(
280
290
  "[rsc-router] Could not find @vitejs/plugin-rsc. " +
281
- "@rangojs/router requires the Vite RSC plugin.\n" +
282
- "The RSC plugin should be included automatically. If you disabled it with\n" +
283
- "rango({ rsc: false }), add rsc() before rango() in your config.",
291
+ "@rangojs/router requires the Vite RSC plugin, which is included automatically by rango().",
284
292
  );
285
293
  }
286
294
 
@@ -326,40 +334,54 @@ export function exposeActionId(): Plugin {
326
334
  return;
327
335
  }
328
336
 
329
- // Dev mode: no hash-to-file mapping needed (IDs are already file paths)
330
- return transformServerReferences(code, id);
337
+ const start = counterTransform ? performance.now() : 0;
338
+ try {
339
+ // Dev mode: no hash-to-file mapping needed (IDs are already file paths)
340
+ return transformServerReferences(code, id);
341
+ } finally {
342
+ counterTransform?.record(id, performance.now() - start);
343
+ }
331
344
  },
332
345
 
333
346
  // Build mode: renderChunk runs after all transforms and bundling complete
334
347
  renderChunk(code, chunk) {
335
- // Only RSC bundle should get file paths for revalidation matching
336
- // SSR bundle must NOT use file paths because client components run there
337
- // and need to match the client bundle during hydration (otherwise: error #418)
338
- const isRscEnv = this.environment?.name === "rsc";
339
-
340
- // Only use file path mapping for RSC environment
341
- const effectiveMap = isRscEnv ? hashToFileMap : undefined;
342
-
343
- // For RSC bundles, both createServerReference and registerServerReference
344
- // may need transforming. Use a single MagicString for correct sourcemaps.
345
- if (isRscEnv && hashToFileMap) {
346
- const s = new MagicString(code);
347
- const changed1 = applyServerReferenceWrapping(code, s, effectiveMap);
348
- const changed2 = applyRegisterReferenceWrapping(code, s, hashToFileMap);
349
- if (changed1 || changed2) {
350
- return {
351
- code: s.toString(),
352
- map: s.generateMap({
353
- source: chunk.fileName,
354
- includeContent: true,
355
- }),
356
- };
348
+ const start = counterRender ? performance.now() : 0;
349
+ try {
350
+ // Only RSC bundle should get file paths for revalidation matching
351
+ // SSR bundle must NOT use file paths because client components run there
352
+ // and need to match the client bundle during hydration (otherwise: error #418)
353
+ const isRscEnv = this.environment?.name === "rsc";
354
+
355
+ // Only use file path mapping for RSC environment
356
+ const effectiveMap = isRscEnv ? hashToFileMap : undefined;
357
+
358
+ // For RSC bundles, both createServerReference and registerServerReference
359
+ // may need transforming. Use a single MagicString for correct sourcemaps.
360
+ if (isRscEnv && hashToFileMap) {
361
+ const s = new MagicString(code);
362
+ const changed1 = applyServerReferenceWrapping(code, s, effectiveMap);
363
+ const changed2 = applyRegisterReferenceWrapping(
364
+ code,
365
+ s,
366
+ hashToFileMap,
367
+ );
368
+ if (changed1 || changed2) {
369
+ return {
370
+ code: s.toString(),
371
+ map: s.generateMap({
372
+ source: chunk.fileName,
373
+ includeContent: true,
374
+ }),
375
+ };
376
+ }
377
+ return null;
357
378
  }
358
- return null;
359
- }
360
379
 
361
- // Non-RSC environments: only transform createServerReference calls
362
- return transformServerReferences(code, chunk.fileName, effectiveMap);
380
+ // Non-RSC environments: only transform createServerReference calls
381
+ return transformServerReferences(code, chunk.fileName, effectiveMap);
382
+ } finally {
383
+ counterRender?.record(chunk.fileName, performance.now() - start);
384
+ }
363
385
  },
364
386
  };
365
387
  }
@@ -19,6 +19,18 @@ export function hashId(filePath: string, exportName: string): string {
19
19
  return `${hash.slice(0, 8)}#${exportName}`;
20
20
  }
21
21
 
22
+ /**
23
+ * Build a stable ID for an export binding. Uses hashed IDs in production
24
+ * builds (short + opaque) and readable path#name IDs in dev.
25
+ */
26
+ export function makeStubId(
27
+ filePath: string,
28
+ exportName: string,
29
+ isBuild: boolean,
30
+ ): string {
31
+ return isBuild ? hashId(filePath, exportName) : `${filePath}#${exportName}`;
32
+ }
33
+
22
34
  /**
23
35
  * Generate an 8-char hex hash for an inline static handler call site.
24
36
  * Uses file path and line number (plus optional index for same-line collisions).
@@ -138,6 +138,36 @@ export function generateExprStubs(
138
138
  };
139
139
  }
140
140
 
141
+ /**
142
+ * Replace handler call expressions with lightweight stub objects on an
143
+ * existing MagicString. Unlike generateExprStubs (which creates its own
144
+ * MagicString and returns the full result), this integrates into the
145
+ * unified transform pipeline so all transforms share one sourcemap.
146
+ */
147
+ export function stubHandlerExprs(
148
+ cfg: HandlerTransformConfig,
149
+ bindings: CreateExportBinding[],
150
+ s: MagicString,
151
+ filePath: string,
152
+ isBuild: boolean,
153
+ ): boolean {
154
+ let hasChanges = false;
155
+ for (const binding of bindings) {
156
+ const exportName = binding.exportNames[0];
157
+ const handlerId = isBuild
158
+ ? hashId(filePath, exportName)
159
+ : `${filePath}#${exportName}`;
160
+
161
+ s.overwrite(
162
+ binding.callExprStart,
163
+ binding.callCloseParenPos + 1,
164
+ `{ __brand: "${cfg.brand}", $$id: "${handlerId}" }`,
165
+ );
166
+ hasChanges = true;
167
+ }
168
+ return hasChanges;
169
+ }
170
+
141
171
  /**
142
172
  * Inject $$id into export const handler calls in RSC environments.
143
173
  */
@@ -4,6 +4,9 @@ import path from "node:path";
4
4
  import { createHash } from "node:crypto";
5
5
  import { normalizePath, findMatchingParen } from "../expose-id-utils.js";
6
6
  import { getImportedFnNames } from "./export-analysis.js";
7
+ import { createRangoDebugger, createCounter, NS } from "../../debug.js";
8
+
9
+ const debug = createRangoDebugger(NS.transform);
7
10
 
8
11
  export function transformRouter(
9
12
  code: string,
@@ -82,11 +85,15 @@ export function transformRouter(
82
85
  */
83
86
  export function exposeRouterId(): Plugin {
84
87
  let projectRoot = "";
88
+ const counter = createCounter(debug, "expose-router-id");
85
89
  return {
86
90
  name: "@rangojs/router:expose-router-id",
87
91
  configResolved(config) {
88
92
  projectRoot = config.root;
89
93
  },
94
+ buildEnd() {
95
+ counter?.flush();
96
+ },
90
97
  transform(code, id) {
91
98
  if (!code.includes("createRouter")) return null;
92
99
  // Accepts both @rangojs/router and @rangojs/router/server subpath.
@@ -102,9 +109,19 @@ export function exposeRouterId(): Plugin {
102
109
  }
103
110
  if (id.includes("node_modules")) return null;
104
111
 
105
- const filePath = normalizePath(path.relative(projectRoot, id));
106
- const routerFnNames = getImportedFnNames(code, "createRouter");
107
- return transformRouter(code, filePath, routerFnNames, normalizePath(id));
112
+ const start = counter ? performance.now() : 0;
113
+ try {
114
+ const filePath = normalizePath(path.relative(projectRoot, id));
115
+ const routerFnNames = getImportedFnNames(code, "createRouter");
116
+ return transformRouter(
117
+ code,
118
+ filePath,
119
+ routerFnNames,
120
+ normalizePath(id),
121
+ );
122
+ } finally {
123
+ counter?.record(id, performance.now() - start);
124
+ }
108
125
  },
109
126
  };
110
127
  }