Skip to content

Commit 67812ed

Browse files
authored
fix: use window.fetch in load functions to allow libraries to patch it (#10009)
1 parent c4a5f6c commit 67812ed

File tree

5 files changed

+73
-4
lines changed

5 files changed

+73
-4
lines changed

.changeset/sharp-birds-invent.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: use `window.fetch` in `load` functions to allow libraries to patch it

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

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ if (DEV) {
2323

2424
check_stack_trace();
2525

26+
/**
27+
* @param {RequestInfo | URL} input
28+
* @param {RequestInit & Record<string, any> | undefined} init
29+
*/
2630
window.fetch = (input, init) => {
2731
// Check if fetch was called via load_node. the lock method only checks if it was called at the
2832
// same time, but not necessarily if it was called from `load`.
@@ -36,10 +40,14 @@ if (DEV) {
3640
const cutoff = stack_array.findIndex((a) => a.includes('load@') || a.includes('at load'));
3741
const stack = stack_array.slice(0, cutoff + 2).join('\n');
3842

39-
const heuristic = can_inspect_stack_trace
43+
const in_load_heuristic = can_inspect_stack_trace
4044
? stack.includes('src/runtime/client/client.js')
4145
: loading;
42-
if (heuristic) {
46+
47+
// This flag is set in initial_fetch and subsequent_fetch
48+
const used_kit_fetch = init?.__sveltekit_fetch__;
49+
50+
if (in_load_heuristic && !used_kit_fetch) {
4351
console.warn(
4452
`Loading ${url} using \`window.fetch\`. For best results, use the \`fetch\` that is passed to your \`load\` function: https://kit.svelte.dev/docs/load#making-fetch-requests`
4553
);
@@ -86,7 +94,7 @@ export function initial_fetch(resource, opts) {
8694
return Promise.resolve(new Response(body, init));
8795
}
8896

89-
return native_fetch(resource, opts);
97+
return DEV ? dev_fetch(resource, opts) : window.fetch(resource, opts);
9098
}
9199

92100
/**
@@ -112,7 +120,22 @@ export function subsequent_fetch(resource, resolved, opts) {
112120
}
113121
}
114122

115-
return native_fetch(resolved, opts);
123+
return DEV ? dev_fetch(resolved, opts) : window.fetch(resolved, opts);
124+
}
125+
126+
/**
127+
* @param {RequestInfo | URL} resource
128+
* @param {RequestInit & Record<string, any> | undefined} opts
129+
*/
130+
function dev_fetch(resource, opts) {
131+
const patched_opts = { ...opts };
132+
// This assigns the __sveltekit_fetch__ flag and makes it non-enumerable
133+
Object.defineProperty(patched_opts, '__sveltekit_fetch__', {
134+
value: true,
135+
writable: true,
136+
configurable: true
137+
});
138+
return window.fetch(resource, patched_opts);
116139
}
117140

118141
/**
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { browser } from '$app/environment';
2+
3+
/** @type {import('./$types').PageLoad} */
4+
export async function load({ url, fetch }) {
5+
// simulate fetch being monkey-patched by a 3rd party library
6+
// run everything only in browser to avoid SSR caching
7+
if (browser) {
8+
const original_fetch = window.fetch;
9+
window.fetch = (input, init) => {
10+
console.log('Called a patched window.fetch');
11+
return original_fetch(input, init);
12+
};
13+
14+
const res = await fetch(`${url.origin}/load/window-fetch/data.json`);
15+
const { answer } = await res.json();
16+
window.fetch = original_fetch;
17+
return { answer };
18+
}
19+
return {};
20+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<script>
2+
/** @type {import('./$types').PageData} */
3+
export let data;
4+
</script>
5+
6+
<h1>{data.answer}</h1>

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

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,21 @@ test.describe('Load', () => {
228228
expect(requests).toEqual([]);
229229
});
230230

231+
test('permits 3rd party patching of fetch in universal load functions', async ({ page }) => {
232+
/** @type {string[]} */
233+
const logs = [];
234+
page.on('console', (msg) => {
235+
if (msg.type() === 'log') {
236+
logs.push(msg.text());
237+
}
238+
});
239+
240+
await page.goto('/load/window-fetch/patching');
241+
expect(await page.textContent('h1')).toBe('42');
242+
243+
expect(logs).toContain('Called a patched window.fetch');
244+
});
245+
231246
if (process.env.DEV) {
232247
test('using window.fetch causes a warning', async ({ page, baseURL }) => {
233248
await Promise.all([

0 commit comments

Comments
 (0)