Skip to content

Commit c202a84

Browse files
authored
[fix] resolve fetch requests from the target page (#5025)
* resolve load's fetch requests from the target page * add tests * add changeset
1 parent d1342c1 commit c202a84

File tree

6 files changed

+85
-4
lines changed

6 files changed

+85
-4
lines changed

.changeset/forty-lions-give.md

Lines changed: 5 additions & 0 deletions
Original file line numberOriginal file lineDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@sveltejs/kit': patch
3+
---
4+
5+
[breaking] resolve relative urls from the target page when using load's fetch

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

Lines changed: 37 additions & 4 deletions
Original file line numberOriginal file lineDiff line numberDiff line change
@@ -578,11 +578,44 @@ export function create_client({ target, session, base, trailing_slash }) {
578
node.uses.stuff = true;
578
node.uses.stuff = true;
579
return { ...stuff };
579
return { ...stuff };
580
},
580
},
581-
fetch(resource, info) {
581+
async fetch(resource, init) {
582-
const requested = typeof resource === 'string' ? resource : resource.url;
582+
let requested;
583-
add_dependency(requested);
583+
584+
if (typeof resource === 'string') {
585+
requested = resource;
586+
} else {
587+
requested = resource.url;
588+
589+
// we're not allowed to modify the received `Request` object, so in order
590+
// to fixup relative urls we create a new equivalent `init` object instead
591+
init = {
592+
// the request body must be consumed in memory until browsers
593+
// implement streaming request bodies and/or the body getter
594+
body:
595+
resource.method === 'GET' || resource.method === 'HEAD'
596+
? undefined
597+
: await resource.blob(),
598+
cache: resource.cache,
599+
credentials: resource.credentials,
600+
headers: resource.headers,
601+
integrity: resource.integrity,
602+
keepalive: resource.keepalive,
603+
method: resource.method,
604+
mode: resource.mode,
605+
redirect: resource.redirect,
606+
referrer: resource.referrer,
607+
referrerPolicy: resource.referrerPolicy,
608+
signal: resource.signal,
609+
...init
610+
};
611+
}
612+
613+
// we must fixup relative urls so they are resolved from the target page
614+
const normalized = new URL(requested, url).href;
615+
add_dependency(normalized);
584

616

585-
return started ? fetch(resource, info) : initial_fetch(resource, info);
617+
// prerendered pages may be served from any origin, so `initial_fetch` urls shouldn't be normalized
618+
return started ? fetch(normalized, init) : initial_fetch(requested, init);
586
},
619
},
587
status: status ?? null,
620
status: status ?? null,
588
error: error ?? null
621
error: error ?? null
Lines changed: 16 additions & 0 deletions
Original file line numberOriginal file lineDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
export function get() {
2+
return {
3+
body: {
4+
answer: 42
5+
}
6+
};
7+
}
8+
9+
/** @type {import('@sveltejs/kit').RequestHandler} */
10+
export async function post({ request }) {
11+
return {
12+
body: {
13+
question: await request.text()
14+
}
15+
};
16+
}
Lines changed: 19 additions & 0 deletions
Original file line numberOriginal file lineDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<script context="module">
2+
/** @type {import('@sveltejs/kit').Load} */
3+
export async function load({ fetch }) {
4+
const get = await fetch('fetch-relative.json');
5+
const post = await fetch('fetch-relative.json', { method: 'post', body: '?' });
6+
7+
return {
8+
props: { ...(await get.json()), ...(await post.json()) }
9+
};
10+
}
11+
</script>
12+
13+
<script>
14+
export let answer;
15+
export let question;
16+
</script>
17+
18+
<h1>the answer is {answer}</h1>
19+
<h2>the question was {question}</h2>

packages/kit/test/apps/basics/src/routes/load/index.svelte

Lines changed: 1 addition & 0 deletions
Original file line numberOriginal file lineDiff line numberDiff line change
@@ -21,6 +21,7 @@
21
<h1>bar == {foo}?</h1>
21
<h1>bar == {foo}?</h1>
22

22

23
<a href="/load/fetch-request">fetch request</a>
23
<a href="/load/fetch-request">fetch request</a>
24+
<a href="/load/fetch-relative">fetch relative</a>
24
<a href="/load/fetch-credentialed">fetch credentialed</a>
25
<a href="/load/fetch-credentialed">fetch credentialed</a>
25
<a href="/load/fetch-headers">fetch headers</a>
26
<a href="/load/fetch-headers">fetch headers</a>
26
<a href="/load/large-response">large response</a>
27
<a href="/load/large-response">large response</a>

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

Lines changed: 7 additions & 0 deletions
Original file line numberOriginal file lineDiff line numberDiff line change
@@ -1311,6 +1311,13 @@ test.describe.parallel('Load', () => {
1311
expect(await page.textContent('h1')).toBe('the answer is 42');
1311
expect(await page.textContent('h1')).toBe('the answer is 42');
1312
});
1312
});
1313

1313

1314+
test('fetch resolves urls relatively to the target page', async ({ page, clicknav }) => {
1315+
await page.goto('/load');
1316+
await clicknav('[href="/load/fetch-relative"]');
1317+
expect(await page.textContent('h1')).toBe('the answer is 42');
1318+
expect(await page.textContent('h2')).toBe('the question was ?');
1319+
});
1320+
1314
test('handles large responses', async ({ page }) => {
1321
test('handles large responses', async ({ page }) => {
1315
await page.goto('/load');
1322
await page.goto('/load');
1316

1323

0 commit comments

Comments
 (0)