diff --git a/packages/kit/src/core/postbuild/fallback.js b/packages/kit/src/core/postbuild/fallback.js index f3356282f19f..90bb954b02d7 100644 --- a/packages/kit/src/core/postbuild/fallback.js +++ b/packages/kit/src/core/postbuild/fallback.js @@ -24,7 +24,12 @@ async function generate_fallback({ manifest_path, env, out_dir, origin, assets } /** @type {import('types').SSRManifest} */ const manifest = (await import(pathToFileURL(manifest_path).href)).manifest; - const { init, respond } = await configure({ building: true, manifest, env }); + const { init, respond } = await configure({ + building: true, + manifest, + env, + read_static: (file) => readFileSync(join(assets, file)) + }); await init(); const response = await respond(new Request(origin + '/[fallback]'), { @@ -36,8 +41,7 @@ async function generate_fallback({ manifest_path, env, out_dir, origin, assets } dependencies: new Map(), remote_responses: new Map(), resolved_route_ids: new Set() - }, - read: (file) => readFileSync(join(assets, file)) + } }); if (response.ok) { diff --git a/packages/kit/src/core/postbuild/prerender.js b/packages/kit/src/core/postbuild/prerender.js index 66a6949f77a1..cbf104f2bda8 100644 --- a/packages/kit/src/core/postbuild/prerender.js +++ b/packages/kit/src/core/postbuild/prerender.js @@ -54,14 +54,6 @@ async function prerender({ /** @type {import('types').ServerModule} */ const { configure, format_response } = await import(pathToFileURL(`${out}/server/index.js`).href); - const { init, respond } = await configure({ - building: true, - prerendering: true, - env, - manifest, - read: (file) => createReadableStream(`${out}/server/${file}`) - }); - const throw_handled = () => { throw new Error('__handled__'); }; @@ -170,12 +162,34 @@ async function prerender({ const emulator = await config.adapter?.emulate?.(); - /** @type {import('types').Logger} */ - const log = logger({ verbose }); - /** @type {Map} */ const saved = new Map(); + const { init, respond } = await configure({ + building: true, + prerendering: true, + env, + manifest, + read: (file) => createReadableStream(`${out}/server/${file}`), + read_static: (file) => { + // stuff we just wrote + const filepath = saved.get(file); + if (filepath) return readFileSync(filepath); + + // Static assets emitted during build + if (file.startsWith(config.appDir)) { + return readFileSync(`${out}/server/${file}`); + } + + // stuff in `static` + return readFileSync(join(config.files.assets, file)); + }, + emulator + }); + + /** @type {import('types').Logger} */ + const log = logger({ verbose }); + const handle_http_error = normalise_error_handler( 'handleHttpError', config.prerender.handleHttpError, @@ -385,21 +399,7 @@ async function prerender({ dependencies, remote_responses, resolved_route_ids - }, - read: (file) => { - // stuff we just wrote - const filepath = saved.get(file); - if (filepath) return readFileSync(filepath); - - // Static assets emitted during build - if (file.startsWith(config.appDir)) { - return readFileSync(`${out}/server/${file}`); - } - - // stuff in `static` - return readFileSync(join(config.files.assets, file)); - }, - emulator + } }); const encoded_id = response.headers.get('x-sveltekit-routeid'); diff --git a/packages/kit/src/exports/vite/dev/index.js b/packages/kit/src/exports/vite/dev/index.js index 5b39b3aeafd1..6c8db03bc85c 100644 --- a/packages/kit/src/exports/vite/dev/index.js +++ b/packages/kit/src/exports/vite/dev/index.js @@ -385,7 +385,20 @@ export async function dev( env, read: (file) => createReadableStream(from_fs(file)), assets, - fix_stack_trace + fix_stack_trace, + read_static: (file) => { + if (file in manifest.server_assets) { + return fs.readFileSync(from_fs(file)); + } + + return fs.readFileSync(path.join(svelte_config.files.assets, file)); + }, + before_handle: async (event, config, prerender, handle) => { + // we need to use .run because .enterWith() is not supported in Cloudflare Workers + // see https://blog.cloudflare.com/workers-node-js-asynclocalstorage/ + return await async_local_storage.run({ event, config, prerender }, handle); + }, + emulator }); await init(); @@ -423,20 +436,7 @@ export async function dev( const { remoteAddress } = req.socket; if (remoteAddress) return remoteAddress; throw new Error('Could not determine clientAddress'); - }, - read: (file) => { - if (file in manifest.server_assets) { - return fs.readFileSync(from_fs(file)); - } - - return fs.readFileSync(path.join(svelte_config.files.assets, file)); - }, - before_handle: async (event, config, prerender, handle) => { - // we need to use .run because .enterWith() is not supported in Cloudflare Workers - // see https://blog.cloudflare.com/workers-node-js-asynclocalstorage/ - return await async_local_storage.run({ event, config, prerender }, handle); - }, - emulator + } }); if (rendered.status === 404) { diff --git a/packages/kit/src/exports/vite/preview/index.js b/packages/kit/src/exports/vite/preview/index.js index 2757364cbff1..b21a148fb45a 100644 --- a/packages/kit/src/exports/vite/preview/index.js +++ b/packages/kit/src/exports/vite/preview/index.js @@ -51,7 +51,15 @@ export async function preview(vite, svelte_config) { manifest, env: loadEnv(vite.config.mode, svelte_config.env.dir, ''), read: (file) => createReadableStream(`${dir}/${file}`), - assets + assets, + read_static: (file) => { + if (file in manifest.server_assets) { + return fs.readFileSync(join(dir, file)); + } + + return fs.readFileSync(join(svelte_config.files.assets, file)); + }, + emulator: await svelte_config.adapter?.emulate?.() }); await server.init(); @@ -63,8 +71,6 @@ export async function preview(vite, svelte_config) { throw error; } - const emulator = await svelte_config.adapter?.emulate?.(); - return () => { // Remove the base middleware. It screws with the URL. // It also only lets through requests beginning with the base path, so that requests beginning @@ -218,15 +224,7 @@ export async function preview(vite, svelte_config) { const { remoteAddress } = req.socket; if (remoteAddress) return remoteAddress; throw new Error('Could not determine clientAddress'); - }, - read: (file) => { - if (file in manifest.server_assets) { - return fs.readFileSync(join(dir, file)); - } - - return fs.readFileSync(join(svelte_config.files.assets, file)); - }, - emulator + } }) ); }); diff --git a/packages/kit/src/runtime/server/fetch.js b/packages/kit/src/runtime/server/fetch.js index 5d355b1ce38a..2d73c2bdbf1f 100644 --- a/packages/kit/src/runtime/server/fetch.js +++ b/packages/kit/src/runtime/server/fetch.js @@ -2,7 +2,7 @@ import { parseSetCookie } from 'cookie'; import { noop } from '../../utils/functions.js'; import { respond } from './respond.js'; import * as paths from '#app/paths'; -import { hooks, manifest, read_implementation } from './internal.js'; +import { hooks, manifest, read_implementation, read_static } from './internal.js'; import { has_prerendered_path } from './utils.js'; import { fork_state_for_subrequest } from './state.js'; @@ -93,12 +93,12 @@ export function create_fetch({ event, get_cookie_header, set_internal }) { if (is_asset || is_asset_html) { const file = is_asset ? filename : filename_html; - if (state.read) { + if (read_static) { const type = is_asset ? manifest.mime_types[filename.slice(filename.lastIndexOf('.'))] : 'text/html'; - return new Response(state.read(file), { + return new Response(read_static(file), { headers: type ? { 'content-type': type } : {} }); } else if (read_implementation && file in manifest.server_assets) { diff --git a/packages/kit/src/runtime/server/index.js b/packages/kit/src/runtime/server/index.js index 2259004c4c49..36542860f6cd 100644 --- a/packages/kit/src/runtime/server/index.js +++ b/packages/kit/src/runtime/server/index.js @@ -1,6 +1,11 @@ import { set_building, set_prerendering } from '#app/env/server'; import { set_assets } from '../app/paths/internal/server.js'; -import { set_fix_stack_trace, set_manifest, set_read_implementation } from './internal.js'; +import { + set_fix_stack_trace, + set_host, + set_manifest, + set_read_implementation +} from './internal.js'; /** * Sets the module-level state the runtime reads, then loads the runtime. Everything that @@ -8,21 +13,16 @@ import { set_fix_stack_trace, set_manifest, set_read_implementation } from './in * @param {import('types').ServerConfigureOptions} opts * @returns {Promise} */ -export async function configure({ - building, - prerendering, - manifest, - read, - assets, - fix_stack_trace, - env -}) { +export async function configure(opts) { + const { building, prerendering, manifest, read, assets, fix_stack_trace, env } = opts; + if (building) set_building(); if (prerendering) set_prerendering(); if (manifest) set_manifest(manifest); if (read) set_read_implementation(read); if (assets !== undefined) set_assets(assets); if (fix_stack_trace) set_fix_stack_trace(fix_stack_trace); + set_host(opts); const instance = await import('./instance.js'); if (env) instance.set_env(env); diff --git a/packages/kit/src/runtime/server/internal.js b/packages/kit/src/runtime/server/internal.js index 32db717920ad..a720e3147305 100644 --- a/packages/kit/src/runtime/server/internal.js +++ b/packages/kit/src/runtime/server/internal.js @@ -57,6 +57,23 @@ export function set_manifest(value) { if (__SVELTEKIT_DEV__) save(manifest_key, value); } +/** @type {import('types').ServerConfigureOptions['read_static']} */ +export let read_static; + +/** @type {import('types').ServerConfigureOptions['before_handle']} */ +export let before_handle; + +/** @type {import('types').ServerConfigureOptions['emulator']} */ +export let emulator; + +/** + * What the process hosting the runtime lends it, set on every `configure` + * @param {import('types').ServerConfigureOptions} opts + */ +export function set_host(opts) { + ({ read_static, before_handle, emulator } = opts); +} + /** * @param {ServerHooks} value */ diff --git a/packages/kit/src/runtime/server/respond.js b/packages/kit/src/runtime/server/respond.js index 48e834d91cff..2dfaa47195f2 100644 --- a/packages/kit/src/runtime/server/respond.js +++ b/packages/kit/src/runtime/server/respond.js @@ -43,7 +43,7 @@ import { } from '../pathname.js'; import { server_data_serializer } from './page/data_serializer.js'; import { get_remote_id, handle_remote_call } from './remote-functions.js'; -import { hooks, manifest } from './internal.js'; +import { before_handle, emulator, hooks, manifest } from './internal.js'; import { options } from '/server.js'; import { respond_with_error, handle_fatal_error } from './page/respond_with_error.js'; @@ -191,8 +191,8 @@ export async function internal_respond(request, state) { }), locals: {}, params: {}, - platform: state.emulator?.platform - ? await state.emulator.platform({ + platform: emulator?.platform + ? await emulator.platform({ config: {}, prerender: !!state.prerendering?.fallback }) @@ -381,7 +381,7 @@ export async function internal_respond(request, state) { } } - if (state.before_handle || state.emulator?.platform) { + if (before_handle || emulator?.platform) { let config = {}; /** @type {import('types').PrerenderOption} */ @@ -396,12 +396,12 @@ export async function internal_respond(request, state) { prerender = state.prerender_default = page_nodes.prerender(); } - if (state.emulator?.platform) { - event.platform = await state.emulator.platform({ config, prerender }); + if (emulator?.platform) { + event.platform = await emulator.platform({ config, prerender }); } - if (state.before_handle) { - return await state.before_handle(event, config, prerender, handle); + if (before_handle) { + return await before_handle(event, config, prerender, handle); } } } diff --git a/packages/kit/src/runtime/server/state.js b/packages/kit/src/runtime/server/state.js index 4506096f4437..1aa605b8007a 100644 --- a/packages/kit/src/runtime/server/state.js +++ b/packages/kit/src/runtime/server/state.js @@ -27,9 +27,6 @@ export function create_request_state(options) { return { getClientAddress: options.getClientAddress, platform: options.platform, - read: options.read, - before_handle: options.before_handle, - emulator: options.emulator, prerendering: options.prerendering, prerender_default: undefined, error: false, diff --git a/packages/kit/src/types/internal.d.ts b/packages/kit/src/types/internal.d.ts index 8742ee7ea824..f481e943ab05 100644 --- a/packages/kit/src/types/internal.d.ts +++ b/packages/kit/src/types/internal.d.ts @@ -44,6 +44,16 @@ export interface ServerConfigureOptions extends Partial { building?: boolean; prerendering?: boolean; fix_stack_trace?: (error: Error) => void; + /** reads static assets from disk when user code fetches them, for the hosts kit runs itself */ + read_static?: (file: string) => Buffer; + /** used during development to check feature availability depending on the current route */ + before_handle?: ( + event: RequestEvent, + config: Record, + prerender: PrerenderOption, + handle: () => Promise + ) => Promise; + emulator?: Emulator; } export interface ServerInstance { @@ -190,16 +200,6 @@ export interface Env { export interface InternalRequestOptions extends RequestOptions { prerendering?: PrerenderOptions; - /** @internal for saving dependencies during prerendering and generating fallback pages */ - read: (file: string) => Buffer; - /** @internal used during development to check feature availability depending on the current route */ - before_handle?: ( - event: RequestEvent, - config: any, - prerender: PrerenderOption, - handle: () => Promise - ) => Promise; - emulator?: Emulator; } export interface ManifestData { @@ -704,19 +704,6 @@ export type RecordSpan = (options: { export interface RequestState { readonly getClientAddress: () => string; readonly platform?: any; - /** @internal reads from the filesystem when user code tries to fetch a static asset */ - readonly read?: (file: string) => Buffer; - /** - * Used to set up `__SVELTEKIT_TRACK__` which checks if a used feature is supported. - * E.g. if `read` from `$app/server` is used, it checks whether the route's config is compatible. - */ - readonly before_handle?: ( - event: RequestEvent, - config: Record, - prerender: PrerenderOption, - handle: () => Promise - ) => Promise; - readonly emulator?: Emulator; readonly prerendering?: PrerenderOptions; /** * When fetching data from a +server.js endpoint in `load`, the page's