55 isRouteName ,
66} from '../types'
77import { createRouterError , ErrorTypes , MatcherError } from '../errors'
8+ import { createMatcherTree } from './matcherTree'
89import { createRouteRecordMatcher , RouteRecordMatcher } from './pathMatcher'
910import { RouteRecordNormalized } from './types'
1011
@@ -14,11 +15,9 @@ import type {
1415 _PathParserOptions ,
1516} from './pathParserRanker'
1617
17- import { comparePathParserScore } from './pathParserRanker'
18-
1918import { warn } from '../warning'
2019import { assign , noop } from '../utils'
21- import type { RouteRecordNameGeneric , _RouteRecordProps } from '../typed-routes'
20+ import type { RouteRecordName , _RouteRecordProps } from '../typed-routes'
2221
2322/**
2423 * Internal RouterMatcher
@@ -27,13 +26,13 @@ import type { RouteRecordNameGeneric, _RouteRecordProps } from '../typed-routes'
2726 */
2827export interface RouterMatcher {
2928 addRoute : ( record : RouteRecordRaw , parent ?: RouteRecordMatcher ) => ( ) => void
30- removeRoute ( matcher : RouteRecordMatcher ) : void
31- removeRoute ( name : NonNullable < RouteRecordNameGeneric > ) : void
29+ removeRoute : {
30+ ( matcher : RouteRecordMatcher ) : void
31+ ( name : RouteRecordName ) : void
32+ }
3233 clearRoutes : ( ) => void
3334 getRoutes : ( ) => RouteRecordMatcher [ ]
34- getRecordMatcher : (
35- name : NonNullable < RouteRecordNameGeneric >
36- ) => RouteRecordMatcher | undefined
35+ getRecordMatcher : ( name : RouteRecordName ) => RouteRecordMatcher | undefined
3736
3837 /**
3938 * Resolves a location. Gives access to the route record that corresponds to the actual path as well as filling the corresponding params objects
@@ -58,18 +57,15 @@ export function createRouterMatcher(
5857 routes : Readonly < RouteRecordRaw [ ] > ,
5958 globalOptions : PathParserOptions
6059) : RouterMatcher {
61- // normalized ordered array of matchers
62- const matchers : RouteRecordMatcher [ ] = [ ]
63- const matcherMap = new Map <
64- NonNullable < RouteRecordNameGeneric > ,
65- RouteRecordMatcher
66- > ( )
60+ // normalized ordered tree of matchers
61+ const matcherTree = createMatcherTree ( )
62+ const matcherMap = new Map < RouteRecordName , RouteRecordMatcher > ( )
6763 globalOptions = mergeOptions (
6864 { strict : false , end : true , sensitive : false } as PathParserOptions ,
6965 globalOptions
7066 )
7167
72- function getRecordMatcher ( name : NonNullable < RouteRecordNameGeneric > ) {
68+ function getRecordMatcher ( name : RouteRecordName ) {
7369 return matcherMap . get ( name )
7470 }
7571
@@ -167,12 +163,6 @@ export function createRouterMatcher(
167163 }
168164 }
169165
170- // Avoid adding a record that doesn't display anything. This allows passing through records without a component to
171- // not be reached and pass through the catch all route
172- if ( isMatchable ( matcher ) ) {
173- insertMatcher ( matcher )
174- }
175-
176166 if ( mainNormalizedRecord . children ) {
177167 const children = mainNormalizedRecord . children
178168 for ( let i = 0 ; i < children . length ; i ++ ) {
@@ -192,6 +182,17 @@ export function createRouterMatcher(
192182 // if (parent && isAliasRecord(originalRecord)) {
193183 // parent.children.push(originalRecord)
194184 // }
185+
186+ // Avoid adding a record that doesn't display anything. This allows passing through records without a component to
187+ // not be reached and pass through the catch all route
188+ if (
189+ ( matcher . record . components &&
190+ Object . keys ( matcher . record . components ) . length ) ||
191+ matcher . record . name ||
192+ matcher . record . redirect
193+ ) {
194+ insertMatcher ( matcher )
195+ }
195196 }
196197
197198 return originalMatcher
@@ -202,35 +203,29 @@ export function createRouterMatcher(
202203 : noop
203204 }
204205
205- function removeRoute (
206- matcherRef : NonNullable < RouteRecordNameGeneric > | RouteRecordMatcher
207- ) {
206+ function removeRoute ( matcherRef : RouteRecordName | RouteRecordMatcher ) {
208207 if ( isRouteName ( matcherRef ) ) {
209208 const matcher = matcherMap . get ( matcherRef )
210209 if ( matcher ) {
211210 matcherMap . delete ( matcherRef )
212- matchers . splice ( matchers . indexOf ( matcher ) , 1 )
211+ matcherTree . remove ( matcher )
213212 matcher . children . forEach ( removeRoute )
214213 matcher . alias . forEach ( removeRoute )
215214 }
216215 } else {
217- const index = matchers . indexOf ( matcherRef )
218- if ( index > - 1 ) {
219- matchers . splice ( index , 1 )
220- if ( matcherRef . record . name ) matcherMap . delete ( matcherRef . record . name )
221- matcherRef . children . forEach ( removeRoute )
222- matcherRef . alias . forEach ( removeRoute )
223- }
216+ matcherTree . remove ( matcherRef )
217+ if ( matcherRef . record . name ) matcherMap . delete ( matcherRef . record . name )
218+ matcherRef . children . forEach ( removeRoute )
219+ matcherRef . alias . forEach ( removeRoute )
224220 }
225221 }
226222
227223 function getRoutes ( ) {
228- return matchers
224+ return matcherTree . toArray ( )
229225 }
230226
231227 function insertMatcher ( matcher : RouteRecordMatcher ) {
232- const index = findInsertionIndex ( matcher , matchers )
233- matchers . splice ( index , 0 , matcher )
228+ matcherTree . add ( matcher )
234229 // only add the original record to the name map
235230 if ( matcher . record . name && ! isAliasRecord ( matcher ) )
236231 matcherMap . set ( matcher . record . name , matcher )
@@ -303,7 +298,7 @@ export function createRouterMatcher(
303298 )
304299 }
305300
306- matcher = matchers . find ( m => m . re . test ( path ) )
301+ matcher = matcherTree . find ( path )
307302 // matcher should have a value after the loop
308303
309304 if ( matcher ) {
@@ -316,7 +311,7 @@ export function createRouterMatcher(
316311 // match by name or path of current route
317312 matcher = currentLocation . name
318313 ? matcherMap . get ( currentLocation . name )
319- : matchers . find ( m => m . re . test ( currentLocation . path ) )
314+ : matcherTree . find ( currentLocation . path )
320315 if ( ! matcher )
321316 throw createRouterError < MatcherError > ( ErrorTypes . MATCHER_NOT_FOUND , {
322317 location,
@@ -351,7 +346,6 @@ export function createRouterMatcher(
351346 routes . forEach ( route => addRoute ( route ) )
352347
353348 function clearRoutes ( ) {
354- matchers . length = 0
355349 matcherMap . clear ( )
356350 }
357351
@@ -560,79 +554,4 @@ function checkMissingParamsInAbsolutePath(
560554 }
561555}
562556
563- /**
564- * Performs a binary search to find the correct insertion index for a new matcher.
565- *
566- * Matchers are primarily sorted by their score. If scores are tied then we also consider parent/child relationships,
567- * with descendants coming before ancestors. If there's still a tie, new routes are inserted after existing routes.
568- *
569- * @param matcher - new matcher to be inserted
570- * @param matchers - existing matchers
571- */
572- function findInsertionIndex (
573- matcher : RouteRecordMatcher ,
574- matchers : RouteRecordMatcher [ ]
575- ) {
576- // First phase: binary search based on score
577- let lower = 0
578- let upper = matchers . length
579-
580- while ( lower !== upper ) {
581- const mid = ( lower + upper ) >> 1
582- const sortOrder = comparePathParserScore ( matcher , matchers [ mid ] )
583-
584- if ( sortOrder < 0 ) {
585- upper = mid
586- } else {
587- lower = mid + 1
588- }
589- }
590-
591- // Second phase: check for an ancestor with the same score
592- const insertionAncestor = getInsertionAncestor ( matcher )
593-
594- if ( insertionAncestor ) {
595- upper = matchers . lastIndexOf ( insertionAncestor , upper - 1 )
596-
597- if ( __DEV__ && upper < 0 ) {
598- // This should never happen
599- warn (
600- `Finding ancestor route "${ insertionAncestor . record . path } " failed for "${ matcher . record . path } "`
601- )
602- }
603- }
604-
605- return upper
606- }
607-
608- function getInsertionAncestor ( matcher : RouteRecordMatcher ) {
609- let ancestor : RouteRecordMatcher | undefined = matcher
610-
611- while ( ( ancestor = ancestor . parent ) ) {
612- if (
613- isMatchable ( ancestor ) &&
614- comparePathParserScore ( matcher , ancestor ) === 0
615- ) {
616- return ancestor
617- }
618- }
619-
620- return
621- }
622-
623- /**
624- * Checks if a matcher can be reachable. This means if it's possible to reach it as a route. For example, routes without
625- * a component, or name, or redirect, are just used to group other routes.
626- * @param matcher
627- * @param matcher.record record of the matcher
628- * @returns
629- */
630- function isMatchable ( { record } : RouteRecordMatcher ) : boolean {
631- return ! ! (
632- record . name ||
633- ( record . components && Object . keys ( record . components ) . length ) ||
634- record . redirect
635- )
636- }
637-
638557export type { PathParserOptions , _PathParserOptions }
0 commit comments