55 isRouteName ,
66} from '../types'
77import { createRouterError , ErrorTypes , MatcherError } from '../errors'
8+ import { createMatcherTree , isMatchable } from './matcherTree'
89import { createRouteRecordMatcher , RouteRecordMatcher } from './pathMatcher'
910import { RouteRecordNormalized } from './types'
1011
@@ -14,8 +15,6 @@ import type {
1415 _PathParserOptions ,
1516} from './pathParserRanker'
1617
17- import { comparePathParserScore } from './pathParserRanker'
18-
1918import { warn } from '../warning'
2019import { assign , noop } from '../utils'
2120import type { RouteRecordNameGeneric , _RouteRecordProps } from '../typed-routes'
@@ -58,8 +57,8 @@ export function createRouterMatcher(
5857 routes : Readonly < RouteRecordRaw [ ] > ,
5958 globalOptions : PathParserOptions
6059) : RouterMatcher {
61- // normalized ordered array of matchers
62- const matchers : RouteRecordMatcher [ ] = [ ]
60+ // normalized ordered tree of matchers
61+ const matcherTree = createMatcherTree ( )
6362 const matcherMap = new Map <
6463 NonNullable < RouteRecordNameGeneric > ,
6564 RouteRecordMatcher
@@ -203,28 +202,24 @@ export function createRouterMatcher(
203202 const matcher = matcherMap . get ( matcherRef )
204203 if ( matcher ) {
205204 matcherMap . delete ( matcherRef )
206- matchers . splice ( matchers . indexOf ( matcher ) , 1 )
205+ matcherTree . remove ( matcher )
207206 matcher . children . forEach ( removeRoute )
208207 matcher . alias . forEach ( removeRoute )
209208 }
210209 } else {
211- const index = matchers . indexOf ( matcherRef )
212- if ( index > - 1 ) {
213- matchers . splice ( index , 1 )
214- if ( matcherRef . record . name ) matcherMap . delete ( matcherRef . record . name )
215- matcherRef . children . forEach ( removeRoute )
216- matcherRef . alias . forEach ( removeRoute )
217- }
210+ matcherTree . remove ( matcherRef )
211+ if ( matcherRef . record . name ) matcherMap . delete ( matcherRef . record . name )
212+ matcherRef . children . forEach ( removeRoute )
213+ matcherRef . alias . forEach ( removeRoute )
218214 }
219215 }
220216
221217 function getRoutes ( ) {
222- return matchers
218+ return matcherTree . toArray ( )
223219 }
224220
225221 function insertMatcher ( matcher : RouteRecordMatcher ) {
226- const index = findInsertionIndex ( matcher , matchers )
227- matchers . splice ( index , 0 , matcher )
222+ matcherTree . add ( matcher )
228223 // only add the original record to the name map
229224 if ( matcher . record . name && ! isAliasRecord ( matcher ) )
230225 matcherMap . set ( matcher . record . name , matcher )
@@ -297,7 +292,7 @@ export function createRouterMatcher(
297292 )
298293 }
299294
300- matcher = matchers . find ( m => m . re . test ( path ) )
295+ matcher = matcherTree . find ( path )
301296 // matcher should have a value after the loop
302297
303298 if ( matcher ) {
@@ -310,7 +305,7 @@ export function createRouterMatcher(
310305 // match by name or path of current route
311306 matcher = currentLocation . name
312307 ? matcherMap . get ( currentLocation . name )
313- : matchers . find ( m => m . re . test ( currentLocation . path ) )
308+ : matcherTree . find ( currentLocation . path )
314309 if ( ! matcher )
315310 throw createRouterError < MatcherError > ( ErrorTypes . MATCHER_NOT_FOUND , {
316311 location,
@@ -345,7 +340,7 @@ export function createRouterMatcher(
345340 routes . forEach ( route => addRoute ( route ) )
346341
347342 function clearRoutes ( ) {
348- matchers . length = 0
343+ matcherTree . clear ( )
349344 matcherMap . clear ( )
350345 }
351346
@@ -528,79 +523,4 @@ function checkMissingParamsInAbsolutePath(
528523 }
529524}
530525
531- /**
532- * Performs a binary search to find the correct insertion index for a new matcher.
533- *
534- * Matchers are primarily sorted by their score. If scores are tied then we also consider parent/child relationships,
535- * with descendants coming before ancestors. If there's still a tie, new routes are inserted after existing routes.
536- *
537- * @param matcher - new matcher to be inserted
538- * @param matchers - existing matchers
539- */
540- function findInsertionIndex (
541- matcher : RouteRecordMatcher ,
542- matchers : RouteRecordMatcher [ ]
543- ) {
544- // First phase: binary search based on score
545- let lower = 0
546- let upper = matchers . length
547-
548- while ( lower !== upper ) {
549- const mid = ( lower + upper ) >> 1
550- const sortOrder = comparePathParserScore ( matcher , matchers [ mid ] )
551-
552- if ( sortOrder < 0 ) {
553- upper = mid
554- } else {
555- lower = mid + 1
556- }
557- }
558-
559- // Second phase: check for an ancestor with the same score
560- const insertionAncestor = getInsertionAncestor ( matcher )
561-
562- if ( insertionAncestor ) {
563- upper = matchers . lastIndexOf ( insertionAncestor , upper - 1 )
564-
565- if ( __DEV__ && upper < 0 ) {
566- // This should never happen
567- warn (
568- `Finding ancestor route "${ insertionAncestor . record . path } " failed for "${ matcher . record . path } "`
569- )
570- }
571- }
572-
573- return upper
574- }
575-
576- function getInsertionAncestor ( matcher : RouteRecordMatcher ) {
577- let ancestor : RouteRecordMatcher | undefined = matcher
578-
579- while ( ( ancestor = ancestor . parent ) ) {
580- if (
581- isMatchable ( ancestor ) &&
582- comparePathParserScore ( matcher , ancestor ) === 0
583- ) {
584- return ancestor
585- }
586- }
587-
588- return
589- }
590-
591- /**
592- * Checks if a matcher can be reachable. This means if it's possible to reach it as a route. For example, routes without
593- * a component, or name, or redirect, are just used to group other routes.
594- * @param matcher
595- * @param matcher.record record of the matcher
596- * @returns
597- */
598- function isMatchable ( { record } : RouteRecordMatcher ) : boolean {
599- return ! ! (
600- record . name ||
601- ( record . components && Object . keys ( record . components ) . length ) ||
602- record . redirect
603- )
604- }
605-
606526export type { PathParserOptions , _PathParserOptions }
0 commit comments