Skip to content

Commit 5d02a95

Browse files
authored
fix: do not mutate URL during reroute logic (#13222)
fixes #13220
1 parent e95d446 commit 5d02a95

File tree

8 files changed

+25
-8
lines changed

8 files changed

+25
-8
lines changed

.changeset/rude-plants-rest.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@sveltejs/kit': patch
3+
---
4+
5+
fix: do not mutate URL during reroute logic

packages/kit/src/exports/public.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -622,6 +622,7 @@ export interface KitConfig {
622622
* What type of client-side router to use.
623623
* - `'pathname'` is the default and means the current URL pathname determines the route
624624
* - `'hash'` means the route is determined by `location.hash`. In this case, SSR and prerendering are disabled. This is only recommended if `pathname` is not an option, for example because you don't control the webserver where your app is deployed.
625+
* It comes with some caveats: you can't use server-side rendering (or indeed any server logic), and you have to make sure that the links in your app all start with /#/, or they won't work. Beyond that, everything works exactly like a normal SvelteKit app.
625626
*
626627
* @default "pathname"
627628
* @since 2.14.0

packages/kit/src/runtime/client/client.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1191,13 +1191,15 @@ function get_navigation_intent(url, invalidating) {
11911191
rerouted = app.hooks.reroute({ url: new URL(url) }) ?? url;
11921192

11931193
if (typeof rerouted === 'string') {
1194+
const tmp = new URL(url); // do not mutate the incoming URL
1195+
11941196
if (app.hash) {
1195-
url.hash = rerouted;
1197+
tmp.hash = rerouted;
11961198
} else {
1197-
url.pathname = rerouted;
1199+
tmp.pathname = rerouted;
11981200
}
11991201

1200-
rerouted = url;
1202+
rerouted = tmp;
12011203
}
12021204
} catch (e) {
12031205
if (DEV) {
Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,5 @@
1-
<h1>Successfully rewritten</h1>
1+
<script>
2+
import { page } from '$app/state';
3+
</script>
4+
5+
<h1>Successfully rewritten, URL should still show a: {page.url.pathname}</h1>

packages/kit/test/apps/basics/test/client.test.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1184,7 +1184,9 @@ test.describe('reroute', () => {
11841184
test('Apply reroute during client side navigation', async ({ page }) => {
11851185
await page.goto('/reroute/basic');
11861186
await page.click("a[href='/reroute/basic/a']");
1187-
expect(await page.textContent('h1')).toContain('Successfully rewritten');
1187+
expect(await page.textContent('h1')).toContain(
1188+
'Successfully rewritten, URL should still show a: /reroute/basic/a'
1189+
);
11881190
});
11891191

11901192
test('Apply reroute after client-only redirects', async ({ page }) => {

packages/kit/test/apps/basics/test/server.test.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -642,7 +642,9 @@ test.describe('Miscellaneous', () => {
642642
test.describe('reroute', () => {
643643
test('Apply reroute when directly accessing a page', async ({ page }) => {
644644
await page.goto('/reroute/basic/a');
645-
expect(await page.textContent('h1')).toContain('Successfully rewritten');
645+
expect(await page.textContent('h1')).toContain(
646+
'Successfully rewritten, URL should still show a: /reroute/basic/a'
647+
);
646648
});
647649

648650
test('Returns a 500 response if reroute throws an error on the server', async ({ page }) => {

packages/kit/test/apps/hash-based-routing/test/test.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,13 +72,13 @@ test.describe('hash based navigation', () => {
7272
await page.locator('a[href="/#/reroute-a"]').click();
7373
await expect(page.locator('p')).toHaveText('rerouted');
7474
let url = new URL(page.url());
75-
expect(url.hash).toBe('#/rerouted');
75+
expect(url.hash).toBe('#/reroute-a');
7676

7777
await page.goto('/');
7878

7979
await page.locator('a[href="/#/reroute-b"]').click();
8080
await expect(page.locator('p')).toHaveText('rerouted');
8181
url = new URL(page.url());
82-
expect(url.hash).toBe('#/rerouted');
82+
expect(url.hash).toBe('#/reroute-b');
8383
});
8484
});

packages/kit/types/index.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -604,6 +604,7 @@ declare module '@sveltejs/kit' {
604604
* What type of client-side router to use.
605605
* - `'pathname'` is the default and means the current URL pathname determines the route
606606
* - `'hash'` means the route is determined by `location.hash`. In this case, SSR and prerendering are disabled. This is only recommended if `pathname` is not an option, for example because you don't control the webserver where your app is deployed.
607+
* It comes with some caveats: you can't use server-side rendering (or indeed any server logic), and you have to make sure that the links in your app all start with /#/, or they won't work. Beyond that, everything works exactly like a normal SvelteKit app.
607608
*
608609
* @default "pathname"
609610
* @since 2.14.0

0 commit comments

Comments
 (0)