]> git.ipfire.org Git - thirdparty/vuejs/router.git/commitdiff
feat: throw if parent and child routes have the same name (#2267)
authorskirtle <65301168+skirtles-code@users.noreply.github.com>
Tue, 29 Oct 2024 16:26:22 +0000 (16:26 +0000)
committerGitHub <noreply@github.com>
Tue, 29 Oct 2024 16:26:22 +0000 (17:26 +0100)
Co-authored-by: Eduardo San Martin Morote <posva@users.noreply.github.com>
packages/router/__tests__/matcher/addingRemoving.spec.ts
packages/router/src/matcher/index.ts

index 85ef37888bb93600c9b84bd7f222ee7dccc69480..9633ae6cc24eb389acb04489add2bfefba8cefc8 100644 (file)
@@ -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' })
index 530630230c4fe55e4a0ab21f26a392f656c6e7e3..9d787ddbc6065d01345d1b48fea36aa66447ac20 100644 (file)
@@ -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