Skip to content

Commit

Permalink
fix: use window.fetch in load functions to allow libraries to pat…
Browse files Browse the repository at this point in the history
…ch it (#10009)
  • Loading branch information
Lms24 authored Oct 10, 2023
1 parent c4a5f6c commit 67812ed
Show file tree
Hide file tree
Showing 5 changed files with 73 additions and 4 deletions.
5 changes: 5 additions & 0 deletions .changeset/sharp-birds-invent.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@sveltejs/kit': patch
---

fix: use `window.fetch` in `load` functions to allow libraries to patch it
31 changes: 27 additions & 4 deletions packages/kit/src/runtime/client/fetcher.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ if (DEV) {

check_stack_trace();

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

const heuristic = can_inspect_stack_trace
const in_load_heuristic = can_inspect_stack_trace
? stack.includes('src/runtime/client/client.js')
: loading;
if (heuristic) {

// This flag is set in initial_fetch and subsequent_fetch
const used_kit_fetch = init?.__sveltekit_fetch__;

if (in_load_heuristic && !used_kit_fetch) {
console.warn(
`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`
);
Expand Down Expand Up @@ -86,7 +94,7 @@ export function initial_fetch(resource, opts) {
return Promise.resolve(new Response(body, init));
}

return native_fetch(resource, opts);
return DEV ? dev_fetch(resource, opts) : window.fetch(resource, opts);
}

/**
Expand All @@ -112,7 +120,22 @@ export function subsequent_fetch(resource, resolved, opts) {
}
}

return native_fetch(resolved, opts);
return DEV ? dev_fetch(resolved, opts) : window.fetch(resolved, opts);
}

/**
* @param {RequestInfo | URL} resource
* @param {RequestInit & Record<string, any> | undefined} opts
*/
function dev_fetch(resource, opts) {
const patched_opts = { ...opts };
// This assigns the __sveltekit_fetch__ flag and makes it non-enumerable
Object.defineProperty(patched_opts, '__sveltekit_fetch__', {
value: true,
writable: true,
configurable: true
});
return window.fetch(resource, patched_opts);
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { browser } from '$app/environment';

/** @type {import('./$types').PageLoad} */
export async function load({ url, fetch }) {
// simulate fetch being monkey-patched by a 3rd party library
// run everything only in browser to avoid SSR caching
if (browser) {
const original_fetch = window.fetch;
window.fetch = (input, init) => {
console.log('Called a patched window.fetch');
return original_fetch(input, init);
};

const res = await fetch(`${url.origin}/load/window-fetch/data.json`);
const { answer } = await res.json();
window.fetch = original_fetch;
return { answer };
}
return {};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<script>
/** @type {import('./$types').PageData} */
export let data;
</script>

<h1>{data.answer}</h1>
15 changes: 15 additions & 0 deletions packages/kit/test/apps/basics/test/client.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,21 @@ test.describe('Load', () => {
expect(requests).toEqual([]);
});

test('permits 3rd party patching of fetch in universal load functions', async ({ page }) => {
/** @type {string[]} */
const logs = [];
page.on('console', (msg) => {
if (msg.type() === 'log') {
logs.push(msg.text());
}
});

await page.goto('/load/window-fetch/patching');
expect(await page.textContent('h1')).toBe('42');

expect(logs).toContain('Called a patched window.fetch');
});

if (process.env.DEV) {
test('using window.fetch causes a warning', async ({ page, baseURL }) => {
await Promise.all([
Expand Down

0 comments on commit 67812ed

Please sign in to comment.