From b6e1fcaf6024072bdd05988a2a8e1725e80c24e1 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Mon, 22 May 2023 17:27:43 +0200 Subject: [PATCH 1/8] fix: Use `window.fetch` instead of unpatched fetch in `load` functions --- packages/kit/src/runtime/client/fetcher.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/kit/src/runtime/client/fetcher.js b/packages/kit/src/runtime/client/fetcher.js index df709d1f3968..54a505037c4d 100644 --- a/packages/kit/src/runtime/client/fetcher.js +++ b/packages/kit/src/runtime/client/fetcher.js @@ -86,7 +86,7 @@ export function initial_fetch(resource, opts) { return Promise.resolve(new Response(body, init)); } - return native_fetch(resource, opts); + return DEV ? native_fetch(resource, opts) : window.fetch(resource, opts); } /** @@ -112,7 +112,7 @@ export function subsequent_fetch(resource, resolved, opts) { } } - return native_fetch(resolved, opts); + return DEV ? native_fetch(resolved, opts) : window.fetch(resolved, opts); } /** From ae835cf360f588438b0386719a4c661201090e90 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Tue, 23 May 2023 10:03:43 +0200 Subject: [PATCH 2/8] changeset --- .changeset/sharp-birds-invent.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/sharp-birds-invent.md diff --git a/.changeset/sharp-birds-invent.md b/.changeset/sharp-birds-invent.md new file mode 100644 index 000000000000..e0f742701e68 --- /dev/null +++ b/.changeset/sharp-birds-invent.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: Use `window.fetch` instead of unpatched fetch in `load` functions From 4500e5bffec31c96f125519d80a1010495d9685c Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Tue, 23 May 2023 10:19:47 +0200 Subject: [PATCH 3/8] always use window.fetch, pass flag to patched window.fetch --- packages/kit/src/runtime/client/fetcher.js | 31 +++++++++++++++++++--- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/packages/kit/src/runtime/client/fetcher.js b/packages/kit/src/runtime/client/fetcher.js index 54a505037c4d..f396c27e46d8 100644 --- a/packages/kit/src/runtime/client/fetcher.js +++ b/packages/kit/src/runtime/client/fetcher.js @@ -23,6 +23,11 @@ if (DEV) { check_stack_trace(); + /** + * + * @param {RequestInfo | URL} input + * @param {RequestInit & Record | 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`. @@ -36,10 +41,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 inLoadHeuristic = 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 calledViaSvelteKitFetch = init?.__sveltekit_fetch__; + + if (inLoadHeuristic && !calledViaSvelteKitFetch) { 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` ); @@ -86,7 +95,14 @@ export function initial_fetch(resource, opts) { return Promise.resolve(new Response(body, init)); } - return DEV ? native_fetch(resource, opts) : window.fetch(resource, opts); + const patchedOpts = DEV + ? { + ...opts, + __sveltekit_fetch__: true + } + : opts; + + return window.fetch(resource, patchedOpts); } /** @@ -112,7 +128,14 @@ export function subsequent_fetch(resource, resolved, opts) { } } - return DEV ? native_fetch(resolved, opts) : window.fetch(resolved, opts); + const patchedOpts = DEV + ? { + ...opts, + __sveltekit_fetch__: true + } + : opts; + + return window.fetch(resolved, patchedOpts); } /** From 0e1d0e04b8b07b1be64905036801a0b8dffe6737 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Tue, 23 May 2023 11:50:32 +0200 Subject: [PATCH 4/8] add test --- .../load/window-fetch/patching/+page.js | 20 +++++++++++++++++++ .../load/window-fetch/patching/+page.svelte | 6 ++++++ .../kit/test/apps/basics/test/client.test.js | 15 ++++++++++++++ 3 files changed, 41 insertions(+) create mode 100644 packages/kit/test/apps/basics/src/routes/load/window-fetch/patching/+page.js create mode 100644 packages/kit/test/apps/basics/src/routes/load/window-fetch/patching/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/load/window-fetch/patching/+page.js b/packages/kit/test/apps/basics/src/routes/load/window-fetch/patching/+page.js new file mode 100644 index 000000000000..115d5e6891cc --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/load/window-fetch/patching/+page.js @@ -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 {}; +} diff --git a/packages/kit/test/apps/basics/src/routes/load/window-fetch/patching/+page.svelte b/packages/kit/test/apps/basics/src/routes/load/window-fetch/patching/+page.svelte new file mode 100644 index 000000000000..7c7268b87da6 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/load/window-fetch/patching/+page.svelte @@ -0,0 +1,6 @@ + + +

{data.answer}

diff --git a/packages/kit/test/apps/basics/test/client.test.js b/packages/kit/test/apps/basics/test/client.test.js index 1405042c235d..53359dfe2bb3 100644 --- a/packages/kit/test/apps/basics/test/client.test.js +++ b/packages/kit/test/apps/basics/test/client.test.js @@ -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([ From 9dfde889bc778dcf148e0b13d6a1cfdfeb573a0c Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Tue, 27 Jun 2023 10:09:59 +0200 Subject: [PATCH 5/8] apply code review suggestions --- packages/kit/src/runtime/client/fetcher.js | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/packages/kit/src/runtime/client/fetcher.js b/packages/kit/src/runtime/client/fetcher.js index f396c27e46d8..ca41d5fec577 100644 --- a/packages/kit/src/runtime/client/fetcher.js +++ b/packages/kit/src/runtime/client/fetcher.js @@ -24,7 +24,6 @@ if (DEV) { check_stack_trace(); /** - * * @param {RequestInfo | URL} input * @param {RequestInit & Record | undefined} init */ @@ -41,14 +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 inLoadHeuristic = can_inspect_stack_trace + const in_load_heuristic = can_inspect_stack_trace ? stack.includes('src/runtime/client/client.js') : loading; // This flag is set in initial_fetch and subsequent_fetch - const calledViaSvelteKitFetch = init?.__sveltekit_fetch__; + const used_kit_fetch = init?.__sveltekit_fetch__; - if (inLoadHeuristic && !calledViaSvelteKitFetch) { + 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` ); @@ -95,14 +94,14 @@ export function initial_fetch(resource, opts) { return Promise.resolve(new Response(body, init)); } - const patchedOpts = DEV + const patched_opts = DEV ? { ...opts, __sveltekit_fetch__: true } : opts; - return window.fetch(resource, patchedOpts); + return window.fetch(resource, patched_opts); } /** From 45c93a48dee5e318163a1f852409b5d8752efbe9 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Tue, 27 Jun 2023 10:17:40 +0200 Subject: [PATCH 6/8] make __sveltekit_fetch__ non-enumerable --- packages/kit/src/runtime/client/fetcher.js | 32 +++++++++++++--------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/packages/kit/src/runtime/client/fetcher.js b/packages/kit/src/runtime/client/fetcher.js index ca41d5fec577..2accbe49b0cf 100644 --- a/packages/kit/src/runtime/client/fetcher.js +++ b/packages/kit/src/runtime/client/fetcher.js @@ -94,12 +94,15 @@ export function initial_fetch(resource, opts) { return Promise.resolve(new Response(body, init)); } - const patched_opts = DEV - ? { - ...opts, - __sveltekit_fetch__: true - } - : opts; + const patched_opts = { ...opts }; + if (DEV) { + // 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); } @@ -127,14 +130,17 @@ export function subsequent_fetch(resource, resolved, opts) { } } - const patchedOpts = DEV - ? { - ...opts, - __sveltekit_fetch__: true - } - : opts; + const patched_opts = { ...opts }; + if (DEV) { + // 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(resolved, patchedOpts); + return window.fetch(resolved, patched_opts); } /** From 64e858d7af69add0a7b26f6ed0e9aa1e688615cd Mon Sep 17 00:00:00 2001 From: Ben McCann <322311+benmccann@users.noreply.github.com> Date: Mon, 9 Oct 2023 14:08:58 -0700 Subject: [PATCH 7/8] Update .changeset/sharp-birds-invent.md --- .changeset/sharp-birds-invent.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/sharp-birds-invent.md b/.changeset/sharp-birds-invent.md index e0f742701e68..e1bdbc421f15 100644 --- a/.changeset/sharp-birds-invent.md +++ b/.changeset/sharp-birds-invent.md @@ -2,4 +2,4 @@ '@sveltejs/kit': patch --- -fix: Use `window.fetch` instead of unpatched fetch in `load` functions +fix: use `window.fetch` in `load` functions to allow libraries to patch it From b1420c5fb5353d2ea64282c6bbd4238d046b5c30 Mon Sep 17 00:00:00 2001 From: Ben McCann <322311+benmccann@users.noreply.github.com> Date: Mon, 9 Oct 2023 16:39:50 -0700 Subject: [PATCH 8/8] DRY --- packages/kit/src/runtime/client/fetcher.js | 37 ++++++++++------------ 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/packages/kit/src/runtime/client/fetcher.js b/packages/kit/src/runtime/client/fetcher.js index 2accbe49b0cf..b31a88a2a99f 100644 --- a/packages/kit/src/runtime/client/fetcher.js +++ b/packages/kit/src/runtime/client/fetcher.js @@ -94,17 +94,7 @@ export function initial_fetch(resource, opts) { return Promise.resolve(new Response(body, init)); } - const patched_opts = { ...opts }; - if (DEV) { - // 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); + return DEV ? dev_fetch(resource, opts) : window.fetch(resource, opts); } /** @@ -130,17 +120,22 @@ export function subsequent_fetch(resource, resolved, opts) { } } - const patched_opts = { ...opts }; - if (DEV) { - // This assigns the __sveltekit_fetch__ flag and makes it non-enumerable - Object.defineProperty(patched_opts, '__sveltekit_fetch__', { - value: true, - writable: true, - configurable: true - }); - } + return DEV ? dev_fetch(resolved, opts) : window.fetch(resolved, opts); +} - return window.fetch(resolved, patched_opts); +/** + * @param {RequestInfo | URL} resource + * @param {RequestInit & Record | 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); } /**