diff --git a/packages/ui/src/utilities/isPathMatchingRoute.spec.ts b/packages/ui/src/utilities/isPathMatchingRoute.spec.ts index 369a5e39923..41090a66a63 100644 --- a/packages/ui/src/utilities/isPathMatchingRoute.spec.ts +++ b/packages/ui/src/utilities/isPathMatchingRoute.spec.ts @@ -181,4 +181,26 @@ describe('isPathMatchingRoute', () => { ).toBe(false) }) }) + + describe('paths that path-to-regexp cannot parse — regression for #15241', () => { + it('should not throw on a trailing bare colon', () => { + expect(() => + isPathMatchingRoute({ currentRoute: '/admin/foo', path: '/foo/:' }), + ).not.toThrow() + }) + + it('should not throw on consecutive colons', () => { + expect(() => + isPathMatchingRoute({ currentRoute: '/admin/foo', path: '/foo::bar' }), + ).not.toThrow() + }) + + it('should not match an unrelated route against an unparseable path', () => { + expect(isPathMatchingRoute({ currentRoute: '/dashboard', path: '/foo/:' })).toBe(false) + }) + + it('should fall back to literal equality for an unparseable path when exact', () => { + expect(isPathMatchingRoute({ currentRoute: '/foo/:', exact: true, path: '/foo/:' })).toBe(true) + }) + }) }) diff --git a/packages/ui/src/utilities/isPathMatchingRoute.ts b/packages/ui/src/utilities/isPathMatchingRoute.ts index 6c81b2e2c6e..297df9fff18 100644 --- a/packages/ui/src/utilities/isPathMatchingRoute.ts +++ b/packages/ui/src/utilities/isPathMatchingRoute.ts @@ -19,12 +19,21 @@ export const isPathMatchingRoute = ({ const keys = [] - const regex = pathToRegexp(viewPath, keys, { - sensitive, - strict, - }) + // A view path that is not a valid path-to-regexp pattern (e.g. a stray ":") + // makes pathToRegexp throw, which would crash the whole admin render since + // this runs inside route-matching `.find` callbacks. Treat an unparseable + // pattern as a literal string and fall back to plain comparison. + let match: null | RegExpExecArray = null + + try { + match = pathToRegexp(viewPath, keys, { + sensitive, + strict, + }).exec(currentRoute) + } catch { + match = null + } - const match = regex.exec(currentRoute) const viewRoute = match?.[0] || viewPath if (exact) {