diff --git a/packages/router/__tests__/matcher/addingRemoving.spec.ts b/packages/router/__tests__/matcher/addingRemoving.spec.ts index 85ef37888..9633ae6cc 100644 --- a/packages/router/__tests__/matcher/addingRemoving.spec.ts +++ b/packages/router/__tests__/matcher/addingRemoving.spec.ts @@ -404,6 +404,52 @@ describe('Matcher: adding and removing records', () => { }) }) + it('throws if a parent and child have the same name', () => { + expect(() => { + createRouterMatcher( + [ + { + path: '/', + component, + name: 'home', + children: [{ path: '/home', component, name: 'home' }], + }, + ], + {} + ) + }).toThrowError( + 'A route named "home" has been added as a child of a route with the same name' + ) + }) + + it('throws if an ancestor and descendant have the same name', () => { + const name = Symbol('home') + const matcher = createRouterMatcher( + [ + { + path: '/', + name, + children: [ + { + path: 'home', + name: 'other', + component, + }, + ], + }, + ], + {} + ) + + const parent = matcher.getRecordMatcher('other') + + expect(() => { + matcher.addRoute({ path: '', component, name }, parent) + }).toThrowError( + 'A route named "Symbol(home)" has been added as a descendant of a route with the same name' + ) + }) + it('adds empty paths as children', () => { const matcher = createRouterMatcher([], {}) matcher.addRoute({ path: '/', component, name: 'parent' }) diff --git a/packages/router/src/matcher/index.ts b/packages/router/src/matcher/index.ts index 530630230..9d787ddbc 100644 --- a/packages/router/src/matcher/index.ts +++ b/packages/router/src/matcher/index.ts @@ -159,8 +159,12 @@ export function createRouterMatcher( // remove the route if named and only for the top record (avoid in nested calls) // this works because the original record is the first one - if (isRootAdd && record.name && !isAliasRecord(matcher)) + if (isRootAdd && record.name && !isAliasRecord(matcher)) { + if (__DEV__) { + checkSameNameAsAncestor(record, parent) + } removeRoute(record.name) + } } // Avoid adding a record that doesn't display anything. This allows passing through records without a component to @@ -529,6 +533,21 @@ function checkChildMissingNameWithEmptyPath( } } +function checkSameNameAsAncestor( + record: RouteRecordRaw, + parent?: RouteRecordMatcher +) { + for (let ancestor = parent; ancestor; ancestor = ancestor.parent) { + if (ancestor.record.name === record.name) { + throw new Error( + `A route named "${String(record.name)}" has been added as a ${ + parent === ancestor ? 'child' : 'descendant' + } of a route with the same name. Route names must be unique and a nested route cannot use the same name as an ancestor.` + ) + } + } +} + function checkMissingParamsInAbsolutePath( record: RouteRecordMatcher, parent: RouteRecordMatcher