Description
Describe the bug
When a request object is used to fetch data in +page.js
instead of a URL the request is repeated on client hydration
export async function load({ fetch }) {
// const res = await fetch('/mock');
// const res = await fetch('http://localhost:5173/' + 'mock'); --- request not repeated on client hydration
const res = await fetch(new Request('http://localhost:5173/' + 'mock')); --- request repeated
const data = await res.json();
return data;
}
Reproduction
https://github.com/vedadeepta/sveltejs-kit-template-default-bug
Minimal repro here.
check the network tab - /mock
api is made on client hydration.
Then goto routes/page.js
and comment out the fetch request with new Request
object in it and make a simple fetch request with just the the URL. check network tab again - no repeated /mock
request on client hydration
Logs
No response
System Info
System:
OS: macOS 13.4.1
CPU: (10) arm64 Apple M1 Pro
Memory: 417.28 MB / 16.00 GB
Shell: 5.9 - /bin/zsh
Binaries:
Node: 18.16.1 - /usr/local/bin/node
Yarn: 1.22.19 - /opt/homebrew/bin/yarn
npm: 9.6.7 - /opt/homebrew/bin/npm
pnpm: 8.6.7 - /opt/homebrew/bin/pnpm
Browsers:
Chrome: 115.0.5790.114
Safari: 16.5.2
npmPackages:
@sveltejs/adapter-auto: ^2.0.0 => 2.1.0
@sveltejs/kit: ^1.20.4 => 1.22.4
svelte: ^4.0.5 => 4.1.2
vite: ^4.4.2 => 4.4.8
Severity
serious, but I can work around it
Possible Cause / Fix
The issue lies in the build_selector function.
When we make request like this fetch(new Request(url))
if opts?.headers
is true which creates a data-hash=[...]
property in the selector
but when we make a request like this fetch(url)
if opts?.headers
is false and data-hash
property is not there in the selector string.
So either we need to changes serialize_data.js
fn or the build_selector
function to account for empty headers when using the request object
we can do something like this to detect is headers are empty and generate hash accordingly
function isHeaderEmpty(headers) {
if (headers == null) {
return true
}
if (headers instanceof Headers) {
return !headers.values().length
}
return false
}
/**
* Build the cache key for a given request
* @param {URL | RequestInfo} resource
* @param {RequestInit} [opts]
*/
function build_selector(resource, opts) {
const url = JSON.stringify(resource instanceof Request ? resource.url : resource);
let selector = `script[data-sveltekit-fetched][data-url=${url}]`;
console.log(opts);
if (!isHeaderEmpty(opts?.headers) || opts?.body) {
/** @type {import('types').StrictBody[]} */
const values = [];
console.log(opts.headers, opts.headers instanceof Headers);
if (opts.headers) {
values.push([...new Headers(opts.headers)].join(','));
}
if (opts.body && (typeof opts.body === 'string' || ArrayBuffer.isView(opts.body))) {
values.push(opts.body);
}
selector += `[data-hash="${hash(...values)}"]`;
}
return selector;
}