diff --git a/packages/core-api/src/routing/hooks.test.tsx b/packages/core-api/src/routing/hooks.test.tsx
index 6c38a6e18a..c2b8679e5f 100644
--- a/packages/core-api/src/routing/hooks.test.tsx
+++ b/packages/core-api/src/routing/hooks.test.tsx
@@ -25,7 +25,7 @@ import {
} from '../extensions/traversal';
import { createPlugin } from '../plugin';
import { routeCollector, routeParentCollector } from './collectors';
-import { useRouteRef, RoutingProvider } from './hooks';
+import { useRouteRef, RoutingProvider, validateRoutes } from './hooks';
import { createRouteRef } from './RouteRef';
import { RouteRef } from './types';
@@ -177,4 +177,29 @@ describe('discovery', () => {
rendered.getByText('Path at inside: /foo/blob/baz'),
).toBeInTheDocument();
});
+
+ it('should handle relative routing of parameterized routes with duplicate param names', () => {
+ const root = (
+
+
+
+
+
+
+
+ );
+
+ const { routes, routeParents } = traverseElementTree({
+ root,
+ discoverers: [childDiscoverer, routeElementDiscoverer],
+ collectors: {
+ routes: routeCollector,
+ routeParents: routeParentCollector,
+ },
+ });
+
+ expect(() => validateRoutes(routes, routeParents)).toThrow(
+ 'Parameter :id is duplicated in path /foo/:id/bar/:id',
+ );
+ });
});
diff --git a/packages/core-api/src/routing/hooks.tsx b/packages/core-api/src/routing/hooks.tsx
index 821fd6f9de..0c36928f10 100644
--- a/packages/core-api/src/routing/hooks.tsx
+++ b/packages/core-api/src/routing/hooks.tsx
@@ -75,3 +75,42 @@ export const RoutingProvider = ({
);
};
+
+export function validateRoutes(
+ routes: Map,
+ routeParents: Map,
+) {
+ const notLeafRoutes = new Set(routeParents.values());
+ notLeafRoutes.delete(undefined);
+
+ for (const route of routeParents.keys()) {
+ if (notLeafRoutes.has(route)) {
+ continue;
+ }
+
+ let currentRouteRef: RouteRef | undefined = route;
+
+ let fullPath = '';
+ while (currentRouteRef) {
+ const path = routes.get(currentRouteRef);
+ if (!path) {
+ throw new Error(`No path for ${currentRouteRef}`);
+ }
+ fullPath = `${path}${fullPath}`;
+ currentRouteRef = routeParents.get(currentRouteRef);
+ }
+
+ const params = fullPath.match(/:(\w+)/g);
+ if (params) {
+ for (let j = 0; j < params.length; j++) {
+ for (let i = j + 1; i < params.length; i++) {
+ if (params[i] === params[j]) {
+ throw new Error(
+ `Parameter ${params[i]} is duplicated in path ${fullPath}`,
+ );
+ }
+ }
+ }
+ }
+ }
+}