@rangojs/router 0.0.0-experimental.60 → 0.0.0-experimental.61

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.
@@ -1745,7 +1745,7 @@ import { resolve } from "node:path";
1745
1745
  // package.json
1746
1746
  var package_default = {
1747
1747
  name: "@rangojs/router",
1748
- version: "0.0.0-experimental.60",
1748
+ version: "0.0.0-experimental.61",
1749
1749
  description: "Django-inspired RSC router with composable URL patterns",
1750
1750
  keywords: [
1751
1751
  "react",
@@ -3456,6 +3456,7 @@ function encodePathParam(value) {
3456
3456
  }
3457
3457
  function substituteRouteParams(pattern, params, encode = encodeURIComponent) {
3458
3458
  let result = pattern;
3459
+ let hadOmittedOptional = false;
3459
3460
  for (const [key, value] of Object.entries(params)) {
3460
3461
  const escaped = escapeRegExp2(key);
3461
3462
  result = result.replace(
@@ -3464,6 +3465,13 @@ function substituteRouteParams(pattern, params, encode = encodeURIComponent) {
3464
3465
  );
3465
3466
  result = result.replace(`*${key}`, encode(value));
3466
3467
  }
3468
+ result = result.replace(/:([a-zA-Z_][a-zA-Z0-9_]*)(\([^)]*\))?\?/g, () => {
3469
+ hadOmittedOptional = true;
3470
+ return "";
3471
+ });
3472
+ if (hadOmittedOptional) {
3473
+ result = result.replace(/\/\/+/g, "/").replace(/\/+$/, "") || "/";
3474
+ }
3467
3475
  return result;
3468
3476
  }
3469
3477
  async function runWithConcurrency(items, concurrency, fn) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rangojs/router",
3
- "version": "0.0.0-experimental.60",
3
+ "version": "0.0.0-experimental.61",
4
4
  "description": "Django-inspired RSC router with composable URL patterns",
5
5
  "keywords": [
6
6
  "react",
@@ -31,6 +31,7 @@ export function encodePathParam(value: unknown): string {
31
31
  /**
32
32
  * Substitute route params into a pattern, stripping constraint and optional
33
33
  * syntax (:param(a|b)? -> value). Also handles wildcard params (*key).
34
+ * Optional params not present in `params` are removed from the output.
34
35
  */
35
36
  export function substituteRouteParams(
36
37
  pattern: string,
@@ -38,6 +39,9 @@ export function substituteRouteParams(
38
39
  encode: (value: string) => string = encodeURIComponent,
39
40
  ): string {
40
41
  let result = pattern;
42
+ let hadOmittedOptional = false;
43
+
44
+ // First pass: substitute provided params
41
45
  for (const [key, value] of Object.entries(params)) {
42
46
  const escaped = escapeRegExp(key);
43
47
  result = result.replace(
@@ -46,6 +50,18 @@ export function substituteRouteParams(
46
50
  );
47
51
  result = result.replace(`*${key}`, encode(value));
48
52
  }
53
+
54
+ // Second pass: strip remaining optional param placeholders not in params
55
+ result = result.replace(/:([a-zA-Z_][a-zA-Z0-9_]*)(\([^)]*\))?\?/g, () => {
56
+ hadOmittedOptional = true;
57
+ return "";
58
+ });
59
+
60
+ // Clean up slashes from omitted optional segments
61
+ if (hadOmittedOptional) {
62
+ result = result.replace(/\/\/+/g, "/").replace(/\/+$/, "") || "/";
63
+ }
64
+
49
65
  return result;
50
66
  }
51
67