diff --git a/.changeset/dirty-rivers-tickle.md b/.changeset/dirty-rivers-tickle.md new file mode 100644 index 000000000000..ae5f874cf63a --- /dev/null +++ b/.changeset/dirty-rivers-tickle.md @@ -0,0 +1,8 @@ +--- +"@sveltejs/adapter-cloudflare-workers": patch +"@sveltejs/adapter-cloudflare": patch +"@sveltejs/adapter-netlify": patch +"@sveltejs/kit": patch +--- + +fix: correctly handle relative paths when fetching assets on the server diff --git a/packages/adapter-cloudflare-workers/files/entry.js b/packages/adapter-cloudflare-workers/files/entry.js index c63b6482ad83..5f022e5096b9 100644 --- a/packages/adapter-cloudflare-workers/files/entry.js +++ b/packages/adapter-cloudflare-workers/files/entry.js @@ -57,7 +57,10 @@ export default { const filename = stripped_pathname.slice(base_path.length + 1); if (filename) { is_static_asset = - manifest.assets.has(filename) || manifest.assets.has(filename + '/index.html'); + manifest.assets.has(filename) || + manifest.assets.has(filename + '/index.html') || + filename in manifest._.server_assets || + filename + '/index.html' in manifest._.server_assets; } let location = pathname.at(-1) === '/' ? stripped_pathname : pathname + '/'; diff --git a/packages/adapter-cloudflare/src/worker.js b/packages/adapter-cloudflare/src/worker.js index 978c61a08ab8..c3c27a0b041f 100644 --- a/packages/adapter-cloudflare/src/worker.js +++ b/packages/adapter-cloudflare/src/worker.js @@ -33,7 +33,10 @@ const worker = { const filename = stripped_pathname.slice(base_path.length + 1); if (filename) { is_static_asset = - manifest.assets.has(filename) || manifest.assets.has(filename + '/index.html'); + manifest.assets.has(filename) || + manifest.assets.has(filename + '/index.html') || + filename in manifest._.server_assets || + filename + '/index.html' in manifest._.server_assets; } let location = pathname.at(-1) === '/' ? stripped_pathname : pathname + '/'; diff --git a/packages/adapter-netlify/src/edge.js b/packages/adapter-netlify/src/edge.js index dafbec5d0626..85f75483ea35 100644 --- a/packages/adapter-netlify/src/edge.js +++ b/packages/adapter-netlify/src/edge.js @@ -54,6 +54,8 @@ function is_static_file(request) { return ( manifest.assets.has(file) || manifest.assets.has(file + '/index.html') || + file in manifest._.server_assets || + file + '/index.html' in manifest._.server_assets || prerendered.has(pathname || '/') ); } diff --git a/packages/kit/src/exports/vite/dev/index.js b/packages/kit/src/exports/vite/dev/index.js index c93bf5e690ee..5d6f5cda4071 100644 --- a/packages/kit/src/exports/vite/dev/index.js +++ b/packages/kit/src/exports/vite/dev/index.js @@ -527,7 +527,13 @@ export async function dev(vite, vite_config, svelte_config) { if (remoteAddress) return remoteAddress; throw new Error('Could not determine clientAddress'); }, - read: (file) => fs.readFileSync(path.join(svelte_config.kit.files.assets, file)), + read: (file) => { + if (file in manifest._.server_assets) { + return fs.readFileSync(from_fs(file)); + } + + return fs.readFileSync(path.join(svelte_config.kit.files.assets, file)); + }, before_handle: (event, config, prerender) => { async_local_storage.enterWith({ event, config, prerender }); }, diff --git a/packages/kit/src/exports/vite/preview/index.js b/packages/kit/src/exports/vite/preview/index.js index 1e70d40573ee..2b4214e843f2 100644 --- a/packages/kit/src/exports/vite/preview/index.js +++ b/packages/kit/src/exports/vite/preview/index.js @@ -193,7 +193,13 @@ export async function preview(vite, vite_config, svelte_config) { if (remoteAddress) return remoteAddress; throw new Error('Could not determine clientAddress'); }, - read: (file) => fs.readFileSync(join(svelte_config.kit.files.assets, file)), + read: (file) => { + if (file in manifest._.server_assets) { + return fs.readFileSync(join(dir, file)); + } + + return fs.readFileSync(join(svelte_config.kit.files.assets, file)); + }, emulator }) ); diff --git a/packages/kit/src/runtime/server/fetch.js b/packages/kit/src/runtime/server/fetch.js index ff4a1fa527e2..7e2177d6661c 100644 --- a/packages/kit/src/runtime/server/fetch.js +++ b/packages/kit/src/runtime/server/fetch.js @@ -1,6 +1,7 @@ import * as set_cookie_parser from 'set-cookie-parser'; import { respond } from './respond.js'; import * as paths from '__sveltekit/paths'; +import { read_implementation } from '__sveltekit/server'; /** * @param {{ @@ -81,8 +82,9 @@ export function create_fetch({ event, options, manifest, state, get_cookie_heade ).slice(1); const filename_html = `${filename}/index.html`; // path may also match path/index.html - const is_asset = manifest.assets.has(filename); - const is_asset_html = manifest.assets.has(filename_html); + const is_asset = manifest.assets.has(filename) || filename in manifest._.server_assets; + const is_asset_html = + manifest.assets.has(filename_html) || filename_html in manifest._.server_assets; if (is_asset || is_asset_html) { const file = is_asset ? filename : filename_html; @@ -95,6 +97,16 @@ export function create_fetch({ event, options, manifest, state, get_cookie_heade return new Response(state.read(file), { headers: type ? { 'content-type': type } : {} }); + } else if (read_implementation) { + const length = manifest._.server_assets[file]; + const type = manifest.mimeTypes[file.slice(file.lastIndexOf('.'))]; + + return new Response(read_implementation(file), { + headers: { + 'Content-Length': '' + length, + 'Content-Type': type + } + }); } return await fetch(request); diff --git a/packages/kit/test/apps/basics/src/routes/endpoint-output/fetch-asset/absolute/+server.js b/packages/kit/test/apps/basics/src/routes/endpoint-output/fetch-asset/absolute/+server.js new file mode 100644 index 000000000000..cf86224b98f6 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/endpoint-output/fetch-asset/absolute/+server.js @@ -0,0 +1,14 @@ +import file_path from '../test.txt?url'; + +/** @type {import('@sveltejs/kit').RequestHandler} */ +export async function GET({ url }) { + const absolute = new URL(file_path, url.origin); + + const response = await fetch(absolute); + + return new Response(response.body, { + headers: { + 'Content-Type': response.headers.get('Content-Type') + } + }); +} diff --git a/packages/kit/test/apps/basics/src/routes/endpoint-output/fetch-asset/relative/+server.js b/packages/kit/test/apps/basics/src/routes/endpoint-output/fetch-asset/relative/+server.js new file mode 100644 index 000000000000..6be0859685ad --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/endpoint-output/fetch-asset/relative/+server.js @@ -0,0 +1,12 @@ +import file_path from '../test.txt?url'; + +/** @type {import('@sveltejs/kit').RequestHandler} */ +export async function GET({ fetch }) { + const response = await fetch(file_path); + + return new Response(response.body, { + headers: { + 'Content-Type': response.headers.get('Content-Type') + } + }); +} diff --git a/packages/kit/test/apps/basics/src/routes/endpoint-output/fetch-asset/test.txt b/packages/kit/test/apps/basics/src/routes/endpoint-output/fetch-asset/test.txt new file mode 100644 index 000000000000..ac2bac6d5300 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/endpoint-output/fetch-asset/test.txt @@ -0,0 +1 @@ +Cos sie konczy, cos zaczyna \ No newline at end of file diff --git a/packages/kit/test/apps/basics/test/server.test.js b/packages/kit/test/apps/basics/test/server.test.js index 3c8803a588f1..6590346e21b1 100644 --- a/packages/kit/test/apps/basics/test/server.test.js +++ b/packages/kit/test/apps/basics/test/server.test.js @@ -250,6 +250,20 @@ test.describe('Endpoints', () => { expect(response.status()).toBe(200); expect(await response.text()).toBe('catch-all'); }); + + test('can get assets using absolute path', async ({ request }) => { + const response = await request.get('/endpoint-output/fetch-asset/absolute'); + expect(response.status()).toBe(200); + expect(response.headers()['content-type']).toBe('text/plain'); + expect(await response.text()).toBe('Cos sie konczy, cos zaczyna'); + }); + + test('can get assets using relative path', async ({ request }) => { + const response = await request.get('/endpoint-output/fetch-asset/relative'); + expect(response.status()).toBe(200); + expect(response.headers()['content-type']).toBe('text/plain'); + expect(await response.text()).toBe('Cos sie konczy, cos zaczyna'); + }); }); test.describe('Errors', () => {