From 281e9cadf47afa143dfacf2b686a63647919fe27 Mon Sep 17 00:00:00 2001 From: leesb971204 Date: Mon, 14 Apr 2025 18:03:02 +0900 Subject: [PATCH 1/3] fix(router-core): simplify pathname parsing by removing unnecessary decoding logic Signed-off-by: leesb971204 --- packages/router-core/src/path.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/packages/router-core/src/path.ts b/packages/router-core/src/path.ts index f2daff5104f..09a4b4a650f 100644 --- a/packages/router-core/src/path.ts +++ b/packages/router-core/src/path.ts @@ -183,12 +183,7 @@ export function parsePathname(pathname?: string): Array { return { type: 'pathname', - value: part.includes('%25') - ? part - .split('%25') - .map((segment) => decodeURI(segment)) - .join('%25') - : decodeURI(part), + value: part, } }), ) From 51fff1abf17415037064a909997cd24bbe0f0413 Mon Sep 17 00:00:00 2001 From: leesb971204 Date: Mon, 14 Apr 2025 18:34:18 +0900 Subject: [PATCH 2/3] fix(router-core): updated function to correctly decode segments with URI encoded characters. Signed-off-by: leesb971204 --- packages/router-core/src/path.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/packages/router-core/src/path.ts b/packages/router-core/src/path.ts index 09a4b4a650f..ee72d83d6ca 100644 --- a/packages/router-core/src/path.ts +++ b/packages/router-core/src/path.ts @@ -1,4 +1,4 @@ -import { last } from './utils' +import { hasUriEncodedChars, last } from './utils' import type { MatchLocation } from './RouterProvider' import type { AnyPathParams } from './route' @@ -183,7 +183,14 @@ export function parsePathname(pathname?: string): Array { return { type: 'pathname', - value: part, + value: part.includes('%25') + ? part + .split('%25') + .map((segment) => decodeURI(segment)) + .join('%25') + : hasUriEncodedChars(part) + ? part + : decodeURI(part), } }), ) From f2193449b981b9ef0b8f8587c1050f48ff55e1ed Mon Sep 17 00:00:00 2001 From: leesb971204 Date: Thu, 5 Jun 2025 10:51:06 +0900 Subject: [PATCH 3/3] fix(router-core): update URL path segment encoding logic in tests and parsing function Signed-off-by: leesb971204 --- packages/react-router/tests/router.test.tsx | 46 +++++++++++---------- packages/router-core/src/path.ts | 11 +---- 2 files changed, 26 insertions(+), 31 deletions(-) diff --git a/packages/react-router/tests/router.test.tsx b/packages/react-router/tests/router.test.tsx index 1a295a3b0b6..da88b2518f2 100644 --- a/packages/react-router/tests/router.test.tsx +++ b/packages/react-router/tests/router.test.tsx @@ -723,72 +723,74 @@ describe('encoding: URL path segment', () => { it.each([ { input: '/path-segment/%C3%A9', - output: '/path-segment/Γ©', + output: '/path-segment/%25C3%25A9', type: 'encoded', }, { input: '/path-segment/Γ©', - output: '/path-segment/Γ©', - type: 'not encoded', + output: '/path-segment/%C3%A9', + type: 'encoded', }, { - input: '/path-segment/100%25', // `%25` = `%` - output: '/path-segment/100%25', - type: 'not encoded', + input: '/path-segment/100%25', + output: '/path-segment/100%2525', + type: 'encoded', }, { input: '/path-segment/100%25%25', - output: '/path-segment/100%25%25', - type: 'not encoded', + output: '/path-segment/100%2525%2525', + type: 'encoded', }, { - input: '/path-segment/100%26', // `%26` = `&` - output: '/path-segment/100%26', - type: 'not encoded', + input: '/path-segment/100%26', + output: '/path-segment/100%2526', + type: 'encoded', }, { input: '/path-segment/%F0%9F%9A%80', - output: '/path-segment/πŸš€', + output: '/path-segment/%25F0%259F%259A%2580', type: 'encoded', }, { input: '/path-segment/%F0%9F%9A%80to%2Fthe%2Fmoon', - output: '/path-segment/πŸš€to%2Fthe%2Fmoon', + output: '/path-segment/%25F0%259F%259A%2580to%252Fthe%252Fmoon', type: 'encoded', }, { input: '/path-segment/%25%F0%9F%9A%80to%2Fthe%2Fmoon', - output: '/path-segment/%25πŸš€to%2Fthe%2Fmoon', + output: '/path-segment/%2525%25F0%259F%259A%2580to%252Fthe%252Fmoon', type: 'encoded', }, { input: '/path-segment/%F0%9F%9A%80to%2Fthe%2Fmoon%25', - output: '/path-segment/πŸš€to%2Fthe%2Fmoon%25', + output: '/path-segment/%25F0%259F%259A%2580to%252Fthe%252Fmoon%2525', type: 'encoded', }, { input: '/path-segment/%F0%9F%9A%80to%2Fthe%2Fmoon%25%F0%9F%9A%80to%2Fthe%2Fmoon', - output: '/path-segment/πŸš€to%2Fthe%2Fmoon%25πŸš€to%2Fthe%2Fmoon', + output: + '/path-segment/%25F0%259F%259A%2580to%252Fthe%252Fmoon%2525%25F0%259F%259A%2580to%252Fthe%252Fmoon', type: 'encoded', }, { input: '/path-segment/πŸš€', - output: '/path-segment/πŸš€', - type: 'not encoded', + output: '/path-segment/%F0%9F%9A%80', + type: 'encoded', }, { input: '/path-segment/πŸš€to%2Fthe%2Fmoon', - output: '/path-segment/πŸš€to%2Fthe%2Fmoon', - type: 'not encoded', + output: '/path-segment/%F0%9F%9A%80to%252Fthe%252Fmoon', + type: 'encoded', }, ])( 'should resolve $input to $output when the path segment is $type', async ({ input, output }) => { const { router } = createTestRouter({ - history: createMemoryHistory({ initialEntries: [input] }), + history: createMemoryHistory({ + initialEntries: [input.split('/').map(encodeURIComponent).join('/')], // In browser, the path is encoded by default + }), }) - render() await act(() => router.load()) diff --git a/packages/router-core/src/path.ts b/packages/router-core/src/path.ts index ee72d83d6ca..09a4b4a650f 100644 --- a/packages/router-core/src/path.ts +++ b/packages/router-core/src/path.ts @@ -1,4 +1,4 @@ -import { hasUriEncodedChars, last } from './utils' +import { last } from './utils' import type { MatchLocation } from './RouterProvider' import type { AnyPathParams } from './route' @@ -183,14 +183,7 @@ export function parsePathname(pathname?: string): Array { return { type: 'pathname', - value: part.includes('%25') - ? part - .split('%25') - .map((segment) => decodeURI(segment)) - .join('%25') - : hasUriEncodedChars(part) - ? part - : decodeURI(part), + value: part, } }), )