@rangojs/router 0.0.0-experimental.104 → 0.0.0-experimental.105

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.
@@ -2040,7 +2040,7 @@ import { resolve } from "node:path";
2040
2040
  // package.json
2041
2041
  var package_default = {
2042
2042
  name: "@rangojs/router",
2043
- version: "0.0.0-experimental.104",
2043
+ version: "0.0.0-experimental.105",
2044
2044
  description: "Django-inspired RSC router with composable URL patterns",
2045
2045
  keywords: [
2046
2046
  "react",
@@ -3842,9 +3842,12 @@ function checkSelfGenWrite(state, filePath, consume) {
3842
3842
 
3843
3843
  // src/vite/utils/manifest-utils.ts
3844
3844
  function flattenLeafEntries(prefixTree, routeManifest, result) {
3845
- function visit(node) {
3845
+ function visit(node, ancestorStaticPrefixes) {
3846
3846
  const children = node.children || {};
3847
3847
  if (Object.keys(children).length === 0 && node.routes && node.routes.length > 0) {
3848
+ if (ancestorStaticPrefixes.has(node.staticPrefix)) {
3849
+ return;
3850
+ }
3848
3851
  const routes = {};
3849
3852
  for (const name of node.routes) {
3850
3853
  if (name in routeManifest) {
@@ -3853,13 +3856,15 @@ function flattenLeafEntries(prefixTree, routeManifest, result) {
3853
3856
  }
3854
3857
  result.push({ staticPrefix: node.staticPrefix, routes });
3855
3858
  } else {
3859
+ const nextAncestors = new Set(ancestorStaticPrefixes);
3860
+ nextAncestors.add(node.staticPrefix);
3856
3861
  for (const child of Object.values(children)) {
3857
- visit(child);
3862
+ visit(child, nextAncestors);
3858
3863
  }
3859
3864
  }
3860
3865
  }
3861
3866
  for (const node of Object.values(prefixTree)) {
3862
- visit(node);
3867
+ visit(node, /* @__PURE__ */ new Set());
3863
3868
  }
3864
3869
  }
3865
3870
  function buildRouteToStaticPrefix(prefixTree, result) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rangojs/router",
3
- "version": "0.0.0-experimental.104",
3
+ "version": "0.0.0-experimental.105",
4
4
  "description": "Django-inspired RSC router with composable URL patterns",
5
5
  "keywords": [
6
6
  "react",
@@ -4,20 +4,33 @@
4
4
  * used directly by evaluateLazyEntry() without running the handler.
5
5
  * Non-leaf nodes are skipped because they have nested lazy includes that
6
6
  * require the handler to run for discovery.
7
+ *
8
+ * A leaf is also skipped when its staticPrefix collides with an ancestor
9
+ * include node's staticPrefix. That happens when a dynamic param collapses the
10
+ * staticPrefix of nested includes onto the parent's (e.g. `/m/:id/edit` -> sp
11
+ * `/m`): precomputing such a leaf under the collapsed prefix would let the
12
+ * ancestor's lazy entry claim a route it cannot register (the route is behind
13
+ * further nested lazy includes), producing a RouteNotFoundError at request time
14
+ * (issue #506). Those routes are resolved via the handler chain instead.
7
15
  */
8
16
  export function flattenLeafEntries(
9
17
  prefixTree: Record<string, any>,
10
18
  routeManifest: Record<string, string>,
11
19
  result: Array<{ staticPrefix: string; routes: Record<string, string> }>,
12
20
  ): void {
13
- function visit(node: any): void {
21
+ function visit(node: any, ancestorStaticPrefixes: Set<string>): void {
14
22
  const children = node.children || {};
15
23
  if (
16
24
  Object.keys(children).length === 0 &&
17
25
  node.routes &&
18
26
  node.routes.length > 0
19
27
  ) {
20
- // Leaf node: collect its routes from the manifest
28
+ // Leaf node. Skip if its staticPrefix collides with an ancestor include
29
+ // node's staticPrefix (dynamic-param collapse) — see doc comment above.
30
+ if (ancestorStaticPrefixes.has(node.staticPrefix)) {
31
+ return;
32
+ }
33
+ // Collect its routes from the manifest
21
34
  const routes: Record<string, string> = {};
22
35
  for (const name of node.routes) {
23
36
  if (name in routeManifest) {
@@ -26,14 +39,17 @@ export function flattenLeafEntries(
26
39
  }
27
40
  result.push({ staticPrefix: node.staticPrefix, routes });
28
41
  } else {
29
- // Non-leaf: recurse into children
42
+ // Non-leaf: recurse into children, tracking this node's staticPrefix as
43
+ // an ancestor so a collapsed nested leaf below it is not over-claimed.
44
+ const nextAncestors = new Set(ancestorStaticPrefixes);
45
+ nextAncestors.add(node.staticPrefix);
30
46
  for (const child of Object.values(children)) {
31
- visit(child);
47
+ visit(child, nextAncestors);
32
48
  }
33
49
  }
34
50
  }
35
51
  for (const node of Object.values(prefixTree)) {
36
- visit(node);
52
+ visit(node, new Set());
37
53
  }
38
54
  }
39
55