Skip to content

Commit 454ecf8

Browse files
committed
Fix problems caused by merging 'main'
1 parent aca96a5 commit 454ecf8

File tree

2 files changed

+88
-100
lines changed

2 files changed

+88
-100
lines changed

packages/router/src/matcher/index.ts

Lines changed: 2 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {
55
isRouteName,
66
} from '../types'
77
import { createRouterError, ErrorTypes, MatcherError } from '../errors'
8-
import { createMatcherTree } from './matcherTree'
8+
import { createMatcherTree, isMatchable } from './matcherTree'
99
import { createRouteRecordMatcher, RouteRecordMatcher } from './pathMatcher'
1010
import { RouteRecordNormalized } from './types'
1111

@@ -340,7 +340,7 @@ export function createRouterMatcher(
340340
routes.forEach(route => addRoute(route))
341341

342342
function clearRoutes() {
343-
matchers.length = 0
343+
matcherTree.clear()
344344
matcherMap.clear()
345345
}
346346

@@ -523,79 +523,4 @@ function checkMissingParamsInAbsolutePath(
523523
}
524524
}
525525

526-
/**
527-
* Performs a binary search to find the correct insertion index for a new matcher.
528-
*
529-
* Matchers are primarily sorted by their score. If scores are tied then we also consider parent/child relationships,
530-
* with descendants coming before ancestors. If there's still a tie, new routes are inserted after existing routes.
531-
*
532-
* @param matcher - new matcher to be inserted
533-
* @param matchers - existing matchers
534-
*/
535-
function findInsertionIndex(
536-
matcher: RouteRecordMatcher,
537-
matchers: RouteRecordMatcher[]
538-
) {
539-
// First phase: binary search based on score
540-
let lower = 0
541-
let upper = matchers.length
542-
543-
while (lower !== upper) {
544-
const mid = (lower + upper) >> 1
545-
const sortOrder = comparePathParserScore(matcher, matchers[mid])
546-
547-
if (sortOrder < 0) {
548-
upper = mid
549-
} else {
550-
lower = mid + 1
551-
}
552-
}
553-
554-
// Second phase: check for an ancestor with the same score
555-
const insertionAncestor = getInsertionAncestor(matcher)
556-
557-
if (insertionAncestor) {
558-
upper = matchers.lastIndexOf(insertionAncestor, upper - 1)
559-
560-
if (__DEV__ && upper < 0) {
561-
// This should never happen
562-
warn(
563-
`Finding ancestor route "${insertionAncestor.record.path}" failed for "${matcher.record.path}"`
564-
)
565-
}
566-
}
567-
568-
return upper
569-
}
570-
571-
function getInsertionAncestor(matcher: RouteRecordMatcher) {
572-
let ancestor: RouteRecordMatcher | undefined = matcher
573-
574-
while ((ancestor = ancestor.parent)) {
575-
if (
576-
isMatchable(ancestor) &&
577-
comparePathParserScore(matcher, ancestor) === 0
578-
) {
579-
return ancestor
580-
}
581-
}
582-
583-
return
584-
}
585-
586-
/**
587-
* Checks if a matcher can be reachable. This means if it's possible to reach it as a route. For example, routes without
588-
* a component, or name, or redirect, are just used to group other routes.
589-
* @param matcher
590-
* @param matcher.record record of the matcher
591-
* @returns
592-
*/
593-
function isMatchable({ record }: RouteRecordMatcher): boolean {
594-
return !!(
595-
record.name ||
596-
(record.components && Object.keys(record.components).length) ||
597-
record.redirect
598-
)
599-
}
600-
601526
export type { PathParserOptions, _PathParserOptions }

packages/router/src/matcher/matcherTree.ts

Lines changed: 86 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
11
import { RouteRecordMatcher } from './pathMatcher'
22
import { comparePathParserScore } from './pathParserRanker'
3+
import { warn } from '../warning'
34

4-
type MatcherTree = {
5+
type MatcherNode = {
56
add: (matcher: RouteRecordMatcher) => void
67
remove: (matcher: RouteRecordMatcher) => void
78
find: (path: string) => RouteRecordMatcher | undefined
89
toArray: () => RouteRecordMatcher[]
910
}
1011

12+
type MatcherTree = MatcherNode & {
13+
clear: () => void
14+
}
15+
1116
function normalizePath(path: string) {
1217
// We match case-insensitively initially, then let the matcher check more rigorously
1318
path = path.toUpperCase()
@@ -37,9 +42,8 @@ function chooseBestMatcher(
3742
}
3843

3944
export function createMatcherTree(): MatcherTree {
40-
const root = createMatcherNode()
41-
const exactMatchers: Record<string, RouteRecordMatcher[]> =
42-
Object.create(null)
45+
let root = createMatcherNode()
46+
let exactMatchers: Record<string, RouteRecordMatcher[]> = Object.create(null)
4347

4448
return {
4549
add(matcher) {
@@ -66,6 +70,11 @@ export function createMatcherTree(): MatcherTree {
6670
}
6771
},
6872

73+
clear() {
74+
root = createMatcherNode()
75+
exactMatchers = Object.create(null)
76+
},
77+
6978
find(path) {
7079
const matchers = exactMatchers[normalizePath(path)]
7180

@@ -87,8 +96,8 @@ export function createMatcherTree(): MatcherTree {
8796
}
8897
}
8998

90-
function createMatcherNode(depth = 1): MatcherTree {
91-
let segments: Record<string, MatcherTree> | null = null
99+
function createMatcherNode(depth = 1): MatcherNode {
100+
let segments: Record<string, MatcherNode> | null = null
92101
let wildcards: RouteRecordMatcher[] | null = null
93102

94103
return {
@@ -191,29 +200,83 @@ function insertMatcher(
191200
matchers.splice(index, 0, matcher)
192201
}
193202

203+
/**
204+
* Performs a binary search to find the correct insertion index for a new matcher.
205+
*
206+
* Matchers are primarily sorted by their score. If scores are tied then we also consider parent/child relationships,
207+
* with descendants coming before ancestors. If there's still a tie, new routes are inserted after existing routes.
208+
*
209+
* @param matcher - new matcher to be inserted
210+
* @param matchers - existing matchers
211+
*/
194212
function findInsertionIndex(
195213
matcher: RouteRecordMatcher,
196214
matchers: RouteRecordMatcher[]
197215
) {
198-
let i = 0
199-
while (
200-
i < matchers.length &&
201-
comparePathParserScore(matcher, matchers[i]) >= 0 &&
202-
// Adding children with empty path should still appear before the parent
203-
// https://github.com/vuejs/router/issues/1124
204-
(matcher.record.path !== matchers[i].record.path ||
205-
!isRecordChildOf(matcher, matchers[i]))
206-
)
207-
i++
216+
// First phase: binary search based on score
217+
let lower = 0
218+
let upper = matchers.length
219+
220+
while (lower !== upper) {
221+
const mid = (lower + upper) >> 1
222+
const sortOrder = comparePathParserScore(matcher, matchers[mid])
223+
224+
if (sortOrder < 0) {
225+
upper = mid
226+
} else {
227+
lower = mid + 1
228+
}
229+
}
230+
231+
// Second phase: check for an ancestor with the same score
232+
const insertionAncestor = getInsertionAncestor(matcher)
233+
234+
if (insertionAncestor) {
235+
upper = matchers.lastIndexOf(insertionAncestor, upper - 1)
236+
237+
if (__DEV__ && upper < 0) {
238+
// This should never happen
239+
warn(
240+
`Finding ancestor route "${insertionAncestor.record.path}" failed for "${matcher.record.path}"`
241+
)
242+
}
243+
}
244+
245+
return upper
246+
}
247+
248+
function getInsertionAncestor(matcher: RouteRecordMatcher) {
249+
let ancestor: RouteRecordMatcher | undefined = matcher
250+
251+
while ((ancestor = ancestor.parent)) {
252+
if (
253+
isMatchable(ancestor) &&
254+
matcher.staticTokens.length === ancestor.staticTokens.length &&
255+
comparePathParserScore(matcher, ancestor) === 0 &&
256+
ancestor.staticTokens.every(
257+
(token, index) =>
258+
matcher.staticTokens[index].toUpperCase() === token.toUpperCase()
259+
)
260+
) {
261+
return ancestor
262+
}
263+
}
208264

209-
return i
265+
return
210266
}
211267

212-
function isRecordChildOf(
213-
record: RouteRecordMatcher,
214-
parent: RouteRecordMatcher
215-
): boolean {
216-
return parent.children.some(
217-
child => child === record || isRecordChildOf(record, child)
268+
/**
269+
* Checks if a matcher can be reachable. This means if it's possible to reach it as a route. For example, routes without
270+
* a component, or name, or redirect, are just used to group other routes.
271+
* @param matcher
272+
* @param matcher.record record of the matcher
273+
* @returns
274+
*/
275+
// TODO: This should probably live elsewhere
276+
export function isMatchable({ record }: RouteRecordMatcher): boolean {
277+
return !!(
278+
record.name ||
279+
(record.components && Object.keys(record.components).length) ||
280+
record.redirect
218281
)
219282
}

0 commit comments

Comments
 (0)