Skip to content

Commit fa7f933

Browse files
committed
Remove dynamic hash and collapse into _explodeOptionalSegments
1 parent 647f412 commit fa7f933

File tree

1 file changed

+30
-62
lines changed

1 file changed

+30
-62
lines changed

packages/router/utils.ts

Lines changed: 30 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -440,9 +440,20 @@ function flattenRoutes<
440440
}
441441

442442
/**
443-
* Computes all combinations of optional path segments for a given path.
443+
* Computes all combinations of optional path segments for a given path,
444+
* excluding combinations that are ambiguous and of lower priority.
445+
*
446+
* For example, `/one/:two?/three/:four?/:five?` explodes to:
447+
* - `/one/three`
448+
* - `/one/:two/three`
449+
* - `/one/three/:four`
450+
* - `/one/three/:five`
451+
* - `/one/:two/three/:four`
452+
* - `/one/:two/three/:five`
453+
* - `/one/three/:four/:five`
454+
* - `/one/:two/three/:four/:five`
444455
*/
445-
let _explodeOptionalSegments = (path: string): string[] => {
456+
function explodeOptionalSegments(path: string): string[] {
446457
let segments = path.split("/");
447458
if (segments.length === 0) return [];
448459

@@ -459,66 +470,23 @@ let _explodeOptionalSegments = (path: string): string[] => {
459470
return isOptional ? ["", required] : [required];
460471
}
461472

462-
let restExploded = _explodeOptionalSegments(rest.join("/"));
463-
return restExploded.flatMap((subpath) => {
464-
// /one + / + :two/three -> /one/:two/three
465-
let requiredExploded = subpath === "" ? required : required + "/" + subpath;
466-
// For optional segments, return the exploded path _without_ current segment first (`subpath`)
467-
// and exploded path _with_ current segment later (`subpath`)
468-
// This ensures that exploded paths are emitted in priority order
469-
// `/one/three/:four` will come before `/one/three/:five`
470-
return isOptional ? [subpath, requiredExploded] : [requiredExploded];
471-
});
472-
};
473-
474-
/**
475-
* Computes all combinations of optional path segments for a given path,
476-
* excluding combinations that are ambiguous and of lower priority.
477-
*
478-
* For example, `/one/:two?/three/:four?/:five?` explodes to:
479-
* - `/one/three`
480-
* - `/one/:two/three`
481-
* - `/one/three/:four`
482-
* - `/one/:two/three/:four`
483-
* - `/one/three/:four/:five`
484-
* - `/one/:two/three/:four/:five`
485-
*
486-
* Note that these paths are not returned:
487-
* - `/one/three/:five` (because `/one/three/:four` has priority)
488-
* - `/one/:two/three/:five` (because `/one/:two/three/:four` has priority)
489-
*/
490-
let explodeOptionalSegments = (path: string) => {
491-
let result: string[] = [];
492-
// Compute hash for dynamic path segments
493-
// /one/:two/three/:four -> /one/:/three/:
494-
let dynamicHash = (subpath: string) =>
495-
subpath
496-
.split("/")
497-
.map((segment) => (segment.startsWith(":") ? ":" : segment))
498-
.join("/");
499-
500-
let dynamicHashes = new Set<string>();
501-
for (let exploded of _explodeOptionalSegments(path)) {
502-
let hash = dynamicHash(exploded);
503-
504-
// `/one/three/:four` and `/one/three/:five` have the same hash: `/one/three/:`
505-
// so we only emit the first one of these we come across
506-
// _explodeOptionalSegments returns exploded paths in priority order,
507-
// so `/one/three/:four` will come before `/one/three/:five`
508-
if (dynamicHashes.has(hash)) continue;
509-
510-
dynamicHashes.add(hash);
511-
512-
// for absolute paths, ensure `/` instead of empty segment
513-
if (path.startsWith("/") && exploded === "") {
514-
result.push("/");
515-
continue;
516-
}
517-
518-
result.push(exploded);
519-
}
520-
return result;
521-
};
473+
let restExploded = explodeOptionalSegments(rest.join("/"));
474+
return restExploded
475+
.flatMap((subpath) => {
476+
// /one + / + :two/three -> /one/:two/three
477+
let requiredExploded =
478+
subpath === "" ? required : required + "/" + subpath;
479+
// For optional segments, return the exploded path _without_ current segment first (`subpath`)
480+
// and exploded path _with_ current segment later (`subpath`)
481+
// This ensures that exploded paths are emitted in priority order
482+
// `/one/three/:four` will come before `/one/three/:five`
483+
return isOptional ? [subpath, requiredExploded] : [requiredExploded];
484+
})
485+
.map((exploded) => {
486+
// for absolute paths, ensure `/` instead of empty segment
487+
return path.startsWith("/") && exploded === "" ? "/" : exploded;
488+
});
489+
}
522490

523491
function rankRouteBranches(branches: RouteBranch[]): void {
524492
branches.sort((a, b) =>

0 commit comments

Comments
 (0)