diff --git a/packages/react-router/tests/link.test.tsx b/packages/react-router/tests/link.test.tsx index 63495f44027..eaefd2d8999 100644 --- a/packages/react-router/tests/link.test.tsx +++ b/packages/react-router/tests/link.test.tsx @@ -6510,8 +6510,8 @@ describe('encoded and unicode paths', () => { name: 'with prefix', path: '/foo/prefix@๋Œ€{$}', expectedPath: - '/foo/prefix@%EB%8C%80test[s%5C/.%5C/parameter%25!%F0%9F%9A%80@]', - expectedLocation: '/foo/prefix@๋Œ€test[s%5C/.%5C/parameter%25!๐Ÿš€@]', + '/foo/prefix@%EB%8C%80test[s%5C/.%5C/parameter%25!%F0%9F%9A%80%40]', + expectedLocation: '/foo/prefix@๋Œ€test[s%5C/.%5C/parameter%25!๐Ÿš€%40]', params: { _splat: 'test[s\\/.\\/parameter%!๐Ÿš€@]', '*': 'test[s\\/.\\/parameter%!๐Ÿš€@]', @@ -6521,8 +6521,8 @@ describe('encoded and unicode paths', () => { name: 'with suffix', path: '/foo/{$}๋Œ€suffix@', expectedPath: - '/foo/test[s%5C/.%5C/parameter%25!%F0%9F%9A%80@]%EB%8C%80suffix@', - expectedLocation: '/foo/test[s%5C/.%5C/parameter%25!๐Ÿš€@]๋Œ€suffix@', + '/foo/test[s%5C/.%5C/parameter%25!%F0%9F%9A%80%40]%EB%8C%80suffix@', + expectedLocation: '/foo/test[s%5C/.%5C/parameter%25!๐Ÿš€%40]๋Œ€suffix@', params: { _splat: 'test[s\\/.\\/parameter%!๐Ÿš€@]', '*': 'test[s\\/.\\/parameter%!๐Ÿš€@]', diff --git a/packages/react-router/tests/navigate.test.tsx b/packages/react-router/tests/navigate.test.tsx index 3aaaa7124b0..32c05f6daf0 100644 --- a/packages/react-router/tests/navigate.test.tsx +++ b/packages/react-router/tests/navigate.test.tsx @@ -1300,8 +1300,8 @@ describe('encoded and unicode paths', () => { name: 'with prefix', path: '/foo/prefix@๋Œ€{$}', expectedPath: - '/foo/prefix@%EB%8C%80test[s%5C/.%5C/parameter%25!%F0%9F%9A%80@]', - expectedLocation: '/foo/prefix@๋Œ€test[s%5C/.%5C/parameter%25!๐Ÿš€@]', + '/foo/prefix@%EB%8C%80test[s%5C/.%5C/parameter%25!%F0%9F%9A%80%40]', + expectedLocation: '/foo/prefix@๋Œ€test[s%5C/.%5C/parameter%25!๐Ÿš€%40]', params: { _splat: 'test[s\\/.\\/parameter%!๐Ÿš€@]', '*': 'test[s\\/.\\/parameter%!๐Ÿš€@]', @@ -1311,8 +1311,8 @@ describe('encoded and unicode paths', () => { name: 'with suffix', path: '/foo/{$}๋Œ€suffix@', expectedPath: - '/foo/test[s%5C/.%5C/parameter%25!%F0%9F%9A%80@]%EB%8C%80suffix@', - expectedLocation: '/foo/test[s%5C/.%5C/parameter%25!๐Ÿš€@]๋Œ€suffix@', + '/foo/test[s%5C/.%5C/parameter%25!%F0%9F%9A%80%40]%EB%8C%80suffix@', + expectedLocation: '/foo/test[s%5C/.%5C/parameter%25!๐Ÿš€%40]๋Œ€suffix@', params: { _splat: 'test[s\\/.\\/parameter%!๐Ÿš€@]', '*': 'test[s\\/.\\/parameter%!๐Ÿš€@]', diff --git a/packages/react-router/tests/useNavigate.test.tsx b/packages/react-router/tests/useNavigate.test.tsx index 44db285f47d..b5f473885f7 100644 --- a/packages/react-router/tests/useNavigate.test.tsx +++ b/packages/react-router/tests/useNavigate.test.tsx @@ -2667,8 +2667,8 @@ describe('encoded and unicode paths', () => { name: 'with prefix', path: '/foo/prefix@๋Œ€{$}', expectedPath: - '/foo/prefix@%EB%8C%80test[s%5C/.%5C/parameter%25!%F0%9F%9A%80@]', - expectedLocation: '/foo/prefix@๋Œ€test[s%5C/.%5C/parameter%25!๐Ÿš€@]', + '/foo/prefix@%EB%8C%80test[s%5C/.%5C/parameter%25!%F0%9F%9A%80%40]', + expectedLocation: '/foo/prefix@๋Œ€test[s%5C/.%5C/parameter%25!๐Ÿš€%40]', params: { _splat: 'test[s\\/.\\/parameter%!๐Ÿš€@]', '*': 'test[s\\/.\\/parameter%!๐Ÿš€@]', @@ -2678,8 +2678,8 @@ describe('encoded and unicode paths', () => { name: 'with suffix', path: '/foo/{$}๋Œ€suffix@', expectedPath: - '/foo/test[s%5C/.%5C/parameter%25!%F0%9F%9A%80@]%EB%8C%80suffix@', - expectedLocation: '/foo/test[s%5C/.%5C/parameter%25!๐Ÿš€@]๋Œ€suffix@', + '/foo/test[s%5C/.%5C/parameter%25!%F0%9F%9A%80%40]%EB%8C%80suffix@', + expectedLocation: '/foo/test[s%5C/.%5C/parameter%25!๐Ÿš€%40]๋Œ€suffix@', params: { _splat: 'test[s\\/.\\/parameter%!๐Ÿš€@]', '*': 'test[s\\/.\\/parameter%!๐Ÿš€@]', @@ -2699,10 +2699,10 @@ describe('encoded and unicode paths', () => { { name: 'with path param', path: `/foo/$id`, - expectedPath: '/foo/test[s%5C%2F.%5C%2Fparameter%25!%F0%9F%9A%80]', - expectedLocation: '/foo/test[s%5C%2F.%5C%2Fparameter%25!๐Ÿš€]', + expectedPath: '/foo/test[s%5C%2F.%5C%2Fparameter%25!%F0%9F%9A%80%40]', + expectedLocation: '/foo/test[s%5C%2F.%5C%2Fparameter%25!๐Ÿš€%40]', params: { - id: 'test[s\\/.\\/parameter%!๐Ÿš€]', + id: 'test[s\\/.\\/parameter%!๐Ÿš€@]', }, }, ] diff --git a/packages/router-core/src/path.ts b/packages/router-core/src/path.ts index 373ad257036..ab8b97bc9b1 100644 --- a/packages/router-core/src/path.ts +++ b/packages/router-core/src/path.ts @@ -247,8 +247,15 @@ function encodeParam( if (typeof value !== 'string') return value if (key === '_splat') { + // Early return if value only contains URL-safe characters (performance optimization) + if (/^[a-zA-Z0-9\-._~!/]*$/.test(value)) return value // the splat/catch-all routes shouldn't have the '/' encoded out - return encodeURI(value) + // Use encodeURIComponent for each segment to properly encode spaces, + // plus signs, and other special characters that encodeURI leaves unencoded + return value + .split('/') + .map((segment) => encodePathParam(segment, decoder)) + .join('/') } else { return encodePathParam(value, decoder) } diff --git a/packages/router-core/src/router.ts b/packages/router-core/src/router.ts index 9fbb6744bb8..6a9784ef466 100644 --- a/packages/router-core/src/router.ts +++ b/packages/router-core/src/router.ts @@ -6,7 +6,7 @@ import { createControlledPromise, decodePath, deepEqual, - encodeNonAscii, + encodePathLikeUrl, findLast, functionalUpdate, isDangerousProtocol, @@ -1962,7 +1962,7 @@ export class RouterCore< // fullPath is already the correct href (origin-stripped) // We need to encode non-ASCII (unicode) characters for the href // since decodePath decoded them from the interpolated path - href = encodeNonAscii(fullPath) + href = encodePathLikeUrl(fullPath) publicHref = href } diff --git a/packages/router-core/src/utils.ts b/packages/router-core/src/utils.ts index d6046ea37c9..eac67cce034 100644 --- a/packages/router-core/src/utils.ts +++ b/packages/router-core/src/utils.ts @@ -628,18 +628,33 @@ export function decodePath(path: string, decodeIgnore?: Array): string { } /** - * Encodes non-ASCII (unicode) characters in a path while preserving - * already percent-encoded sequences. This is used to generate proper - * href values without constructing URL objects. + * Encodes a path the same way `new URL()` would, but without the overhead of full URL parsing. * - * Unlike encodeURI, this won't double-encode percent-encoded sequences - * like %2F or %25 because it only targets non-ASCII characters. + * This function encodes: + * - Whitespace characters (spaces โ†’ %20, tabs โ†’ %09, etc.) + * - Non-ASCII/Unicode characters (emojis, accented characters, etc.) + * + * It preserves: + * - Already percent-encoded sequences (won't double-encode %2F, %25, etc.) + * - ASCII special characters valid in URL paths (@, $, &, +, etc.) + * - Forward slashes as path separators + * + * Used to generate proper href values for SSR without constructing URL objects. + * + * @example + * encodePathLikeUrl('/path/file name.pdf') // '/path/file%20name.pdf' + * encodePathLikeUrl('/path/ๆ—ฅๆœฌ่ชž') // '/path/%E6%97%A5%E6%9C%AC%E8%AA%9E' + * encodePathLikeUrl('/path/already%20encoded') // '/path/already%20encoded' (preserved) */ -export function encodeNonAscii(path: string): string { +export function encodePathLikeUrl(path: string): string { + // Encode whitespace and non-ASCII characters that browsers encode in URLs + + // biome-ignore lint/suspicious/noControlCharactersInRegex: intentional ASCII range check // eslint-disable-next-line no-control-regex - if (!/[^\u0000-\u007F]/.test(path)) return path + if (!/\s|[^\u0000-\u007F]/.test(path)) return path + // biome-ignore lint/suspicious/noControlCharactersInRegex: intentional ASCII range check // eslint-disable-next-line no-control-regex - return path.replace(/[^\u0000-\u007F]/gu, encodeURIComponent) + return path.replace(/\s|[^\u0000-\u007F]/gu, encodeURIComponent) } /** diff --git a/packages/router-core/tests/path.test.ts b/packages/router-core/tests/path.test.ts index 39641dd4ef9..b34416640e1 100644 --- a/packages/router-core/tests/path.test.ts +++ b/packages/router-core/tests/path.test.ts @@ -440,6 +440,73 @@ describe.each([{ server: true }, { server: false }])( }) }) + describe('splat params with special characters', () => { + it.each([ + { + name: 'should encode spaces in splat param', + path: '/$', + params: { _splat: 'file name.pdf' }, + result: '/file%20name.pdf', + }, + { + name: 'should preserve parentheses in splat param (RFC 3986 unreserved)', + path: '/$', + params: { _splat: 'file(1).pdf' }, + result: '/file(1).pdf', + }, + { + name: 'should encode brackets in splat param', + path: '/$', + params: { _splat: 'file[1].pdf' }, + result: '/file%5B1%5D.pdf', + }, + { + name: 'should encode spaces in nested splat param paths', + path: '/$', + params: { _splat: 'folder/sub folder/file name.pdf' }, + result: '/folder/sub%20folder/file%20name.pdf', + }, + { + name: 'should encode spaces and brackets but preserve parentheses', + path: '/$', + params: { _splat: 'docs/file (copy) [2].pdf' }, + result: '/docs/file%20(copy)%20%5B2%5D.pdf', + }, + { + name: 'should encode hash in splat param', + path: '/$', + params: { _splat: 'page#section' }, + result: '/page%23section', + }, + { + name: 'should handle splat param with prefix and special characters', + path: '/files/prefix{$}', + params: { _splat: 'my file.pdf' }, + result: '/files/prefixmy%20file.pdf', + }, + { + name: 'should encode plus signs in splat param', + path: '/$', + params: { _splat: 'file+name.pdf' }, + result: '/file%2Bname.pdf', + }, + { + name: 'should encode equals signs in splat param', + path: '/$', + params: { _splat: 'query=value' }, + result: '/query%3Dvalue', + }, + ])('$name', ({ path, params, result }) => { + expect( + interpolatePath({ + path, + params, + server, + }).interpolatedPath, + ).toBe(result) + }) + }) + describe('named params (prefix + suffix)', () => { it.each([ { diff --git a/packages/router-core/tests/utils.test.ts b/packages/router-core/tests/utils.test.ts index 39bd97728e4..ea466a0ec43 100644 --- a/packages/router-core/tests/utils.test.ts +++ b/packages/router-core/tests/utils.test.ts @@ -2,6 +2,7 @@ import { afterEach, describe, expect, it } from 'vitest' import { decodePath, deepEqual, + encodePathLikeUrl, escapeHtml, isPlainArray, replaceEqualDeep, @@ -1039,3 +1040,40 @@ describe('escapeHtml', () => { ) }) }) + +describe('encodePathLikeUrl', () => { + it('should return path unchanged if no non-ASCII characters', () => { + expect(encodePathLikeUrl('/foo/bar/baz')).toBe('/foo/bar/baz') + }) + + it('should encode non-ASCII characters', () => { + expect(encodePathLikeUrl('/path/caf\u00e9')).toBe('/path/caf%C3%A9') + }) + + it('should encode unicode characters in path segments', () => { + expect(encodePathLikeUrl('/users/\u4e2d\u6587/profile')).toBe( + '/users/%E4%B8%AD%E6%96%87/profile', + ) + }) + + it('should encode spaces but preserve other ASCII special characters', () => { + // encodePathLikeUrl encodes whitespace and non-ASCII, but not other ASCII special chars + expect(encodePathLikeUrl('/path/file name.pdf')).toBe( + '/path/file%20name.pdf', + ) + expect(encodePathLikeUrl('/path/file[1].pdf')).toBe('/path/file[1].pdf') + expect(encodePathLikeUrl('/path#section')).toBe('/path#section') + }) + + it('should handle mixed ASCII and non-ASCII characters', () => { + expect(encodePathLikeUrl('/path/caf\u00e9 (copy).pdf')).toBe( + '/path/caf%C3%A9%20(copy).pdf', + ) + }) + + it('should handle emoji characters', () => { + expect(encodePathLikeUrl('/path/\u{1F600}/file')).toBe( + '/path/%F0%9F%98%80/file', + ) + }) +}) diff --git a/packages/solid-router/tests/link.test.tsx b/packages/solid-router/tests/link.test.tsx index 5adfee22474..a9edfd6ce56 100644 --- a/packages/solid-router/tests/link.test.tsx +++ b/packages/solid-router/tests/link.test.tsx @@ -6506,8 +6506,8 @@ describe('encoded and unicode paths', () => { name: 'with prefix', path: '/foo/prefix@๋Œ€{$}', expectedPath: - '/foo/prefix@%EB%8C%80test[s%5C/.%5C/parameter%25!%F0%9F%9A%80@]', - expectedLocation: '/foo/prefix@๋Œ€test[s%5C/.%5C/parameter%25!๐Ÿš€@]', + '/foo/prefix@%EB%8C%80test[s%5C/.%5C/parameter%25!%F0%9F%9A%80%40]', + expectedLocation: '/foo/prefix@๋Œ€test[s%5C/.%5C/parameter%25!๐Ÿš€%40]', params: { _splat: 'test[s\\/.\\/parameter%!๐Ÿš€@]', '*': 'test[s\\/.\\/parameter%!๐Ÿš€@]', @@ -6517,8 +6517,8 @@ describe('encoded and unicode paths', () => { name: 'with suffix', path: '/foo/{$}๋Œ€suffix@', expectedPath: - '/foo/test[s%5C/.%5C/parameter%25!%F0%9F%9A%80@]%EB%8C%80suffix@', - expectedLocation: '/foo/test[s%5C/.%5C/parameter%25!๐Ÿš€@]๋Œ€suffix@', + '/foo/test[s%5C/.%5C/parameter%25!%F0%9F%9A%80%40]%EB%8C%80suffix@', + expectedLocation: '/foo/test[s%5C/.%5C/parameter%25!๐Ÿš€%40]๋Œ€suffix@', params: { _splat: 'test[s\\/.\\/parameter%!๐Ÿš€@]', '*': 'test[s\\/.\\/parameter%!๐Ÿš€@]', diff --git a/packages/solid-router/tests/navigate.test.tsx b/packages/solid-router/tests/navigate.test.tsx index 4aaacc8674d..6f235e0147b 100644 --- a/packages/solid-router/tests/navigate.test.tsx +++ b/packages/solid-router/tests/navigate.test.tsx @@ -1270,8 +1270,8 @@ describe('encoded and unicode paths', () => { name: 'with prefix', path: '/foo/prefix@๋Œ€{$}', expectedPath: - '/foo/prefix@%EB%8C%80test[s%5C/.%5C/parameter%25!%F0%9F%9A%80@]', - expectedLocation: '/foo/prefix@๋Œ€test[s%5C/.%5C/parameter%25!๐Ÿš€@]', + '/foo/prefix@%EB%8C%80test[s%5C/.%5C/parameter%25!%F0%9F%9A%80%40]', + expectedLocation: '/foo/prefix@๋Œ€test[s%5C/.%5C/parameter%25!๐Ÿš€%40]', params: { _splat: 'test[s\\/.\\/parameter%!๐Ÿš€@]', '*': 'test[s\\/.\\/parameter%!๐Ÿš€@]', @@ -1281,8 +1281,8 @@ describe('encoded and unicode paths', () => { name: 'with suffix', path: '/foo/{$}๋Œ€suffix@', expectedPath: - '/foo/test[s%5C/.%5C/parameter%25!%F0%9F%9A%80@]%EB%8C%80suffix@', - expectedLocation: '/foo/test[s%5C/.%5C/parameter%25!๐Ÿš€@]๋Œ€suffix@', + '/foo/test[s%5C/.%5C/parameter%25!%F0%9F%9A%80%40]%EB%8C%80suffix@', + expectedLocation: '/foo/test[s%5C/.%5C/parameter%25!๐Ÿš€%40]๋Œ€suffix@', params: { _splat: 'test[s\\/.\\/parameter%!๐Ÿš€@]', '*': 'test[s\\/.\\/parameter%!๐Ÿš€@]', diff --git a/packages/solid-router/tests/useNavigate.test.tsx b/packages/solid-router/tests/useNavigate.test.tsx index 531f0a474dd..a90b63fd4f1 100644 --- a/packages/solid-router/tests/useNavigate.test.tsx +++ b/packages/solid-router/tests/useNavigate.test.tsx @@ -2651,8 +2651,8 @@ describe('encoded and unicode paths', () => { name: 'with prefix', path: '/foo/prefix@๋Œ€{$}', expectedPath: - '/foo/prefix@%EB%8C%80test[s%5C/.%5C/parameter%25!%F0%9F%9A%80@]', - expectedLocation: '/foo/prefix@๋Œ€test[s%5C/.%5C/parameter%25!๐Ÿš€@]', + '/foo/prefix@%EB%8C%80test[s%5C/.%5C/parameter%25!%F0%9F%9A%80%40]', + expectedLocation: '/foo/prefix@๋Œ€test[s%5C/.%5C/parameter%25!๐Ÿš€%40]', params: { _splat: 'test[s\\/.\\/parameter%!๐Ÿš€@]', '*': 'test[s\\/.\\/parameter%!๐Ÿš€@]', @@ -2662,8 +2662,8 @@ describe('encoded and unicode paths', () => { name: 'with suffix', path: '/foo/{$}๋Œ€suffix@', expectedPath: - '/foo/test[s%5C/.%5C/parameter%25!%F0%9F%9A%80@]%EB%8C%80suffix@', - expectedLocation: '/foo/test[s%5C/.%5C/parameter%25!๐Ÿš€@]๋Œ€suffix@', + '/foo/test[s%5C/.%5C/parameter%25!%F0%9F%9A%80%40]%EB%8C%80suffix@', + expectedLocation: '/foo/test[s%5C/.%5C/parameter%25!๐Ÿš€%40]๋Œ€suffix@', params: { _splat: 'test[s\\/.\\/parameter%!๐Ÿš€@]', '*': 'test[s\\/.\\/parameter%!๐Ÿš€@]', @@ -2683,10 +2683,10 @@ describe('encoded and unicode paths', () => { { name: 'with path param', path: `/foo/$id`, - expectedPath: '/foo/test[s%5C%2F.%5C%2Fparameter%25!%F0%9F%9A%80]', - expectedLocation: '/foo/test[s%5C%2F.%5C%2Fparameter%25!๐Ÿš€]', + expectedPath: '/foo/test[s%5C%2F.%5C%2Fparameter%25!%F0%9F%9A%80%40]', + expectedLocation: '/foo/test[s%5C%2F.%5C%2Fparameter%25!๐Ÿš€%40]', params: { - id: 'test[s\\/.\\/parameter%!๐Ÿš€]', + id: 'test[s\\/.\\/parameter%!๐Ÿš€@]', }, }, ] diff --git a/packages/vue-router/tests/link.test.tsx b/packages/vue-router/tests/link.test.tsx index b49cc08fb8b..ca9a446f552 100644 --- a/packages/vue-router/tests/link.test.tsx +++ b/packages/vue-router/tests/link.test.tsx @@ -6542,8 +6542,8 @@ describe('encoded and unicode paths', () => { name: 'with prefix', path: '/foo/prefix@๋Œ€{$}', expectedPath: - '/foo/prefix@%EB%8C%80test[s%5C/.%5C/parameter%25!%F0%9F%9A%80@]', - expectedLocation: '/foo/prefix@๋Œ€test[s%5C/.%5C/parameter%25!๐Ÿš€@]', + '/foo/prefix@%EB%8C%80test[s%5C/.%5C/parameter%25!%F0%9F%9A%80%40]', + expectedLocation: '/foo/prefix@๋Œ€test[s%5C/.%5C/parameter%25!๐Ÿš€%40]', params: { _splat: 'test[s\\/.\\/parameter%!๐Ÿš€@]', '*': 'test[s\\/.\\/parameter%!๐Ÿš€@]', @@ -6553,8 +6553,8 @@ describe('encoded and unicode paths', () => { name: 'with suffix', path: '/foo/{$}๋Œ€suffix@', expectedPath: - '/foo/test[s%5C/.%5C/parameter%25!%F0%9F%9A%80@]%EB%8C%80suffix@', - expectedLocation: '/foo/test[s%5C/.%5C/parameter%25!๐Ÿš€@]๋Œ€suffix@', + '/foo/test[s%5C/.%5C/parameter%25!%F0%9F%9A%80%40]%EB%8C%80suffix@', + expectedLocation: '/foo/test[s%5C/.%5C/parameter%25!๐Ÿš€%40]๋Œ€suffix@', params: { _splat: 'test[s\\/.\\/parameter%!๐Ÿš€@]', '*': 'test[s\\/.\\/parameter%!๐Ÿš€@]', diff --git a/packages/vue-router/tests/navigate.test.tsx b/packages/vue-router/tests/navigate.test.tsx index b080b420f02..b2d891904a3 100644 --- a/packages/vue-router/tests/navigate.test.tsx +++ b/packages/vue-router/tests/navigate.test.tsx @@ -1270,8 +1270,8 @@ describe('encoded and unicode paths', () => { name: 'with prefix', path: '/foo/prefix@๋Œ€{$}', expectedPath: - '/foo/prefix@%EB%8C%80test[s%5C/.%5C/parameter%25!%F0%9F%9A%80@]', - expectedLocation: '/foo/prefix@๋Œ€test[s%5C/.%5C/parameter%25!๐Ÿš€@]', + '/foo/prefix@%EB%8C%80test[s%5C/.%5C/parameter%25!%F0%9F%9A%80%40]', + expectedLocation: '/foo/prefix@๋Œ€test[s%5C/.%5C/parameter%25!๐Ÿš€%40]', params: { _splat: 'test[s\\/.\\/parameter%!๐Ÿš€@]', '*': 'test[s\\/.\\/parameter%!๐Ÿš€@]', @@ -1281,8 +1281,8 @@ describe('encoded and unicode paths', () => { name: 'with suffix', path: '/foo/{$}๋Œ€suffix@', expectedPath: - '/foo/test[s%5C/.%5C/parameter%25!%F0%9F%9A%80@]%EB%8C%80suffix@', - expectedLocation: '/foo/test[s%5C/.%5C/parameter%25!๐Ÿš€@]๋Œ€suffix@', + '/foo/test[s%5C/.%5C/parameter%25!%F0%9F%9A%80%40]%EB%8C%80suffix@', + expectedLocation: '/foo/test[s%5C/.%5C/parameter%25!๐Ÿš€%40]๋Œ€suffix@', params: { _splat: 'test[s\\/.\\/parameter%!๐Ÿš€@]', '*': 'test[s\\/.\\/parameter%!๐Ÿš€@]', diff --git a/packages/vue-router/tests/useNavigate.test.tsx b/packages/vue-router/tests/useNavigate.test.tsx index 6bfd4adf290..e71151863f7 100644 --- a/packages/vue-router/tests/useNavigate.test.tsx +++ b/packages/vue-router/tests/useNavigate.test.tsx @@ -2652,8 +2652,8 @@ describe('encoded and unicode paths', () => { name: 'with prefix', path: '/foo/prefix@๋Œ€{$}', expectedPath: - '/foo/prefix@%EB%8C%80test[s%5C/.%5C/parameter%25!%F0%9F%9A%80@]', - expectedLocation: '/foo/prefix@๋Œ€test[s%5C/.%5C/parameter%25!๐Ÿš€@]', + '/foo/prefix@%EB%8C%80test[s%5C/.%5C/parameter%25!%F0%9F%9A%80%40]', + expectedLocation: '/foo/prefix@๋Œ€test[s%5C/.%5C/parameter%25!๐Ÿš€%40]', params: { _splat: 'test[s\\/.\\/parameter%!๐Ÿš€@]', '*': 'test[s\\/.\\/parameter%!๐Ÿš€@]', @@ -2663,8 +2663,8 @@ describe('encoded and unicode paths', () => { name: 'with suffix', path: '/foo/{$}๋Œ€suffix@', expectedPath: - '/foo/test[s%5C/.%5C/parameter%25!%F0%9F%9A%80@]%EB%8C%80suffix@', - expectedLocation: '/foo/test[s%5C/.%5C/parameter%25!๐Ÿš€@]๋Œ€suffix@', + '/foo/test[s%5C/.%5C/parameter%25!%F0%9F%9A%80%40]%EB%8C%80suffix@', + expectedLocation: '/foo/test[s%5C/.%5C/parameter%25!๐Ÿš€%40]๋Œ€suffix@', params: { _splat: 'test[s\\/.\\/parameter%!๐Ÿš€@]', '*': 'test[s\\/.\\/parameter%!๐Ÿš€@]', @@ -2684,10 +2684,10 @@ describe('encoded and unicode paths', () => { { name: 'with path param', path: `/foo/$id`, - expectedPath: '/foo/test[s%5C%2F.%5C%2Fparameter%25!%F0%9F%9A%80]', - expectedLocation: '/foo/test[s%5C%2F.%5C%2Fparameter%25!๐Ÿš€]', + expectedPath: '/foo/test[s%5C%2F.%5C%2Fparameter%25!%F0%9F%9A%80%40]', + expectedLocation: '/foo/test[s%5C%2F.%5C%2Fparameter%25!๐Ÿš€%40]', params: { - id: 'test[s\\/.\\/parameter%!๐Ÿš€]', + id: 'test[s\\/.\\/parameter%!๐Ÿš€@]', }, }, ]