Skip to content
Merged
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
10 changes: 10 additions & 0 deletions .changeset/smooth-flies-begin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
"@sveltejs/adapter-cloudflare": major
"@sveltejs/adapter-netlify": major
"@sveltejs/adapter-node": major
"@sveltejs/adapter-vercel": major
"@sveltejs/kit": major
---

breaking: replace the `builder.generateManifest` with `builder.generateServerInstance` and `builder.manifest`

4 changes: 2 additions & 2 deletions documentation/docs/25-build-and-deploy/99-writing-adapters.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ Within the `adapt` method, there are a number of things that an adapter should d
- Clear out the build directory
- Write SvelteKit output with `builder.writeClient`, `builder.writeServer`, and `builder.writePrerendered`
- Output code that:
- Imports `Server` from `${builder.getServerDirectory()}/index.js`
- Instantiates the app with a manifest generated with `builder.generateManifest({ relativePath })`
- Imports a `server` instance generated by `${builder.generateServerInstance()}`
- Runs `await server.init({ env, read })`
- Listens for requests from the platform, converts them to a standard [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request) if necessary, calls the `server.respond(request, { getClientAddress })` function to generate a [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) and responds with it
- expose any platform-specific information to SvelteKit via the `platform` option passed to `server.respond`
- Bundle the output to avoid needing to install dependencies on the target platform, if necessary
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,7 @@ For adapter authors, there are some additional changes:
- `builder.createEntries` has been removed — use `builder.writeClient`, `builder.writeServer` and `builder.writePrerendered` directly
- `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

## Responses

Expand Down
8 changes: 6 additions & 2 deletions packages/adapter-bun/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -136,9 +136,13 @@ export default function (opts = {}) {
const manifest_file = path.resolve(server, 'manifest.js');
const server_options_file = path.resolve(src_dir, 'options.js');

const tmp = builder.getBuildDirectory('bun-tmp');
fs.mkdirSync(tmp, { recursive: true });
builder.generateServerInstance(`${tmp}/server.js`);

const virtual_files = {
[manifest_file]:
`export const manifest = ${builder.generateManifest({ relativePath: './' })};\n` +
`export const app_dir = ${JSON.stringify(builder.config.appDir)};\n` +
`export const base = ${JSON.stringify(builder.config.paths.base || '/')};\n` +
`export const embed = ${JSON.stringify(!!buildOptions.compile)};\n` +
`export const env_prefix = ${JSON.stringify(envPrefix)};\n` +
Expand Down Expand Up @@ -198,7 +202,7 @@ export default function (opts = {}) {
name: 'adapter-bun',
setup(build) {
build.onResolve({ filter: /^(SERVER|MANIFEST|ROUTES|SERVER_OPTIONS)$/ }, ({ path }) => {
if (path === 'SERVER') return { path: `${server}/index.js` };
if (path === 'SERVER') return { path: `${tmp}/server.js` };
if (path === 'MANIFEST') return { path: manifest_file };
if (path === 'ROUTES') return { path: routes_file };
if (path === 'SERVER_OPTIONS') return { path: server_options_file };
Expand Down
4 changes: 2 additions & 2 deletions packages/adapter-bun/internal.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
declare module 'MANIFEST' {
export const manifest: import('@sveltejs/kit').SSRManifest;
export const app_dir: string;
export const base: string;
export const embed: boolean;
export const env_prefix: string;
Expand All @@ -12,7 +12,7 @@ declare module 'ROUTES' {
}

declare module 'SERVER' {
export { Server } from '@sveltejs/kit';
export const server: import('@sveltejs/kit').Server;
}

declare module 'SERVER_OPTIONS' {
Expand Down
6 changes: 2 additions & 4 deletions packages/adapter-bun/src/handler.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
/** @import { Server as BunServer } from 'bun' */
import { Server } from 'SERVER';
import { manifest, origin, env_prefix } from 'MANIFEST';
import { server } from 'SERVER';
import { origin, env_prefix } from 'MANIFEST';
import { server_assets } from 'ROUTES';
import { env, number_env } from './env.js';

const server = new Server(manifest);

const address_header = env('ADDRESS_HEADER', '').toLowerCase();
const protocol_header = env('PROTOCOL_HEADER', '').toLowerCase();
const host_header = env('HOST_HEADER', '').toLowerCase();
Expand Down
4 changes: 2 additions & 2 deletions packages/adapter-bun/src/routes-util.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/** @import { BunFile, BunRequest, Serve } from 'bun' */
import { manifest, base, embed } from 'MANIFEST';
import { app_dir, base, embed } from 'MANIFEST';
import path from 'node:path';
import { fileURLToPath } from 'node:url';

Expand Down Expand Up @@ -174,7 +174,7 @@ function file_route(file, meta, extra_headers = {}) {
* @returns {Array<[string, RouteHandler]>}
*/
export function client_asset(url, filename = url, meta) {
const immutable = url.startsWith(`${manifest.appDir}/immutable/`);
const immutable = url.startsWith(`${app_dir}/immutable/`);
Comment thread
vercel[bot] marked this conversation as resolved.
Resolved
const route = file_route(
resolve_file('client', filename),
meta,
Expand Down
13 changes: 9 additions & 4 deletions packages/adapter-bun/test/adapter.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ describe('Bun build configuration', () => {

const files = bun_build.mock.calls[0][0].files;
expect(files[manifest_file]).toBe(
'export const manifest = {"appDir":"_app"};\n' +
'export const app_dir = "_app";\n' +
'export const base = "/docs";\n' +
'export const embed = false;\n' +
'export const env_prefix = "APP_";\n' +
Expand All @@ -130,7 +130,9 @@ describe('Bun build configuration', () => {
expect(files[options_file]).toBe(
'export default {"hostname":"127.0.0.1","port":4000,"development":true};'
);
expect(builder.generateManifest).toHaveBeenCalledWith({ relativePath: './' });
expect(builder.generateServerInstance).toHaveBeenCalledWith(
'.svelte-kit/output/bun-tmp/server.js'
);
});

test('resolves generated runtime modules through the Bun plugin', async () => {
Expand All @@ -144,7 +146,7 @@ describe('Bun build configuration', () => {
);
const resolve_module = on_resolve.mock.calls[0][1];
expect(resolve_module({ path: 'SERVER' })).toEqual({
path: '.svelte-kit/output/server/index.js'
path: '.svelte-kit/output/bun-tmp/server.js'
});
expect(resolve_module({ path: 'MANIFEST' })).toEqual({ path: manifest_file });
expect(resolve_module({ path: 'ROUTES' })).toEqual({ path: routes_file });
Expand Down Expand Up @@ -576,7 +578,7 @@ function create_builder({
instrumentation?: boolean;
} = {}) {
return {
config: { outDir: '.svelte-kit', paths: { base, origin } },
config: { outDir: '.svelte-kit', paths: { base, origin }, appDir: '_app' },
routes,
prerendered: {
pages: new Map(prerendered_pages),
Expand All @@ -588,6 +590,9 @@ function create_builder({
warn: mock((_message: string) => {}),
info: mock((_message: string) => {})
},
getAppPath: () => `${base}/_app`,
generateServerInstance: mock(() => {}),
getBuildDirectory: (dir: string) => `.svelte-kit/output/${dir}`,
getServerDirectory: () => '.svelte-kit/output/server',
writeClient: mock(() => client_files),
writePrerendered: mock(() => prerendered_files),
Expand Down
12 changes: 2 additions & 10 deletions packages/adapter-bun/test/handler.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ afterEach(() => {
test('initializes SvelteKit with Bun environment variables and server-readable assets', async () => {
const loaded = await load_handler();

expect(loaded.construct).toHaveBeenCalledWith(loaded.manifest);
expect(loaded.init).toHaveBeenCalledWith({
env: Bun.env,
read: expect.any(Function)
Expand Down Expand Up @@ -196,23 +195,18 @@ async function load_handler({
envPrefix = '',
response = new Response('ok')
}: { origin?: string; envPrefix?: string; response?: Response } = {}) {
const manifest = { appDir: '_app' };
const stream = new ReadableStream();
const asset = { stream: mock(() => stream) };
const construct = mock((_value: unknown) => {});
const init = mock(async (_options: any) => {});
const respond = mock(async (_request: Request, _options: any) => response);

class Server {
constructor(value: unknown) {
construct(value);
}
init = init;
respond = respond;
}

mock.module('SERVER', () => ({ Server }));
mock_manifest({ manifest, origin, env_prefix: envPrefix });
mock.module('SERVER', () => ({ server: new Server() }));
mock_manifest({ app_dir: '_app', origin, env_prefix: envPrefix });
mock_routes({ server_assets: new Map([['asset.txt', asset]]) });

const request_ip = mock((_request: Request): any => ({
Expand All @@ -227,9 +221,7 @@ async function load_handler({

return {
handler,
manifest,
stream,
construct,
init,
respond,
request_ip,
Expand Down
6 changes: 3 additions & 3 deletions packages/adapter-bun/test/mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,19 @@ import { mock } from 'bun:test';
// exports that src modules pull from these build-generated specifiers

export function mock_manifest({
manifest,
app_dir,
base,
embed,
origin,
env_prefix = ''
}: {
manifest?: unknown;
app_dir?: string;
base?: string;
embed?: boolean;
origin?: string;
env_prefix?: string;
} = {}) {
mock.module('MANIFEST', () => ({ manifest, base, embed, origin, env_prefix }));
mock.module('MANIFEST', () => ({ app_dir, base, embed, origin, env_prefix }));
}

export function mock_routes({
Expand Down
6 changes: 3 additions & 3 deletions packages/adapter-bun/test/routes.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ test('a root deployment registers routes with a leading slash', async () => {
});

test('immutable SvelteKit assets receive a long-lived cache policy', async () => {
const { routes } = await load_routes({ appDir: '_app' });
const { routes } = await load_routes({ app_dir: '_app' });

const request = new Request('http://localhost/');
const immutable = (
Expand Down Expand Up @@ -283,8 +283,8 @@ test('prerendered redirects retain their status and location', async () => {
expect((handler as any).GET.headers.get('location')).toBe('/new');
});

async function load_routes({ base = '/', embed = false, appDir = '_app' } = {}) {
mock_manifest({ manifest: { appDir }, base, embed });
async function load_routes({ base = '/', embed = false, app_dir = '_app' } = {}) {
mock_manifest({ app_dir, base, embed });
// the real Bun.file runs, with the spy recording resolved paths; the files it
// points at need not exist because nothing reads their contents
const file = spyOn(Bun, 'file');
Expand Down
21 changes: 13 additions & 8 deletions packages/adapter-cloudflare/files/internal.d.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
declare module 'SERVER' {
export { Server } from '@sveltejs/kit';
export const server: import('@sveltejs/kit').Server;
}

declare module 'MANIFEST' {
import { SSRManifest } from '@sveltejs/kit';

export const manifest: SSRManifest;
export const prerendered: Set<string>;
export const app_path: string;
export const base_path: string;
namespace Cloudflare {
interface Env {
ASSETS_BINDING: {
fetch: typeof fetch;
};
[key: string]: string | undefined;
}
}

declare const BASE_PATH: string;
declare const APP_PATH: string;
declare const PRERENDERED: Set<string>;
declare const MANIFEST_ASSETS: Set<string>;
30 changes: 11 additions & 19 deletions packages/adapter-cloudflare/files/worker.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import { Server } from 'SERVER';
import { manifest, prerendered, base_path } from 'MANIFEST';
import { server } from 'SERVER';
import { env } from 'cloudflare:workers';

const server = new Server(manifest);
const base_path = BASE_PATH;
const assets = MANIFEST_ASSETS;
const prerendered = PRERENDERED;

const app_path = `/${manifest.appPath}`;

const immutable = `${app_path}/immutable/`;
const version_file = `${app_path}/version.json`;
const immutable = `/${APP_PATH}/immutable/`;
const version_file = `/${APP_PATH}/version.json`;

/**
* We don't know the origin until we receive a request, but
Expand All @@ -17,11 +16,10 @@ const version_file = `${app_path}/version.json`;
let origin;

const initialized = server.init({
env: /** @type {Record<string, string>} */ (env),
env,
read: async (file) => {
const url = `${origin}/${file}`;
const response =
await /** @type {{ ASSETS: { fetch: typeof fetch } }} */ (env).ASSETS.fetch(url);
const response = await env.ASSETS_BINDING.fetch(url);

if (!response.ok) {
throw new Error(
Expand All @@ -33,13 +31,8 @@ const initialized = server.init({
}
});

/** @type {import('@cloudflare/workers-types').ExportedHandler<Cloudflare.Env>} */
export default {
/**
* @param {Request} req
* @param {{ ASSETS: { fetch: typeof fetch } }} env
* @param {ExecutionContext} ctx
* @returns {Promise<Response>}
*/
async fetch(req, env, ctx) {
if (!origin) {
origin = new URL(req.url).origin;
Expand All @@ -61,8 +54,7 @@ export default {
let is_static_asset = false;
const filename = stripped_pathname.slice(base_path.length + 1);
if (filename) {
is_static_asset =
manifest.assets.has(filename) || manifest.assets.has(filename + '/index.html');
is_static_asset = assets.has(filename) || assets.has(filename + '/index.html');
}

let location = pathname.at(-1) === '/' ? stripped_pathname : pathname + '/';
Expand All @@ -73,7 +65,7 @@ export default {
pathname === version_file ||
pathname.startsWith(immutable)
) {
let res = await env.ASSETS.fetch(req);
let res = await env.ASSETS_BINDING.fetch(req);
// `_headers` applies cache regardless of status, so we need to ensure an
// error response does not get cached
if (res.status >= 400) {
Expand Down
16 changes: 7 additions & 9 deletions packages/adapter-cloudflare/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -103,22 +103,20 @@ export default function (options = {}) {

// worker
const worker_dest_dir = path.dirname(worker_dest);
writeFileSync(
`${tmp}/manifest.js`,
`export const manifest = ${builder.generateManifest({ relativePath: path.posix.relative(tmp, builder.getServerDirectory()) })};\n\n` +
`export const prerendered = new Set(${JSON.stringify(builder.prerendered.paths)});\n\n` +
`export const base_path = ${JSON.stringify(builder.config.paths.base)};\n`
);
builder.generateServerInstance(`${tmp}/server.js`);
builder.copy(`${files}/worker.js`, worker_dest, {
replace: {
// the paths returned by the Wrangler config might be Windows paths,
// so we need to convert them to POSIX paths or else the backslashes
// will be interpreted as escape characters and create an incorrect import path.
// We also need to ensure the relative imports start with ./ since Wrangler
// errors if a relative import looks like a package import
SERVER: `./${posixify(path.relative(worker_dest_dir, builder.getServerDirectory()))}/index.js`,
MANIFEST: `./${posixify(path.relative(worker_dest_dir, tmp))}/manifest.js`,
ASSETS: assets_binding
SERVER: `./${posixify(path.relative(worker_dest_dir, tmp))}/server.js`,
BASE_PATH: JSON.stringify(builder.config.paths.base),
APP_PATH: JSON.stringify(builder.getAppPath()),
MANIFEST_ASSETS: `new Set(${JSON.stringify(builder.manifest.assets.map((a) => a.path))})`,
PRERENDERED: `new Set(${JSON.stringify(builder.prerendered.paths)})`,
ASSETS_BINDING: assets_binding
}
});
if (builder.hasServerInstrumentationFile()) {
Expand Down
5 changes: 1 addition & 4 deletions packages/adapter-netlify/files/edge.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
import { Server } from '0SERVER';
import { manifest } from 'MANIFEST';

const server = new Server(manifest);
import { server } from '0SERVER';

/**
* We don't know the origin until we receive a request, but
Expand Down
7 changes: 2 additions & 5 deletions packages/adapter-netlify/files/serverless.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
import { Server } from '0SERVER';
import { createReadableStream } from '@sveltejs/kit/node';
import process from 'node:process';

/**
* @param {import('@sveltejs/kit').SSRManifest} manifest
* @param {import('@sveltejs/kit').Server} server
* @returns {(request: Request, context: import('@netlify/types').Context) => Promise<Response>}
*/
export function init(manifest) {
const server = new Server(manifest);

export function init(server) {
/** @type {Promise<void> | null} */
let init_promise = server.init({
env: process.env,
Expand Down
Loading
Loading