Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/server-boot-configure.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@sveltejs/kit': patch
---

chore: configure the server runtime in one place, deprecate `Server` in favour of the `server` object written by `builder.generateServerInstance`
Original file line number Diff line number Diff line change
Expand Up @@ -520,6 +520,7 @@ For adapter authors, there are some additional changes:
- `builder.compress` returns a list of compressed files
- `builder.mkdirp` and `builder.rimraf` are deprecated in favour of `node:fs` methods
- `builder.generateManifest` has been removed — use `builder.generateServerInstance` to replace it, and `builder.manifest` to access the manifest
- the `Server` class exported from the server output is deprecated — use the `server` object written by `builder.generateServerInstance`

#### Server instrumentation

Expand Down
4 changes: 2 additions & 2 deletions packages/kit/src/core/adapt/builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ export function create_builder({
write(
dest,
dedent`
import { Server } from '${relative}/index.js';
import { create_server } from '${relative}/index.js';
const manifest = ${generate_manifest({
build_data,
prerendered: prerendered.paths,
Expand All @@ -240,7 +240,7 @@ export function create_builder({
remotes,
root: vite_config.root
})};
export const server = new Server(manifest);
export const server = create_server(manifest);
`
);
},
Expand Down
24 changes: 9 additions & 15 deletions packages/kit/src/core/postbuild/analyse.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,21 +42,15 @@ async function analyse({
const server_root = join(config.outDir, 'output');

/** @type {import('types').ServerInternalModule} */
const internal = await import(pathToFileURL(`${server_root}/server/internal.js`).href);

// configure `import { building } from '$app/env'` —
// essential we do this before analysing the code
internal.set_building();

// set `read` and `manifest`, in case they're used in initialisation
internal.set_manifest(manifest);
internal.set_read_implementation((file) => createReadableStream(`${server_root}/server/${file}`));

// `set_env` lives in a separate module that imports the user's `src/env` config. We import it
// *after* `set_building()` so that `building`-dependent expressions resolve correctly
/** @type {typeof import('<sveltekit:generated>/env/config.js')} */
const { set_env } = await import(pathToFileURL(`${server_root}/server/env.js`).href);
set_env(env);
const { configure } = await import(pathToFileURL(`${server_root}/server/internal.js`).href);

// everything user modules may read at their top level, before any of them are analysed
await configure({
building: true,
manifest,
env,
read: (file) => createReadableStream(`${server_root}/server/${file}`)
});

/** @type {import('types').ServerMetadata} */
const metadata = {
Expand Down
12 changes: 5 additions & 7 deletions packages/kit/src/core/postbuild/fallback.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,18 @@ async function generate_fallback({ manifest_path, env, out_dir, origin, assets }
const server_root = join(out_dir, 'output');

/** @type {import('types').ServerInternalModule} */
const { set_building } = await import(pathToFileURL(`${server_root}/server/internal.js`).href);
const { configure } = await import(pathToFileURL(`${server_root}/server/internal.js`).href);
await configure({ building: true });

/** @type {import('types').ServerModule} */
const { Server } = await import(pathToFileURL(`${server_root}/server/index.js`).href);
const { init, respond } = await import(pathToFileURL(`${server_root}/server/index.js`).href);

/** @type {import('types').SSRManifest} */
const manifest = (await import(pathToFileURL(manifest_path).href)).manifest;

set_building();
await init({ manifest, env });

const server = new Server(manifest);
await server.init({ env });

const response = await server.respond(new Request(origin + '/[fallback]'), {
const response = await respond(new Request(origin + '/[fallback]'), {
getClientAddress: () => {
throw new Error('Cannot read clientAddress during prerendering');
},
Expand Down
31 changes: 10 additions & 21 deletions packages/kit/src/core/postbuild/prerender.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,22 +52,15 @@ async function prerender({
const manifest = (await import(pathToFileURL(manifest_path).href)).manifest;

/** @type {import('types').ServerInternalModule} */
const { set_building, set_prerendering, set_manifest, set_read_implementation, format_response } =
await import(pathToFileURL(`${out}/server/internal.js`).href);

// configure `import { building } from `$app/env` —
// essential we do this before analysing the code
set_building();
set_prerendering();
const { configure, format_response } = await import(
pathToFileURL(`${out}/server/internal.js`).href
);

// `set_env` and `Server` live in modules that import the user's `src/env` config. We import them
// *after* `set_building()` so that `building`-dependent expressions resolve correctly
/** @type {typeof import('<sveltekit:generated>/env/config.js')} */
const { set_env } = await import(pathToFileURL(`${out}/server/env.js`).href);
set_env(env);
// `building` and `prerendering` have to be set before the server module evaluates the user's env config
await configure({ building: true, prerendering: true, env });

/** @type {import('types').ServerModule} */
const { Server } = await import(pathToFileURL(`${out}/server/index.js`).href);
const { init, respond } = await import(pathToFileURL(`${out}/server/index.js`).href);

const throw_handled = () => {
throw new Error('__handled__');
Expand Down Expand Up @@ -384,7 +377,7 @@ async function prerender({

const request = new Request(prerender_origin + encoded);

const response = await server.respond(request, {
const response = await respond(request, {
getClientAddress() {
throw new Error('Cannot read clientAddress during prerendering');
},
Expand Down Expand Up @@ -653,8 +646,8 @@ async function prerender({

// the user's remote function modules may reference `read` or the `manifest` at the top-level
// so we need to set them before evaluating those modules to avoid potential runtime errors
set_manifest(manifest);
set_read_implementation((file) => createReadableStream(`${out}/server/${file}`));
const read = (/** @type {string} */ file) => createReadableStream(`${out}/server/${file}`);
await configure({ manifest, read });
Comment thread
teemingc marked this conversation as resolved.

/** @type {Array<import('types').RemotePrerenderInternals>} */
const prerender_functions = [];
Expand All @@ -676,11 +669,7 @@ async function prerender({

// only run the server after the `should_prerender` check so that we
// don't run the user's init hook unnecessarily
const server = new Server(manifest);
await server.init({
env,
read: (file) => createReadableStream(`${config.outDir}/output/server/${file}`)
});
await init({ manifest, env, read });

log.info('Prerendering');

Expand Down
39 changes: 38 additions & 1 deletion packages/kit/src/core/sync/write_server.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ const server_template = ({
import { set_building, set_prerendering } from '$app/env/server';
import { set_assets } from '$app/paths/internal/server';
import { set_fix_stack_trace, set_manifest, set_read_implementation, format_response } from '${runtime_directory}/server/internal.js';
import { stream_from_iterable } from '${runtime_directory}/utils.js';
import error from './shared/error-template.js';

export const options = {
Expand Down Expand Up @@ -69,7 +70,43 @@ export async function get_hooks() {
};
}

export { set_assets, set_building, set_fix_stack_trace, set_manifest, set_prerendering, set_read_implementation, format_response };
/**
* Sets the module-level state the server runtime reads, in the order it has to happen:
* \`building\` and \`prerendering\` before the env module evaluates the user's \`src/env\` config,
* which may read them, and everything else before user modules run
* @param {import('types').ServerConfigureOptions} opts
*/
export async function configure({ building, prerendering, env, manifest, read, assets, fix_stack_trace }) {
if (building) set_building();
if (prerendering) set_prerendering();

if (manifest) set_manifest(manifest);
if (assets !== undefined) set_assets(assets);
if (fix_stack_trace) set_fix_stack_trace(fix_stack_trace);

if (read) {
// the public \`read\` may return a promise, the runtime expects a stream
set_read_implementation((file) => {
const result = read(file);
if (result instanceof ReadableStream) return result;

return stream_from_iterable(
(async function* () {
const stream = await result;
if (stream) yield* stream;
})()
);
});
}

// evaluates the user's \`src/env\` config, which may read any of the above
if (env) {
const { set_env } = await import('<sveltekit:generated>/env/config.js');
set_env(env);
}
}

export { format_response };
`;

/**
Expand Down
20 changes: 8 additions & 12 deletions packages/kit/src/exports/vite/dev/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -376,24 +376,20 @@ export async function dev(
await runner.import(resolved_instrumentation);
}

// we have to import `Server` before calling `set_assets`
const { Server } = /** @type {ServerModule} */ (
const { init, respond } = /** @type {ServerModule} */ (
await runner.import(`${get_runtime_base(root)}/server/index.js`)
);

const { set_fix_stack_trace, format_response } = await runner.import(
const { format_response } = await runner.import(
`${get_runtime_base(root)}/server/internal.js`
);
set_fix_stack_trace(fix_stack_trace);

const { set_assets } = await runner.import('$app/paths/internal/server');
set_assets(assets);

const server = new Server(manifest);

await server.init({
await init({
manifest,
env,
read: (file) => createReadableStream(from_fs(file))
read: (file) => createReadableStream(from_fs(file)),
assets,
fix_stack_trace
});

const request = (svelte_config.adapter?.vite?.getRequest ?? getRequest)({
Expand Down Expand Up @@ -424,7 +420,7 @@ export async function dev(
return;
}

const rendered = await server.respond(request, {
const rendered = await respond(request, {
getClientAddress: () => {
const { remoteAddress } = req.socket;
if (remoteAddress) return remoteAddress;
Expand Down
19 changes: 7 additions & 12 deletions packages/kit/src/exports/vite/preview/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/** @import { NextHandleFunction } from 'connect' */
/** @import { PreviewServer } from 'vite' */
/** @import { ValidatedConfig, ServerInternalModule, ServerModule } from 'types' */
/** @import { ValidatedConfig, ServerModule } from 'types' */
import fs from 'node:fs';
import { join } from 'node:path';
import { pathToFileURL } from 'node:url';
Expand Down Expand Up @@ -37,23 +37,18 @@ export async function preview(vite, svelte_config) {
await import(pathToFileURL(instrumentation).href);
}

/** @type {ServerInternalModule} */
const { set_assets } = await import(pathToFileURL(join(dir, 'internal.js')).href);

/** @type {ServerModule} */
const { Server } = await import(pathToFileURL(join(dir, 'index.js')).href);
const { init, respond } = await import(pathToFileURL(join(dir, 'index.js')).href);

/** @type {{ manifest: import('types').SSRManifest }} */
const { manifest } = await import(pathToFileURL(join(dir, 'manifest.js')).href);

set_assets(assets);

const server = new Server(manifest);

try {
await server.init({
await init({
manifest,
env: loadEnv(vite.config.mode, svelte_config.env.dir, ''),
read: (file) => createReadableStream(`${dir}/${file}`)
read: (file) => createReadableStream(`${dir}/${file}`),
assets
});
} catch (error) {
// Vite erases the error message when starting the preview server so we store
Expand Down Expand Up @@ -213,7 +208,7 @@ export async function preview(vite, svelte_config) {

(svelte_config.adapter?.vite?.setResponse ?? setResponse)(
res,
await server.respond(request, {
await respond(request, {
getClientAddress: () => {
const { remoteAddress } = req.socket;
if (remoteAddress) return remoteAddress;
Expand Down
Loading
Loading