@jsenv/navi 0.29.26 → 0.29.27

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.
@@ -2775,6 +2775,32 @@ const createRoutePattern = (pattern, { searchParams = {} } = {}) => {
2775
2775
  return false;
2776
2776
  }
2777
2777
 
2778
+ // Descending into a child path param means "this URL keeps the value you
2779
+ // are on" — right for a param that QUALIFIES a position (a tab, a mode),
2780
+ // wrong for one that NAMES a page. Two things must both hold for it to be
2781
+ // a name:
2782
+ //
2783
+ // - literal routes are declared for its other values ("/games/me/done").
2784
+ // Declaring them is the developer saying these values are places; a tab
2785
+ // nobody named a route after stays a qualifier;
2786
+ // - it has a default value, which makes THIS url the url of that default:
2787
+ // "/games/me" IS section=a-venir. Descending would leave the default
2788
+ // unaddressable — two states, one url. Without a default ("/map" is not
2789
+ // a panel, it is the absence of one) this url means nothing yet and
2790
+ // stays free to remember where you were.
2791
+ const thisUrlAlreadyMeansAParamValue = (connection) => {
2792
+ if (connection.paramType !== "path") {
2793
+ return false;
2794
+ }
2795
+ if (pathConnectionMap.has(connection.paramName)) {
2796
+ return false; // we carry that param ourselves, we are not its default
2797
+ }
2798
+ if (!connection.namedByLiteralRoutes) {
2799
+ return false;
2800
+ }
2801
+ return connection.getDefaultValue() !== undefined;
2802
+ };
2803
+
2778
2804
  // Check if child has active non-default signal values
2779
2805
  let hasActiveParams = false;
2780
2806
  const childParams = { ...compatibility.childParams };
@@ -2801,7 +2827,10 @@ const createRoutePattern = (pattern, { searchParams = {} } = {}) => {
2801
2827
  if (signalValue !== undefined) {
2802
2828
  // No explicit override - use signal value
2803
2829
  childParams[paramName] = signalValue;
2804
- if (connection.isCustomValue(signalValue)) {
2830
+ if (
2831
+ connection.isCustomValue(signalValue) &&
2832
+ !thisUrlAlreadyMeansAParamValue(connection)
2833
+ ) {
2805
2834
  hasActiveParams = true;
2806
2835
  }
2807
2836
  }
@@ -5323,6 +5352,57 @@ const setupRoutePatterns = (routePatterns) => {
5323
5352
  collectDescendantPathSignals(routePattern);
5324
5353
  routePattern.descendantPathSignals = descendantPathSignalsByIndex;
5325
5354
  }
5355
+ // Phase 5b: Flag path params whose values are ALSO declared as literal routes
5356
+ // ("/games/me/done" next to "/games/me/:section"). That declaration is the
5357
+ // only reliable statement that the param names pages rather than qualifying
5358
+ // one — read by shouldUseChildRoute to decide whether an ancestor url may
5359
+ // descend into it.
5360
+ for (const routePattern of routePatternSet) {
5361
+ for (const connection of routePattern.connections) {
5362
+ if (connection.paramType !== "path") {
5363
+ continue;
5364
+ }
5365
+ const paramSegment = routePattern.pattern.segments.find(
5366
+ (seg) => seg.type === "param" && seg.name === connection.paramName,
5367
+ );
5368
+ if (!paramSegment) {
5369
+ continue;
5370
+ }
5371
+ const { index } = paramSegment;
5372
+ const sharesPathUpTo = (otherSegments) => {
5373
+ for (let i = 0; i < index; i++) {
5374
+ const seg = routePattern.pattern.segments[i];
5375
+ const otherSeg = otherSegments[i];
5376
+ if (!otherSeg) {
5377
+ return false;
5378
+ }
5379
+ if (seg.type === "literal" && otherSeg.type === "literal") {
5380
+ if (seg.value !== otherSeg.value) {
5381
+ return false;
5382
+ }
5383
+ } else if (seg.type !== otherSeg.type) {
5384
+ return false;
5385
+ }
5386
+ }
5387
+ return true;
5388
+ };
5389
+ for (const otherPattern of routePatternSet) {
5390
+ if (otherPattern === routePattern) {
5391
+ continue;
5392
+ }
5393
+ const otherSegments = otherPattern.pattern.segments;
5394
+ const otherSegment = otherSegments[index];
5395
+ if (!otherSegment || otherSegment.type !== "literal") {
5396
+ continue;
5397
+ }
5398
+ if (!sharesPathUpTo(otherSegments)) {
5399
+ continue;
5400
+ }
5401
+ connection.namedByLiteralRoutes = true;
5402
+ break;
5403
+ }
5404
+ }
5405
+ }
5326
5406
  // Phase 6: Calculate depths for all patterns
5327
5407
  for (const routePattern of routePatternSet) {
5328
5408
  calculatePatternDepth(routePattern);