diff --git a/.changeset/witty-carrots-notice.md b/.changeset/witty-carrots-notice.md new file mode 100644 index 000000000000..7c0f5cd7de08 --- /dev/null +++ b/.changeset/witty-carrots-notice.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +Only normalise internal URLs diff --git a/packages/kit/src/runtime/client/client.js b/packages/kit/src/runtime/client/client.js index bd07f012ba6f..16a2889d61ad 100644 --- a/packages/kit/src/runtime/client/client.js +++ b/packages/kit/src/runtime/client/client.js @@ -238,6 +238,10 @@ export function create_client({ target, session, base, trailing_slash }) { return false; // unnecessary, but TypeScript prefers it this way } + // if this is an internal navigation intent, use the normalized + // URL for the rest of the function + url = intent?.url || url; + // abort if user navigated during update if (token !== current_token) return false; @@ -891,9 +895,12 @@ export function create_client({ target, session, base, trailing_slash }) { const params = route.exec(path); if (params) { - const id = normalize_path(url.pathname, trailing_slash) + url.search; + const normalized = new URL( + url.origin + normalize_path(url.pathname, trailing_slash) + url.search + url.hash + ); + const id = normalized.pathname + normalized.search; /** @type {import('./types').NavigationIntent} */ - const intent = { id, route, params: decode_params(params), url }; + const intent = { id, route, params: decode_params(params), url: normalized }; return intent; } } @@ -930,9 +937,6 @@ export function create_client({ target, session, base, trailing_slash }) { return; } - const pathname = normalize_path(url.pathname, trailing_slash); - const normalized = new URL(url.origin + pathname + url.search + url.hash); - update_scroll_positions(current_history_index); accepted(); @@ -940,12 +944,12 @@ export function create_client({ target, session, base, trailing_slash }) { if (started) { stores.navigating.set({ from: current.url, - to: normalized + to: url }); } await update( - normalized, + url, redirect_chain, false, { @@ -954,7 +958,7 @@ export function create_client({ target, session, base, trailing_slash }) { details }, () => { - const navigation = { from, to: normalized }; + const navigation = { from, to: url }; callbacks.after_navigate.forEach((fn) => fn(navigation)); stores.navigating.set(null); diff --git a/packages/kit/test/apps/basics/src/routes/routing/slashes/index.svelte b/packages/kit/test/apps/basics/src/routes/routing/slashes/index.svelte index b363f6435561..1c2295b61be0 100644 --- a/packages/kit/test/apps/basics/src/routes/routing/slashes/index.svelte +++ b/packages/kit/test/apps/basics/src/routes/routing/slashes/index.svelte @@ -1,3 +1,8 @@ + + /routing/ /routing/? -/routing/?foo=bar \ No newline at end of file +/routing/?foo=bar +external diff --git a/packages/kit/test/apps/basics/test/client.test.js b/packages/kit/test/apps/basics/test/client.test.js index b64954822ca2..0a491d5fdf68 100644 --- a/packages/kit/test/apps/basics/test/client.test.js +++ b/packages/kit/test/apps/basics/test/client.test.js @@ -1,5 +1,5 @@ import { expect } from '@playwright/test'; -import { test } from '../../../utils.js'; +import { start_server, test } from '../../../utils.js'; /** @typedef {import('@playwright/test').Response} Response */ @@ -544,6 +544,24 @@ test.describe('Routing', () => { expect(await page.textContent('#window-hash')).toBe('#target'); expect(await page.textContent('#page-url-hash')).toBe('#target'); }); + + test('does not normalize external path', async ({ page }) => { + const urls = []; + + const { port, close } = await start_server((req, res) => { + urls.push(req.url); + res.end('ok'); + }); + + try { + await page.goto(`/routing/slashes?port=${port}`); + await page.click(`a[href="http://localhost:${port}/with-slash/"]`); + + expect(urls).toEqual(['/with-slash/']); + } finally { + await close(); + } + }); }); test.describe('Shadow DOM', () => {