diff --git a/.changeset/two-tigers-cry.md b/.changeset/two-tigers-cry.md new file mode 100644 index 0000000000000..d6a9a5a4e9f5e --- /dev/null +++ b/.changeset/two-tigers-cry.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +[fix] don't decode URL when finding matching route diff --git a/packages/kit/src/core/create_manifest_data/index.js b/packages/kit/src/core/create_manifest_data/index.js index 8cb6aa87aec0f..acef1c8ea6a79 100644 --- a/packages/kit/src/core/create_manifest_data/index.js +++ b/packages/kit/src/core/create_manifest_data/index.js @@ -388,13 +388,10 @@ function get_pattern(segments, add_trailing_slash) { .map((part) => { return part.dynamic ? '([^/]+?)' - : part.content - .normalize() - .replace(/\?/g, '%3F') - .replace(/#/g, '%23') - .replace(/%5B/g, '[') - .replace(/%5D/g, ']') - .replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + : encodeURIComponent(part.content.normalize()).replace( + /[.*+?^${}()|[\]\\]/g, + '\\$&' + ); }) .join(''); }) diff --git a/packages/kit/src/core/create_manifest_data/index.spec.js b/packages/kit/src/core/create_manifest_data/index.spec.js index 2f0a80d0fd9df..203a6421de596 100644 --- a/packages/kit/src/core/create_manifest_data/index.spec.js +++ b/packages/kit/src/core/create_manifest_data/index.spec.js @@ -132,18 +132,20 @@ test('creates routes with layout', () => { ]); }); -test('encodes invalid characters', () => { +test('encoding of characters', () => { const { components, routes } = create('samples/encoding'); // had to remove ? and " because windows // const quote = 'samples/encoding/".svelte'; const hash = 'samples/encoding/#.svelte'; + const potato = 'samples/encoding/土豆.svelte'; // const question_mark = 'samples/encoding/?.svelte'; assert.equal(components, [ layout, error, + potato, // quote, hash // question_mark @@ -152,6 +154,7 @@ test('encodes invalid characters', () => { assert.equal( routes.map((p) => p.pattern), [ + /^\/%E5%9C%9F%E8%B1%86\/?$/, // /^\/%22\/?$/, /^\/%23\/?$/ // /^\/%3F\/?$/ diff --git "a/packages/kit/src/core/create_manifest_data/test/samples/encoding/\345\234\237\350\261\206.svelte" "b/packages/kit/src/core/create_manifest_data/test/samples/encoding/\345\234\237\350\261\206.svelte" new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/packages/kit/src/runtime/client/renderer.js b/packages/kit/src/runtime/client/renderer.js index 4f3de22b7da25..eb5399658cc49 100644 --- a/packages/kit/src/runtime/client/renderer.js +++ b/packages/kit/src/runtime/client/renderer.js @@ -541,8 +541,8 @@ export class Renderer { * @param {boolean} no_cache * @returns {Promise} undefined if fallthrough */ - async _load({ route, info: { path, decoded_path, query } }, no_cache) { - const key = `${decoded_path}?${query}`; + async _load({ route, info: { path, query } }, no_cache) { + const key = `${path}?${query}`; if (!no_cache) { const cached = this.cache.get(key); @@ -552,7 +552,7 @@ export class Renderer { const [pattern, a, b, get_params] = route; const params = get_params ? // the pattern is for the route which we've already matched to this path - get_params(/** @type {RegExpExecArray} */ (pattern.exec(decoded_path))) + get_params(/** @type {RegExpExecArray} */ (pattern.exec(path))) : {}; const changed = this.current.page && { diff --git a/packages/kit/src/runtime/client/router.js b/packages/kit/src/runtime/client/router.js index 9fb9c5dd07155..95fafcca34267 100644 --- a/packages/kit/src/runtime/client/router.js +++ b/packages/kit/src/runtime/client/router.js @@ -170,13 +170,12 @@ export class Router { if (this.owns(url)) { const path = url.pathname.slice(this.base.length) || '/'; - const decoded_path = decodeURI(path); - const routes = this.routes.filter(([pattern]) => pattern.test(decoded_path)); + const routes = this.routes.filter(([pattern]) => pattern.test(path)); const query = new URLSearchParams(url.search); const id = `${path}?${query}`; - return { id, routes, path, decoded_path, query }; + return { id, routes, path, query }; } } diff --git a/packages/kit/src/runtime/client/types.d.ts b/packages/kit/src/runtime/client/types.d.ts index e5865fa3591bc..82a91d9034240 100644 --- a/packages/kit/src/runtime/client/types.d.ts +++ b/packages/kit/src/runtime/client/types.d.ts @@ -5,7 +5,6 @@ export type NavigationInfo = { id: string; routes: CSRRoute[]; path: string; - decoded_path: string; query: URLSearchParams; }; diff --git a/packages/kit/src/runtime/server/index.js b/packages/kit/src/runtime/server/index.js index 8d6d4be7050f3..d915cec6fa279 100644 --- a/packages/kit/src/runtime/server/index.js +++ b/packages/kit/src/runtime/server/index.js @@ -54,9 +54,8 @@ export async function respond(incoming, options, state = {}) { }); } - const decoded = decodeURI(request.path); for (const route of options.manifest.routes) { - const match = route.pattern.exec(decoded); + const match = route.pattern.exec(request.path); if (!match) continue; const response = diff --git a/packages/kit/test/apps/basics/src/routes/encoded/_tests.js b/packages/kit/test/apps/basics/src/routes/encoded/_tests.js index cb33123d204cc..f8fce00a643ba 100644 --- a/packages/kit/test/apps/basics/src/routes/encoded/_tests.js +++ b/packages/kit/test/apps/basics/src/routes/encoded/_tests.js @@ -9,6 +9,11 @@ export default function (test) { assert.equal(decodeURI(await page.innerHTML('h3')), '/encoded/苗条'); }); + test('visits a route with a doubly encoded space', '/encoded/test%2520me', async ({ page }) => { + assert.equal(await page.innerHTML('h2'), '/encoded/test%2520me: test%20me'); + assert.equal(await page.innerHTML('h3'), '/encoded/test%2520me: test%20me'); + }); + test( 'visits a dynamic route with non-ASCII character', '/encoded',