From 7902736600b1f37386e9a255bd251b59c18f911c Mon Sep 17 00:00:00 2001 From: Sheraff Date: Wed, 26 Nov 2025 13:24:34 +0100 Subject: [PATCH 1/3] fix(router-core): empty wilcard node is matched over sibling layout route --- .../router-core/src/new-process-route-tree.ts | 6 ++++++ .../tests/new-process-route-tree.test.ts | 17 +++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/packages/router-core/src/new-process-route-tree.ts b/packages/router-core/src/new-process-route-tree.ts index 2d3b41cc11..bbdbde56e2 100644 --- a/packages/router-core/src/new-process-route-tree.ts +++ b/packages/router-core/src/new-process-route-tree.ts @@ -1018,6 +1018,12 @@ function getNodeMatch( } } + if (bestMatch && wildcardMatch) { + return isFrameMoreSpecific(wildcardMatch, bestMatch) + ? bestMatch + : wildcardMatch + } + if (bestMatch) return bestMatch if (wildcardMatch) return wildcardMatch diff --git a/packages/router-core/tests/new-process-route-tree.test.ts b/packages/router-core/tests/new-process-route-tree.test.ts index ced5ac1b8b..ea1dc0db04 100644 --- a/packages/router-core/tests/new-process-route-tree.test.ts +++ b/packages/router-core/tests/new-process-route-tree.test.ts @@ -546,6 +546,23 @@ describe('findRouteMatch', () => { expect(res?.route.id).toBe('/a/b/$') expect(res?.params).toEqual({ _splat: 'foo', '*': 'foo' }) }) + describe('edge-case #5969: trailing empty wildcard should match', () => { + it('basic', () => { + const tree = makeTree(['/a/$']) + expect(findRouteMatch('/a/', tree)?.route.id).toBe('/a/$') + expect(findRouteMatch('/a', tree)?.route.id).toBe('/a/$') + }) + it('with layout route', () => { + const tree = makeTree(['/a', '/a/$']) + expect(findRouteMatch('/a/', tree)?.route.id).toBe('/a/$') + expect(findRouteMatch('/a', tree)?.route.id).toBe('/a/$') + }) + it('with index route (should not match)', () => { + const tree = makeTree(['/a/', '/a/$']) + expect(findRouteMatch('/a/', tree)?.route.id).toBe('/a/') + expect(findRouteMatch('/a', tree)?.route.id).toBe('/a/') + }) + }) }) describe('nested routes', () => { From 3222765353e1f21a5bb5d1d7452eb2f6d6f9aa47 Mon Sep 17 00:00:00 2001 From: Sheraff Date: Wed, 26 Nov 2025 13:45:30 +0100 Subject: [PATCH 2/3] add edge-case unit test --- packages/router-core/tests/new-process-route-tree.test.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/router-core/tests/new-process-route-tree.test.ts b/packages/router-core/tests/new-process-route-tree.test.ts index ea1dc0db04..c496c5a459 100644 --- a/packages/router-core/tests/new-process-route-tree.test.ts +++ b/packages/router-core/tests/new-process-route-tree.test.ts @@ -562,6 +562,11 @@ describe('findRouteMatch', () => { expect(findRouteMatch('/a/', tree)?.route.id).toBe('/a/') expect(findRouteMatch('/a', tree)?.route.id).toBe('/a/') }) + it('edge-case: deeper index route through skipped optional segments (should not match)', () => { + const tree = makeTree(['/{-$foo}/{-$bar}/a/', '/a/$']) + expect(findRouteMatch('/a/', tree)?.route.id).toBe('/{-$foo}/{-$bar}/a/') + expect(findRouteMatch('/a', tree)?.route.id).toBe('/{-$foo}/{-$bar}/a/') + }) }) }) From b11d530d765e257c731f8c448cfd6ce772886128 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Wed, 26 Nov 2025 12:46:44 +0000 Subject: [PATCH 3/3] ci: apply automated fixes --- packages/router-core/tests/new-process-route-tree.test.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/router-core/tests/new-process-route-tree.test.ts b/packages/router-core/tests/new-process-route-tree.test.ts index c496c5a459..65b7da14ca 100644 --- a/packages/router-core/tests/new-process-route-tree.test.ts +++ b/packages/router-core/tests/new-process-route-tree.test.ts @@ -564,7 +564,9 @@ describe('findRouteMatch', () => { }) it('edge-case: deeper index route through skipped optional segments (should not match)', () => { const tree = makeTree(['/{-$foo}/{-$bar}/a/', '/a/$']) - expect(findRouteMatch('/a/', tree)?.route.id).toBe('/{-$foo}/{-$bar}/a/') + expect(findRouteMatch('/a/', tree)?.route.id).toBe( + '/{-$foo}/{-$bar}/a/', + ) expect(findRouteMatch('/a', tree)?.route.id).toBe('/{-$foo}/{-$bar}/a/') }) })