@@ -368,6 +368,9 @@ function sortDynamic(
368368 if ( ! a . suffix && b . suffix ) return 1
369369 if ( a . caseSensitive && ! b . caseSensitive ) return - 1
370370 if ( ! a . caseSensitive && b . caseSensitive ) return 1
371+
372+ // we don't need a tiebreaker here
373+ // at this point the 2 nodes cannot conflict during matching
371374 return 0
372375}
373376
@@ -507,20 +510,6 @@ type SegmentNode<T extends RouteLike> = {
507510 notFound : T | null
508511}
509512
510- // function intoRouteLike(routeTree, parent) {
511- // const route = {
512- // id: routeTree.id,
513- // fullPath: routeTree.fullPath,
514- // path: routeTree.path,
515- // isRoot: routeTree.isRoot,
516- // options: routeTree.options && 'caseSensitive' in routeTree.options ? { caseSensitive: routeTree.options.caseSensitive } : undefined,
517- // }
518- // if (routeTree.children) {
519- // route.children = routeTree.children.map(child => intoRouteLike(child, route))
520- // }
521- // return route
522- // }
523-
524513type RouteLike = {
525514 path ?: string // relative path from the parent,
526515 children ?: Array < RouteLike > // child routes,
@@ -774,7 +763,7 @@ function extractParams<T extends RouteLike>(
774763 node . suffix || node . prefix
775764 ? part ! . substring ( preLength , part ! . length - sufLength )
776765 : part
777- if ( value ?. length ) params [ name ] = decodeURIComponent ( value )
766+ if ( value ) params [ name ] = decodeURIComponent ( value )
778767 } else if ( node . kind === SEGMENT_TYPE_WILDCARD ) {
779768 const n = node
780769 const value = path . substring (
@@ -800,6 +789,24 @@ function buildBranch<T extends RouteLike>(node: AnySegmentNode<T>) {
800789 return list
801790}
802791
792+ type MatchStackFrame < T extends RouteLike > = {
793+ node : AnySegmentNode < T >
794+ /** index of the segment of path */
795+ index : number
796+ /** how many nodes between `node` and the root of the segment tree */
797+ depth : number
798+ /**
799+ * Bitmask of skipped optional segments.
800+ *
801+ * This is a very performant way of storing an "array of booleans", but it means beyond 32 segments we can't track skipped optionals.
802+ * If we really really need to support more than 32 segments we can switch to using a `BigInt` here. It's about 2x slower in worst case scenarios.
803+ */
804+ skipped : number
805+ statics : number
806+ dynamics : number
807+ optionals : number
808+ }
809+
803810function getNodeMatch < T extends RouteLike > (
804811 path : string ,
805812 parts : Array < string > ,
@@ -810,23 +817,7 @@ function getNodeMatch<T extends RouteLike>(
810817 const pathIsIndex = trailingSlash && path !== '/'
811818 const partsLength = parts . length - ( trailingSlash ? 1 : 0 )
812819
813- type Frame = {
814- node : AnySegmentNode < T >
815- /** index of the segment of path */
816- index : number
817- /** how many nodes between `node` and the root of the segment tree */
818- depth : number
819- /**
820- * Bitmask of skipped optional segments.
821- *
822- * This is a very performant way of storing an "array of booleans", but it means beyond 32 segments we can't track skipped optionals.
823- * If we really really need to support more than 32 segments we can switch to using a `BigInt` here. It's about 2x slower in worst case scenarios.
824- */
825- skipped : number
826- statics : number
827- dynamics : number
828- optionals : number
829- }
820+ type Frame = MatchStackFrame < T >
830821
831822 // use a stack to explore all possible paths (params cause branching)
832823 // iterate "backwards" (low priority first) so that we can push() each candidate, and pop() the highest priority candidate first
@@ -839,8 +830,8 @@ function getNodeMatch<T extends RouteLike>(
839830 {
840831 node : segmentTree ,
841832 index : 1 ,
842- depth : 1 ,
843833 skipped : 0 ,
834+ depth : 1 ,
844835 statics : 1 ,
845836 dynamics : 0 ,
846837 optionals : 0 ,
@@ -857,30 +848,14 @@ function getNodeMatch<T extends RouteLike>(
857848 let { node, index, skipped, depth, statics, dynamics, optionals } = frame
858849
859850 // In fuzzy mode, track the best partial match we've found so far
860- if (
861- fuzzy &&
862- node . notFound &&
863- ( ! bestFuzzy ||
864- statics > bestFuzzy . statics ||
865- ( statics === bestFuzzy . statics &&
866- ( dynamics > bestFuzzy . dynamics ||
867- ( dynamics === bestFuzzy . dynamics &&
868- optionals > bestFuzzy . optionals ) ) ) )
869- ) {
851+ if ( fuzzy && node . notFound && isFrameMoreSpecific ( bestFuzzy , frame ) ) {
870852 bestFuzzy = frame
871853 }
872854
873855 const isBeyondPath = index === partsLength
874856 if ( isBeyondPath ) {
875857 if ( node . route && ( ! pathIsIndex || node . isIndex ) ) {
876- if (
877- ! bestMatch ||
878- statics > bestMatch . statics ||
879- ( statics === bestMatch . statics &&
880- ( dynamics > bestMatch . dynamics ||
881- ( dynamics === bestMatch . dynamics &&
882- optionals > bestMatch . optionals ) ) )
883- ) {
858+ if ( isFrameMoreSpecific ( bestMatch , frame ) ) {
884859 bestMatch = frame
885860 }
886861
@@ -895,16 +870,7 @@ function getNodeMatch<T extends RouteLike>(
895870 let lowerPart : string
896871
897872 // 5. Try wildcard match
898- if (
899- node . wildcard &&
900- ( ! wildcardMatch ||
901- statics > wildcardMatch . statics ||
902- ( statics === wildcardMatch . statics &&
903- dynamics > wildcardMatch . dynamics ) ||
904- ( statics === wildcardMatch . statics &&
905- dynamics === wildcardMatch . dynamics &&
906- optionals > wildcardMatch . optionals ) )
907- ) {
873+ if ( node . wildcard && isFrameMoreSpecific ( wildcardMatch , frame ) ) {
908874 for ( const segment of node . wildcard ) {
909875 const { prefix, suffix } = segment
910876 if ( prefix ) {
@@ -1053,3 +1019,18 @@ function getNodeMatch<T extends RouteLike>(
10531019
10541020 return null
10551021}
1022+
1023+ function isFrameMoreSpecific (
1024+ // the stack frame previously saved as "best match"
1025+ prev : MatchStackFrame < any > | null ,
1026+ // the candidate stack frame
1027+ next : MatchStackFrame < any > ,
1028+ ) : boolean {
1029+ if ( ! prev ) return true
1030+ return (
1031+ next . statics > prev . statics ||
1032+ ( next . statics === prev . statics &&
1033+ ( next . dynamics > prev . dynamics ||
1034+ ( next . dynamics === prev . dynamics && next . optionals > prev . optionals ) ) )
1035+ )
1036+ }
0 commit comments