Skip to content

Commit 3f6f633

Browse files
committed
feat: throw if parent and child routes have the same name
1 parent 50ac702 commit 3f6f633

File tree

2 files changed

+66
-1
lines changed

2 files changed

+66
-1
lines changed

packages/router/__tests__/matcher/addingRemoving.spec.ts

+46
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,52 @@ describe('Matcher: adding and removing records', () => {
388388
})
389389
})
390390

391+
it('throws if a parent and child have the same name', () => {
392+
expect(() => {
393+
createRouterMatcher(
394+
[
395+
{
396+
path: '/',
397+
component,
398+
name: 'home',
399+
children: [{ path: '/home', component, name: 'home' }],
400+
},
401+
],
402+
{}
403+
)
404+
}).toThrowError(
405+
'A route named "home" has been added as a child of a route with the same name'
406+
)
407+
})
408+
409+
it('throws if an ancestor and descendant have the same name', () => {
410+
const name = Symbol('home')
411+
const matcher = createRouterMatcher(
412+
[
413+
{
414+
path: '/',
415+
name,
416+
children: [
417+
{
418+
path: 'home',
419+
name: 'other',
420+
component,
421+
},
422+
],
423+
},
424+
],
425+
{}
426+
)
427+
428+
const parent = matcher.getRecordMatcher('other')
429+
430+
expect(() => {
431+
matcher.addRoute({ path: '', component, name }, parent)
432+
}).toThrowError(
433+
'A route named "Symbol(home)" has been added as a descendant of a route with the same name'
434+
)
435+
})
436+
391437
it('adds empty paths as children', () => {
392438
const matcher = createRouterMatcher([], {})
393439
matcher.addRoute({ path: '/', component, name: 'parent' })

packages/router/src/matcher/index.ts

+20-1
Original file line numberDiff line numberDiff line change
@@ -154,8 +154,12 @@ export function createRouterMatcher(
154154

155155
// remove the route if named and only for the top record (avoid in nested calls)
156156
// this works because the original record is the first one
157-
if (isRootAdd && record.name && !isAliasRecord(matcher))
157+
if (isRootAdd && record.name && !isAliasRecord(matcher)) {
158+
if (__DEV__) {
159+
checkSameNameAsAncestor(record, parent)
160+
}
158161
removeRoute(record.name)
162+
}
159163
}
160164

161165
// Avoid adding a record that doesn't display anything. This allows passing through records without a component to
@@ -499,6 +503,21 @@ function checkChildMissingNameWithEmptyPath(
499503
}
500504
}
501505

506+
function checkSameNameAsAncestor(
507+
record: RouteRecordRaw,
508+
parent?: RouteRecordMatcher
509+
) {
510+
for (let ancestor = parent; ancestor; ancestor = ancestor.parent) {
511+
if (ancestor.record.name === record.name) {
512+
throw new Error(
513+
`A route named "${String(record.name)}" has been added as a ${
514+
parent === ancestor ? 'child' : 'descendant'
515+
} of a route with the same name. This is not allowed, a nested route must not use the same name as an ancestor.`
516+
)
517+
}
518+
}
519+
}
520+
502521
function checkMissingParamsInAbsolutePath(
503522
record: RouteRecordMatcher,
504523
parent: RouteRecordMatcher

0 commit comments

Comments
 (0)