From f53ca45858572f057e28c8e065a44ba383641429 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 16 Jan 2024 16:04:01 -0500 Subject: [PATCH 01/57] feat: implement readAsset --- packages/adapter-node/src/handler.js | 9 ++++-- packages/kit/scripts/generate-dts.js | 1 + packages/kit/src/core/sync/write_server.js | 3 +- packages/kit/src/exports/node/index.js | 22 +++++++++++++ packages/kit/src/exports/public.d.ts | 1 + packages/kit/src/exports/vite/dev/index.js | 7 +++-- packages/kit/src/exports/vite/index.js | 13 +++++++- packages/kit/src/exports/vite/module_ids.js | 5 ++- .../kit/src/exports/vite/preview/index.js | 5 +-- packages/kit/src/runtime/app/server/index.js | 31 +++++++++++++++++++ packages/kit/src/runtime/server/index.js | 10 ++++-- packages/kit/src/types/ambient-private.d.ts | 6 ++++ packages/kit/src/types/internal.d.ts | 1 + packages/kit/types/index.d.ts | 12 +++++++ 14 files changed, 115 insertions(+), 11 deletions(-) create mode 100644 packages/kit/src/runtime/app/server/index.js diff --git a/packages/adapter-node/src/handler.js b/packages/adapter-node/src/handler.js index 6adbe7c35fd8..1023925138aa 100644 --- a/packages/adapter-node/src/handler.js +++ b/packages/adapter-node/src/handler.js @@ -4,7 +4,7 @@ import path from 'node:path'; import sirv from 'sirv'; import { fileURLToPath } from 'node:url'; import { parse as polka_url_parser } from '@polka/url'; -import { getRequest, setResponse } from '@sveltejs/kit/node'; +import { getRequest, setResponse, readFile } from '@sveltejs/kit/node'; import { Server } from 'SERVER'; import { manifest, prerendered } from 'MANIFEST'; import { env } from 'ENV'; @@ -12,7 +12,12 @@ import { env } from 'ENV'; /* global ENV_PREFIX */ const server = new Server(manifest); -await server.init({ env: process.env }); + +await server.init({ + env: process.env, + readAsset: (file) => readFile(path.join(dir, 'client', file)) +}); + const origin = env('ORIGIN', undefined); const xff_depth = parseInt(env('XFF_DEPTH', '1')); const address_header = env('ADDRESS_HEADER', '').toLowerCase(); diff --git a/packages/kit/scripts/generate-dts.js b/packages/kit/scripts/generate-dts.js index 5d23c06d23f8..8d21d7881d7f 100644 --- a/packages/kit/scripts/generate-dts.js +++ b/packages/kit/scripts/generate-dts.js @@ -13,6 +13,7 @@ await createBundle({ '$app/forms': 'src/runtime/app/forms.js', '$app/navigation': 'src/runtime/app/navigation.js', '$app/paths': 'src/runtime/app/paths/types.d.ts', + '$app/server': 'src/runtime/app/server/index.js', '$app/stores': 'src/runtime/app/stores.js' }, include: ['src'] diff --git a/packages/kit/src/core/sync/write_server.js b/packages/kit/src/core/sync/write_server.js index a781711bbfe9..ebccac86cf49 100644 --- a/packages/kit/src/core/sync/write_server.js +++ b/packages/kit/src/core/sync/write_server.js @@ -30,6 +30,7 @@ const server_template = ({ import root from '../root.${isSvelte5Plus() ? 'js' : 'svelte'}'; import { set_building, set_prerendering } from '__sveltekit/environment'; import { set_assets } from '__sveltekit/paths'; +import { set_read_asset } from '__sveltekit/server'; import { set_private_env, set_public_env, set_safe_public_env } from '${runtime_directory}/shared-server.js'; export const options = { @@ -68,7 +69,7 @@ export async function get_hooks() { }; } -export { set_assets, set_building, set_prerendering, set_private_env, set_public_env, set_safe_public_env }; +export { set_assets, set_building, set_prerendering, set_private_env, set_public_env, set_read_asset, set_safe_public_env }; `; // TODO need to re-run this whenever src/app.html or src/error.html are diff --git a/packages/kit/src/exports/node/index.js b/packages/kit/src/exports/node/index.js index d15682838aaf..d85b50227907 100644 --- a/packages/kit/src/exports/node/index.js +++ b/packages/kit/src/exports/node/index.js @@ -1,3 +1,6 @@ +import fs from 'node:fs'; +import { Readable } from 'node:stream'; +import * as mime from 'mrmime'; import * as set_cookie_parser from 'set-cookie-parser'; import { SvelteKitError } from '../../runtime/control.js'; @@ -189,3 +192,22 @@ export async function setResponse(res, response) { } } } + +/** + * TODO + * @param {string} file + */ +export function readFile(file) { + const stats = fs.statSync(file); + const body = Readable.toWeb(fs.createReadStream(file)); + + // @ts-expect-error I think the types are wrong + const response = new Response(body, { + headers: { + 'Content-Length': stats.size, + 'Content-Type': mime.lookup(file) ?? 'application/octet-stream' + } + }); + + return response; +} diff --git a/packages/kit/src/exports/public.d.ts b/packages/kit/src/exports/public.d.ts index 183b85da657c..cc5c128244d7 100644 --- a/packages/kit/src/exports/public.d.ts +++ b/packages/kit/src/exports/public.d.ts @@ -1145,6 +1145,7 @@ export class Server { export interface ServerInitOptions { env: Record; + readAsset?: (file: string) => Response; } export interface SSRManifest { diff --git a/packages/kit/src/exports/vite/dev/index.js b/packages/kit/src/exports/vite/dev/index.js index 7ebf66444548..c4115180b422 100644 --- a/packages/kit/src/exports/vite/dev/index.js +++ b/packages/kit/src/exports/vite/dev/index.js @@ -4,7 +4,7 @@ import { URL } from 'node:url'; import colors from 'kleur'; import sirv from 'sirv'; import { isCSSRequest, loadEnv, buildErrorMessage } from 'vite'; -import { getRequest, setResponse } from '../../../exports/node/index.js'; +import { getRequest, readFile, setResponse } from '../../../exports/node/index.js'; import { installPolyfills } from '../../../exports/node/polyfills.js'; import { coalesce_to_error } from '../../../utils/error.js'; import { posixify, resolve_entry, to_fs } from '../../../utils/filesystem.js'; @@ -470,7 +470,10 @@ export async function dev(vite, vite_config, svelte_config) { const server = new Server(manifest); - await server.init({ env }); + await server.init({ + env, + readAsset: (file) => readFile(`.${file}`) + }); const request = await getRequest({ base, diff --git a/packages/kit/src/exports/vite/index.js b/packages/kit/src/exports/vite/index.js index 98e487c4fae7..5362695f341e 100644 --- a/packages/kit/src/exports/vite/index.js +++ b/packages/kit/src/exports/vite/index.js @@ -31,7 +31,8 @@ import { env_static_public, service_worker, sveltekit_environment, - sveltekit_paths + sveltekit_paths, + sveltekit_server } from './module_ids.js'; import { pathToFileURL } from 'node:url'; @@ -492,6 +493,16 @@ async function kit({ svelte_config }) { } `; } + + case sveltekit_server: { + return dedent` + export let read_asset = null; + + export function set_read_asset(fn) { + read_asset = fn; + } + `; + } } } }; diff --git a/packages/kit/src/exports/vite/module_ids.js b/packages/kit/src/exports/vite/module_ids.js index a6097f5b6f44..8944635d9cc7 100644 --- a/packages/kit/src/exports/vite/module_ids.js +++ b/packages/kit/src/exports/vite/module_ids.js @@ -2,6 +2,9 @@ export const env_static_private = '\0virtual:$env/static/private'; export const env_static_public = '\0virtual:$env/static/public'; export const env_dynamic_private = '\0virtual:$env/dynamic/private'; export const env_dynamic_public = '\0virtual:$env/dynamic/public'; + export const service_worker = '\0virtual:$service-worker'; -export const sveltekit_paths = '\0virtual:__sveltekit/paths'; + export const sveltekit_environment = '\0virtual:__sveltekit/environment'; +export const sveltekit_paths = '\0virtual:__sveltekit/paths'; +export const sveltekit_server = '\0virtual:__sveltekit/server'; diff --git a/packages/kit/src/exports/vite/preview/index.js b/packages/kit/src/exports/vite/preview/index.js index 997922bb5272..96486d219c86 100644 --- a/packages/kit/src/exports/vite/preview/index.js +++ b/packages/kit/src/exports/vite/preview/index.js @@ -4,7 +4,7 @@ import { pathToFileURL } from 'node:url'; import { lookup } from 'mrmime'; import sirv from 'sirv'; import { loadEnv, normalizePath } from 'vite'; -import { getRequest, setResponse } from '../../../exports/node/index.js'; +import { getRequest, readFile, setResponse } from '../../../exports/node/index.js'; import { installPolyfills } from '../../../exports/node/polyfills.js'; import { SVELTE_KIT_ASSETS } from '../../../constants.js'; import { not_found } from '../utils.js'; @@ -47,7 +47,8 @@ export async function preview(vite, vite_config, svelte_config) { const server = new Server(manifest); await server.init({ - env: loadEnv(vite_config.mode, svelte_config.kit.env.dir, '') + env: loadEnv(vite_config.mode, svelte_config.kit.env.dir, ''), + readAsset: (file) => readFile(dir + file) }); return () => { diff --git a/packages/kit/src/runtime/app/server/index.js b/packages/kit/src/runtime/app/server/index.js new file mode 100644 index 000000000000..c53c183fb413 --- /dev/null +++ b/packages/kit/src/runtime/app/server/index.js @@ -0,0 +1,31 @@ +import { read_asset } from '__sveltekit/server'; + +/** + * TODO docs + * @param {string} file + * @returns {Response} + */ +export function readAsset(file) { + if (!read_asset) { + throw new Error( + `No readAsset implementation was provided. Please ensure that your adapter is up to date and supports this feature` + ); + } + + // handle inline assets internally + if (file.startsWith('data:')) { + const [prelude, data] = file.split(';'); + const type = prelude.slice(5) || 'application/octet-stream'; + + const decoded = data.startsWith('base64,') ? atob(data.slice(7)) : decodeURIComponent(data); + + return new Response(decoded, { + headers: { + 'content-type': type + } + }); + } + + // for everything else, delegate to adapter + return read_asset(file); +} diff --git a/packages/kit/src/runtime/server/index.js b/packages/kit/src/runtime/server/index.js index 84257a8c5f36..5191880bfc97 100644 --- a/packages/kit/src/runtime/server/index.js +++ b/packages/kit/src/runtime/server/index.js @@ -4,6 +4,7 @@ import { options, get_hooks } from '__SERVER__/internal.js'; import { DEV } from 'esm-env'; import { filter_private_env, filter_public_env } from '../../utils/env.js'; import { prerendering } from '__sveltekit/environment'; +import { set_read_asset } from '__sveltekit/server'; /** @type {ProxyHandler<{ type: 'public' | 'private' }>} */ const prerender_env_handler = { @@ -30,10 +31,11 @@ export class Server { /** * @param {{ - * env: Record + * env: Record; + * readAsset?: (file: string) => Response; * }} opts */ - async init({ env }) { + async init({ env, readAsset }) { // Take care: Some adapters may have to call `Server.init` per-request to set env vars, // so anything that shouldn't be rerun should be wrapped in an `if` block to make sure it hasn't // been done already. @@ -55,6 +57,10 @@ export class Server { ); set_safe_public_env(public_env); + if (readAsset) { + set_read_asset(readAsset); + } + if (!this.#options.hooks) { try { const module = await get_hooks(); diff --git a/packages/kit/src/types/ambient-private.d.ts b/packages/kit/src/types/ambient-private.d.ts index 544c9a73b78a..223aafabec06 100644 --- a/packages/kit/src/types/ambient-private.d.ts +++ b/packages/kit/src/types/ambient-private.d.ts @@ -16,3 +16,9 @@ declare module '__sveltekit/paths' { export function override(paths: { base: string; assets: string }): void; export function set_assets(path: string): void; } + +/** Internal version of $app/server */ +declare module '__sveltekit/server' { + export function read_asset(path: string): Response; + export function set_read_asset(fn: (path: string) => Response): void; +} diff --git a/packages/kit/src/types/internal.d.ts b/packages/kit/src/types/internal.d.ts index 22f7209be380..9a0c1e138157 100644 --- a/packages/kit/src/types/internal.d.ts +++ b/packages/kit/src/types/internal.d.ts @@ -33,6 +33,7 @@ export interface ServerInternalModule { set_prerendering(): void; set_private_env(environment: Record): void; set_public_env(environment: Record): void; + set_read_asset(implementation: (path: string) => Response): void; set_safe_public_env(environment: Record): void; set_version(version: string): void; set_fix_stack_trace(fix_stack_trace: (error: unknown) => string): void; diff --git a/packages/kit/types/index.d.ts b/packages/kit/types/index.d.ts index acb9442b783d..65ef49a99598 100644 --- a/packages/kit/types/index.d.ts +++ b/packages/kit/types/index.d.ts @@ -1127,6 +1127,7 @@ declare module '@sveltejs/kit' { export interface ServerInitOptions { env: Record; + readAsset?: (file: string) => Response; } export interface SSRManifest { @@ -1873,6 +1874,10 @@ declare module '@sveltejs/kit/node' { }): Promise; export function setResponse(res: import('http').ServerResponse, response: Response): Promise; + /** + * TODO + * */ + export function readFile(file: string): Response; } declare module '@sveltejs/kit/node/polyfills' { @@ -2107,6 +2112,13 @@ declare module '$app/paths' { export function resolveRoute(id: string, params: Record): string; } +declare module '$app/server' { + /** + * TODO docs + * */ + export function readAsset(file: string): Response; +} + declare module '$app/stores' { export function getStores(): { From 716c59447cad6e12b5c480c1ee977426e5e3d51b Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 16 Jan 2024 16:15:26 -0500 Subject: [PATCH 02/57] lint etc --- packages/kit/src/exports/node/index.js | 2 +- packages/kit/src/runtime/app/server/index.js | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/kit/src/exports/node/index.js b/packages/kit/src/exports/node/index.js index d85b50227907..bfdde695f9a4 100644 --- a/packages/kit/src/exports/node/index.js +++ b/packages/kit/src/exports/node/index.js @@ -204,7 +204,7 @@ export function readFile(file) { // @ts-expect-error I think the types are wrong const response = new Response(body, { headers: { - 'Content-Length': stats.size, + 'Content-Length': stats.size.toString(), 'Content-Type': mime.lookup(file) ?? 'application/octet-stream' } }); diff --git a/packages/kit/src/runtime/app/server/index.js b/packages/kit/src/runtime/app/server/index.js index c53c183fb413..a0637ce710dc 100644 --- a/packages/kit/src/runtime/app/server/index.js +++ b/packages/kit/src/runtime/app/server/index.js @@ -8,7 +8,7 @@ import { read_asset } from '__sveltekit/server'; export function readAsset(file) { if (!read_asset) { throw new Error( - `No readAsset implementation was provided. Please ensure that your adapter is up to date and supports this feature` + 'No readAsset implementation was provided. Please ensure that your adapter is up to date and supports this feature' ); } @@ -21,7 +21,8 @@ export function readAsset(file) { return new Response(decoded, { headers: { - 'content-type': type + 'Content-Type': type, + 'Content-Length': decoded.length.toString() } }); } From a294256c582305b38386b43e00248e74ed275fe4 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 16 Jan 2024 16:44:43 -0500 Subject: [PATCH 03/57] maybe make it work on netlify? no idea how to test, manual deploys dont appear to respect .netlify directory --- packages/adapter-netlify/src/serverless.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/adapter-netlify/src/serverless.js b/packages/adapter-netlify/src/serverless.js index db394a888302..4ba0d48ced00 100644 --- a/packages/adapter-netlify/src/serverless.js +++ b/packages/adapter-netlify/src/serverless.js @@ -1,6 +1,7 @@ import './shims'; import { Server } from '0SERVER'; import { split_headers } from './headers.js'; +import { readFile } from '@sveltejs/kit/node'; /** * @param {import('@sveltejs/kit').SSRManifest} manifest @@ -10,7 +11,8 @@ export function init(manifest) { const server = new Server(manifest); let init_promise = server.init({ - env: process.env + env: process.env, + readAsset: (file) => readFile('.netlify/server' + file) }); return async (event, context) => { From 15880deacecfd24739dd2d184e4bbd57c22be116 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 16 Jan 2024 18:17:22 -0500 Subject: [PATCH 04/57] set length/type from manifest --- packages/adapter-netlify/src/serverless.js | 4 ++-- packages/adapter-node/src/handler.js | 5 +++-- .../kit/src/core/generate_manifest/index.js | 20 +++++++++++++++-- packages/kit/src/exports/node/index.js | 22 ------------------- packages/kit/src/exports/public.d.ts | 3 ++- packages/kit/src/exports/vite/dev/index.js | 16 ++++++++++++-- packages/kit/src/exports/vite/index.js | 7 ++++++ .../kit/src/exports/vite/preview/index.js | 6 +++-- packages/kit/src/runtime/app/server/index.js | 20 ++++++++++++----- packages/kit/src/runtime/server/index.js | 6 +++-- packages/kit/src/types/ambient-private.d.ts | 8 +++++-- packages/kit/src/types/internal.d.ts | 3 ++- 12 files changed, 77 insertions(+), 43 deletions(-) diff --git a/packages/adapter-netlify/src/serverless.js b/packages/adapter-netlify/src/serverless.js index 4ba0d48ced00..11a25b5956e9 100644 --- a/packages/adapter-netlify/src/serverless.js +++ b/packages/adapter-netlify/src/serverless.js @@ -1,7 +1,7 @@ import './shims'; import { Server } from '0SERVER'; import { split_headers } from './headers.js'; -import { readFile } from '@sveltejs/kit/node'; +import { Readable } from 'node:stream'; /** * @param {import('@sveltejs/kit').SSRManifest} manifest @@ -12,7 +12,7 @@ export function init(manifest) { let init_promise = server.init({ env: process.env, - readAsset: (file) => readFile('.netlify/server' + file) + readAsset: (file) => Readable.toWeb(fs.createReadStream('.netlify/server' + file)) }); return async (event, context) => { diff --git a/packages/adapter-node/src/handler.js b/packages/adapter-node/src/handler.js index 1023925138aa..7d2c09263164 100644 --- a/packages/adapter-node/src/handler.js +++ b/packages/adapter-node/src/handler.js @@ -3,8 +3,9 @@ import fs from 'node:fs'; import path from 'node:path'; import sirv from 'sirv'; import { fileURLToPath } from 'node:url'; +import { Readable } from 'node:stream'; import { parse as polka_url_parser } from '@polka/url'; -import { getRequest, setResponse, readFile } from '@sveltejs/kit/node'; +import { getRequest, setResponse } from '@sveltejs/kit/node'; import { Server } from 'SERVER'; import { manifest, prerendered } from 'MANIFEST'; import { env } from 'ENV'; @@ -15,7 +16,7 @@ const server = new Server(manifest); await server.init({ env: process.env, - readAsset: (file) => readFile(path.join(dir, 'client', file)) + readAsset: (file) => Readable.toWeb(fs.createReadStream(path.join(dir, 'client', file))) }); const origin = env('ORIGIN', undefined); diff --git a/packages/kit/src/core/generate_manifest/index.js b/packages/kit/src/core/generate_manifest/index.js index 272d79e55d29..e724e131ea09 100644 --- a/packages/kit/src/core/generate_manifest/index.js +++ b/packages/kit/src/core/generate_manifest/index.js @@ -1,3 +1,6 @@ +import fs from 'fs'; +import path from 'path'; +import * as mime from 'mrmime'; import { s } from '../../utils/misc.js'; import { get_mime_lookup } from '../utils.js'; import { resolve_symlinks } from '../../exports/vite/build/utils.js'; @@ -63,6 +66,18 @@ export function generate_manifest({ build_data, relative_path, routes }) { return `[${string},]`; } + // TODO only include assets that are referenced by the current routes + /** @type {Record} */ + const files = {}; + for (const chunk of Object.values(build_data.server_manifest)) { + if (chunk.assets) { + for (const asset of chunk.assets) { + const stats = fs.statSync(path.join(build_data.out_dir, 'server', asset)); + files['/' + asset] = [stats.size, mime.lookup(asset) || '']; + } + } + } + // prettier-ignore // String representation of /** @template {import('@sveltejs/kit').SSRManifest} T */ @@ -74,13 +89,14 @@ export function generate_manifest({ build_data, relative_path, routes }) { mimeTypes: ${s(get_mime_lookup(build_data.manifest_data))}, _: { client: ${s(build_data.client)}, + files: ${s(files)}, nodes: [ ${(node_paths).map(loader).join(',\n')} ], routes: [ ${routes.map(route => { if (!route.page && !route.endpoint) return; - + route.params.forEach(param => { if (param.matcher) matchers.add(param.matcher); }); @@ -98,7 +114,7 @@ export function generate_manifest({ build_data, relative_path, routes }) { ], matchers: async () => { ${Array.from( - matchers, + matchers, type => `const { match: ${type} } = await import ('${(join_relative(relative_path, `/entries/matchers/${type}.js`))}')` ).join('\n')} return { ${Array.from(matchers).join(', ')} }; diff --git a/packages/kit/src/exports/node/index.js b/packages/kit/src/exports/node/index.js index bfdde695f9a4..d15682838aaf 100644 --- a/packages/kit/src/exports/node/index.js +++ b/packages/kit/src/exports/node/index.js @@ -1,6 +1,3 @@ -import fs from 'node:fs'; -import { Readable } from 'node:stream'; -import * as mime from 'mrmime'; import * as set_cookie_parser from 'set-cookie-parser'; import { SvelteKitError } from '../../runtime/control.js'; @@ -192,22 +189,3 @@ export async function setResponse(res, response) { } } } - -/** - * TODO - * @param {string} file - */ -export function readFile(file) { - const stats = fs.statSync(file); - const body = Readable.toWeb(fs.createReadStream(file)); - - // @ts-expect-error I think the types are wrong - const response = new Response(body, { - headers: { - 'Content-Length': stats.size.toString(), - 'Content-Type': mime.lookup(file) ?? 'application/octet-stream' - } - }); - - return response; -} diff --git a/packages/kit/src/exports/public.d.ts b/packages/kit/src/exports/public.d.ts index cc5c128244d7..e9d661b343dc 100644 --- a/packages/kit/src/exports/public.d.ts +++ b/packages/kit/src/exports/public.d.ts @@ -1145,7 +1145,7 @@ export class Server { export interface ServerInitOptions { env: Record; - readAsset?: (file: string) => Response; + readAsset?: (file: string) => ReadableStream; } export interface SSRManifest { @@ -1157,6 +1157,7 @@ export interface SSRManifest { /** private fields */ _: { client: NonNullable; + files: Record; nodes: SSRNodeLoader[]; routes: SSRRoute[]; matchers(): Promise>; diff --git a/packages/kit/src/exports/vite/dev/index.js b/packages/kit/src/exports/vite/dev/index.js index c4115180b422..7da88a84d439 100644 --- a/packages/kit/src/exports/vite/dev/index.js +++ b/packages/kit/src/exports/vite/dev/index.js @@ -1,10 +1,12 @@ import fs from 'node:fs'; import path from 'node:path'; import { URL } from 'node:url'; +import { Readable } from 'node:stream'; import colors from 'kleur'; import sirv from 'sirv'; +import * as mime from 'mrmime'; import { isCSSRequest, loadEnv, buildErrorMessage } from 'vite'; -import { getRequest, readFile, setResponse } from '../../../exports/node/index.js'; +import { getRequest, setResponse } from '../../../exports/node/index.js'; import { installPolyfills } from '../../../exports/node/polyfills.js'; import { coalesce_to_error } from '../../../utils/error.js'; import { posixify, resolve_entry, to_fs } from '../../../utils/filesystem.js'; @@ -123,6 +125,15 @@ export async function dev(vite, vite_config, svelte_config) { fonts: [], uses_env_dynamic_public: true }, + files: new Proxy( + {}, + { + get: (_, /** @type {string} */ file) => { + const stats = fs.statSync('.' + file); + return [stats.size, mime.lookup(file)]; + } + } + ), nodes: manifest_data.nodes.map((node, index) => { return async () => { /** @type {import('types').SSRNode} */ @@ -472,7 +483,8 @@ export async function dev(vite, vite_config, svelte_config) { await server.init({ env, - readAsset: (file) => readFile(`.${file}`) + // @ts-expect-error the types are wrong + readAsset: (file) => Readable.toWeb(fs.createReadStream('.' + file)) }); const request = await getRequest({ diff --git a/packages/kit/src/exports/vite/index.js b/packages/kit/src/exports/vite/index.js index 5362695f341e..8652397b5deb 100644 --- a/packages/kit/src/exports/vite/index.js +++ b/packages/kit/src/exports/vite/index.js @@ -498,9 +498,15 @@ async function kit({ svelte_config }) { return dedent` export let read_asset = null; + export let manifest = null; + export function set_read_asset(fn) { read_asset = fn; } + + export function set_manifest(_) { + manifest = _; + } `; } } @@ -727,6 +733,7 @@ async function kit({ svelte_config }) { app_dir: kit.appDir, app_path: `${kit.paths.base.slice(1)}${kit.paths.base ? '/' : ''}${kit.appDir}`, manifest_data, + out_dir: out, service_worker: service_worker_entry_file ? 'service-worker.js' : null, // TODO make file configurable? client: null, server_manifest diff --git a/packages/kit/src/exports/vite/preview/index.js b/packages/kit/src/exports/vite/preview/index.js index 96486d219c86..f60f85cbf1e0 100644 --- a/packages/kit/src/exports/vite/preview/index.js +++ b/packages/kit/src/exports/vite/preview/index.js @@ -1,10 +1,11 @@ import fs from 'node:fs'; import { join } from 'node:path'; import { pathToFileURL } from 'node:url'; +import { Readable } from 'node:stream'; import { lookup } from 'mrmime'; import sirv from 'sirv'; import { loadEnv, normalizePath } from 'vite'; -import { getRequest, readFile, setResponse } from '../../../exports/node/index.js'; +import { getRequest, setResponse } from '../../../exports/node/index.js'; import { installPolyfills } from '../../../exports/node/polyfills.js'; import { SVELTE_KIT_ASSETS } from '../../../constants.js'; import { not_found } from '../utils.js'; @@ -48,7 +49,8 @@ export async function preview(vite, vite_config, svelte_config) { const server = new Server(manifest); await server.init({ env: loadEnv(vite_config.mode, svelte_config.kit.env.dir, ''), - readAsset: (file) => readFile(dir + file) + // @ts-expect-error the types are wrong + readAsset: (file) => Readable.toWeb(fs.createReadStream(dir + file)) }); return () => { diff --git a/packages/kit/src/runtime/app/server/index.js b/packages/kit/src/runtime/app/server/index.js index a0637ce710dc..364fb246482e 100644 --- a/packages/kit/src/runtime/app/server/index.js +++ b/packages/kit/src/runtime/app/server/index.js @@ -1,4 +1,4 @@ -import { read_asset } from '__sveltekit/server'; +import { read_asset, manifest } from '__sveltekit/server'; /** * TODO docs @@ -21,12 +21,22 @@ export function readAsset(file) { return new Response(decoded, { headers: { - 'Content-Type': type, - 'Content-Length': decoded.length.toString() + 'Content-Length': decoded.length.toString(), + 'Content-Type': type } }); } - // for everything else, delegate to adapter - return read_asset(file); + let info = manifest._.files[file]; + + if (!info) { + throw new Error(`Asset does not exist in deployment: ${file}`); + } + + return new Response(read_asset(file), { + headers: { + 'Content-Length': '' + info[0], + 'Content-Type': info[1] + } + }); } diff --git a/packages/kit/src/runtime/server/index.js b/packages/kit/src/runtime/server/index.js index 5191880bfc97..4b83a6cbe2dd 100644 --- a/packages/kit/src/runtime/server/index.js +++ b/packages/kit/src/runtime/server/index.js @@ -4,7 +4,7 @@ import { options, get_hooks } from '__SERVER__/internal.js'; import { DEV } from 'esm-env'; import { filter_private_env, filter_public_env } from '../../utils/env.js'; import { prerendering } from '__sveltekit/environment'; -import { set_read_asset } from '__sveltekit/server'; +import { set_read_asset, set_manifest } from '__sveltekit/server'; /** @type {ProxyHandler<{ type: 'public' | 'private' }>} */ const prerender_env_handler = { @@ -27,12 +27,14 @@ export class Server { /** @type {import('types').SSROptions} */ this.#options = options; this.#manifest = manifest; + + set_manifest(manifest); } /** * @param {{ * env: Record; - * readAsset?: (file: string) => Response; + * readAsset?: (file: string) => ReadableStream; * }} opts */ async init({ env, readAsset }) { diff --git a/packages/kit/src/types/ambient-private.d.ts b/packages/kit/src/types/ambient-private.d.ts index 223aafabec06..7b14645ab538 100644 --- a/packages/kit/src/types/ambient-private.d.ts +++ b/packages/kit/src/types/ambient-private.d.ts @@ -19,6 +19,10 @@ declare module '__sveltekit/paths' { /** Internal version of $app/server */ declare module '__sveltekit/server' { - export function read_asset(path: string): Response; - export function set_read_asset(fn: (path: string) => Response): void; + import { SSRManifest } from '@sveltejs/kit'; + + export let manifest: SSRManifest; + export function read_asset(path: string): ReadableStream; + export function set_manifest(manifest: SSRManifest): void; + export function set_read_asset(fn: (path: string) => ReadableStream): void; } diff --git a/packages/kit/src/types/internal.d.ts b/packages/kit/src/types/internal.d.ts index 9a0c1e138157..4db8a8413b51 100644 --- a/packages/kit/src/types/internal.d.ts +++ b/packages/kit/src/types/internal.d.ts @@ -33,7 +33,7 @@ export interface ServerInternalModule { set_prerendering(): void; set_private_env(environment: Record): void; set_public_env(environment: Record): void; - set_read_asset(implementation: (path: string) => Response): void; + set_read_asset(implementation: (path: string) => ReadableStream): void; set_safe_public_env(environment: Record): void; set_version(version: string): void; set_fix_stack_trace(fix_stack_trace: (error: unknown) => string): void; @@ -56,6 +56,7 @@ export interface BuildData { app_dir: string; app_path: string; manifest_data: ManifestData; + out_dir: string; service_worker: string | null; client: { start: string; From 5163e36c36a3d73eddb30921c7c932f619a03099 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 16 Jan 2024 18:23:35 -0500 Subject: [PATCH 05/57] tidy up --- .../kit/src/core/generate_manifest/index.js | 4 ++-- packages/kit/src/exports/vite/dev/index.js | 3 +++ packages/kit/src/runtime/app/server/index.js | 18 +++++++++--------- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/packages/kit/src/core/generate_manifest/index.js b/packages/kit/src/core/generate_manifest/index.js index e724e131ea09..2adfb6d34218 100644 --- a/packages/kit/src/core/generate_manifest/index.js +++ b/packages/kit/src/core/generate_manifest/index.js @@ -1,5 +1,5 @@ -import fs from 'fs'; -import path from 'path'; +import fs from 'node:fs'; +import path from 'node:path'; import * as mime from 'mrmime'; import { s } from '../../utils/misc.js'; import { get_mime_lookup } from '../utils.js'; diff --git a/packages/kit/src/exports/vite/dev/index.js b/packages/kit/src/exports/vite/dev/index.js index 7da88a84d439..3cf04d28b991 100644 --- a/packages/kit/src/exports/vite/dev/index.js +++ b/packages/kit/src/exports/vite/dev/index.js @@ -131,6 +131,9 @@ export async function dev(vite, vite_config, svelte_config) { get: (_, /** @type {string} */ file) => { const stats = fs.statSync('.' + file); return [stats.size, mime.lookup(file)]; + }, + has: (_, /** @type {string} */ file) => { + return fs.existsSync('.' + file); } } ), diff --git a/packages/kit/src/runtime/app/server/index.js b/packages/kit/src/runtime/app/server/index.js index 364fb246482e..4e8d1420006a 100644 --- a/packages/kit/src/runtime/app/server/index.js +++ b/packages/kit/src/runtime/app/server/index.js @@ -27,16 +27,16 @@ export function readAsset(file) { }); } - let info = manifest._.files[file]; + if (file in manifest._.files) { + const [length, type] = manifest._.files[file]; - if (!info) { - throw new Error(`Asset does not exist in deployment: ${file}`); + return new Response(read_asset(file), { + headers: { + 'Content-Length': '' + length, + 'Content-Type': type + } + }); } - return new Response(read_asset(file), { - headers: { - 'Content-Length': '' + info[0], - 'Content-Type': info[1] - } - }); + throw new Error(`Asset does not exist: ${file}`); } From 289155ad1da653824f43d21aa4b0e4ba4a365669 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 16 Jan 2024 18:44:30 -0500 Subject: [PATCH 06/57] regenerate types --- packages/kit/types/index.d.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/packages/kit/types/index.d.ts b/packages/kit/types/index.d.ts index 65ef49a99598..c9d6e16c9139 100644 --- a/packages/kit/types/index.d.ts +++ b/packages/kit/types/index.d.ts @@ -1127,7 +1127,7 @@ declare module '@sveltejs/kit' { export interface ServerInitOptions { env: Record; - readAsset?: (file: string) => Response; + readAsset?: (file: string) => ReadableStream; } export interface SSRManifest { @@ -1139,6 +1139,7 @@ declare module '@sveltejs/kit' { /** private fields */ _: { client: NonNullable; + files: Record; nodes: SSRNodeLoader[]; routes: SSRRoute[]; matchers(): Promise>; @@ -1550,6 +1551,7 @@ declare module '@sveltejs/kit' { app_dir: string; app_path: string; manifest_data: ManifestData; + out_dir: string; service_worker: string | null; client: { start: string; @@ -1874,10 +1876,6 @@ declare module '@sveltejs/kit/node' { }): Promise; export function setResponse(res: import('http').ServerResponse, response: Response): Promise; - /** - * TODO - * */ - export function readFile(file: string): Response; } declare module '@sveltejs/kit/node/polyfills' { From da88785ec32e2aa53bab833dd105f311c0478725 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 16 Jan 2024 18:47:59 -0500 Subject: [PATCH 07/57] lint --- packages/adapter-netlify/src/serverless.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/adapter-netlify/src/serverless.js b/packages/adapter-netlify/src/serverless.js index 11a25b5956e9..0b5876485bb8 100644 --- a/packages/adapter-netlify/src/serverless.js +++ b/packages/adapter-netlify/src/serverless.js @@ -12,6 +12,7 @@ export function init(manifest) { let init_promise = server.init({ env: process.env, + // @ts-expect-error the types are wrong readAsset: (file) => Readable.toWeb(fs.createReadStream('.netlify/server' + file)) }); From caf010fbb0e97e25b89230d75cba3c56aee0abbf Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 16 Jan 2024 20:08:50 -0500 Subject: [PATCH 08/57] missed a spot --- packages/adapter-node/src/handler.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/adapter-node/src/handler.js b/packages/adapter-node/src/handler.js index 7d2c09263164..0e874b5c2877 100644 --- a/packages/adapter-node/src/handler.js +++ b/packages/adapter-node/src/handler.js @@ -16,6 +16,7 @@ const server = new Server(manifest); await server.init({ env: process.env, + // @ts-expect-error the types are wrong readAsset: (file) => Readable.toWeb(fs.createReadStream(path.join(dir, 'client', file))) }); From 4eba560595c049e6b7871ad6d5c737e76fcf20d0 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 16 Jan 2024 21:06:34 -0500 Subject: [PATCH 09/57] more efficient manifest generation --- packages/kit/src/core/adapt/builder.js | 1 + .../kit/src/core/generate_manifest/index.js | 42 ++++++++++++++----- packages/kit/src/exports/vite/build/utils.js | 12 +++--- packages/kit/src/types/internal.d.ts | 1 + 4 files changed, 40 insertions(+), 16 deletions(-) diff --git a/packages/kit/src/core/adapt/builder.js b/packages/kit/src/core/adapt/builder.js index 2c55f5027485..a79fde400873 100644 --- a/packages/kit/src/core/adapt/builder.js +++ b/packages/kit/src/core/adapt/builder.js @@ -95,6 +95,7 @@ export function create_builder({ ); }, + // TODO this appears to be unused and should probably be deprecated (and removed in 3.0) async createEntries(fn) { const seen = new Set(); diff --git a/packages/kit/src/core/generate_manifest/index.js b/packages/kit/src/core/generate_manifest/index.js index 2adfb6d34218..5fb15d48dd43 100644 --- a/packages/kit/src/core/generate_manifest/index.js +++ b/packages/kit/src/core/generate_manifest/index.js @@ -3,7 +3,7 @@ import path from 'node:path'; import * as mime from 'mrmime'; import { s } from '../../utils/misc.js'; import { get_mime_lookup } from '../utils.js'; -import { resolve_symlinks } from '../../exports/vite/build/utils.js'; +import { find_deps, resolve_symlinks } from '../../exports/vite/build/utils.js'; import { compact } from '../../utils/array.js'; import { join_relative } from '../../utils/filesystem.js'; import { dedent } from '../sync/utils.js'; @@ -29,12 +29,33 @@ export function generate_manifest({ build_data, relative_path, routes }) { */ const used_nodes = new Set([0, 1]); + // TODO add hooks.server.js asset imports + /** @type {Set} */ + const server_assets = new Set(); + + /** @param {string} id */ + function add_assets(id) { + const deps = find_deps(build_data.server_manifest, id, false); + for (const asset of deps.assets) { + server_assets.add(asset); + } + } + for (const route of routes) { if (route.page) { for (const i of route.page.layouts) used_nodes.add(i); for (const i of route.page.errors) used_nodes.add(i); used_nodes.add(route.page.leaf); } + + if (route.endpoint) { + add_assets(route.endpoint.file); + } + } + + for (const n of used_nodes) { + const node = build_data.manifest_data.nodes[n]; + if (node.server) add_assets(node.server); } const node_paths = compact( @@ -66,16 +87,15 @@ export function generate_manifest({ build_data, relative_path, routes }) { return `[${string},]`; } - // TODO only include assets that are referenced by the current routes - /** @type {Record} */ + const mime_types = get_mime_lookup(build_data.manifest_data); + + /** @type {Record} */ const files = {}; - for (const chunk of Object.values(build_data.server_manifest)) { - if (chunk.assets) { - for (const asset of chunk.assets) { - const stats = fs.statSync(path.join(build_data.out_dir, 'server', asset)); - files['/' + asset] = [stats.size, mime.lookup(asset) || '']; - } - } + for (const asset of server_assets) { + files[asset] = fs.statSync(path.resolve(build_data.out_dir, 'server', asset)).size; + + const ext = path.extname(asset); + mime_types[ext] ??= mime.lookup(ext) || ''; } // prettier-ignore @@ -86,7 +106,7 @@ export function generate_manifest({ build_data, relative_path, routes }) { appDir: ${s(build_data.app_dir)}, appPath: ${s(build_data.app_path)}, assets: new Set(${s(assets)}), - mimeTypes: ${s(get_mime_lookup(build_data.manifest_data))}, + mimeTypes: ${s(mime_types)}, _: { client: ${s(build_data.client)}, files: ${s(files)}, diff --git a/packages/kit/src/exports/vite/build/utils.js b/packages/kit/src/exports/vite/build/utils.js index 7c12c1e2139d..f265747687a9 100644 --- a/packages/kit/src/exports/vite/build/utils.js +++ b/packages/kit/src/exports/vite/build/utils.js @@ -20,7 +20,7 @@ export function find_deps(manifest, entry, add_dynamic_css) { const stylesheets = new Set(); /** @type {Set} */ - const fonts = new Set(); + const imported_assets = new Set(); /** * @param {string} current @@ -36,9 +36,7 @@ export function find_deps(manifest, entry, add_dynamic_css) { if (chunk.assets) { for (const asset of chunk.assets) { - if (/\.(woff2?|ttf|otf)$/.test(asset)) { - fonts.add(asset); - } + imported_assets.add(asset); } } @@ -59,11 +57,15 @@ export function find_deps(manifest, entry, add_dynamic_css) { traverse(file, true); + const assets = Array.from(imported_assets); + return { + assets, file: chunk.file, imports: Array.from(imports), stylesheets: Array.from(stylesheets), - fonts: Array.from(fonts) + // TODO do we need this separately? + fonts: assets.filter((asset) => /\.(woff2?|ttf|otf)$/.test(asset)) }; } diff --git a/packages/kit/src/types/internal.d.ts b/packages/kit/src/types/internal.d.ts index 4db8a8413b51..cc05af7200d5 100644 --- a/packages/kit/src/types/internal.d.ts +++ b/packages/kit/src/types/internal.d.ts @@ -46,6 +46,7 @@ export interface Asset { } export interface AssetDependencies { + assets: string[]; file: string; imports: string[]; stylesheets: string[]; From 6045dfc80985263f76629ac0e0860deb2bb6d36d Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 16 Jan 2024 23:55:24 -0500 Subject: [PATCH 10/57] working on vercel --- packages/adapter-vercel/files/serverless.js | 7 ++- packages/adapter-vercel/index.js | 11 ++--- packages/kit/src/core/adapt/builder.js | 15 ++++++ .../generate_manifest/find_server_assets.js | 47 +++++++++++++++++++ .../kit/src/core/generate_manifest/index.js | 24 ++-------- packages/kit/src/exports/public.d.ts | 7 +++ packages/kit/src/runtime/app/server/index.js | 3 +- packages/kit/types/index.d.ts | 7 +++ 8 files changed, 92 insertions(+), 29 deletions(-) create mode 100644 packages/kit/src/core/generate_manifest/find_server_assets.js diff --git a/packages/adapter-vercel/files/serverless.js b/packages/adapter-vercel/files/serverless.js index d732371568c8..230d43a1a4ef 100644 --- a/packages/adapter-vercel/files/serverless.js +++ b/packages/adapter-vercel/files/serverless.js @@ -1,3 +1,6 @@ +import fs from 'node:fs'; +import path from 'node:path'; +import { Readable } from 'node:stream'; import { installPolyfills } from '@sveltejs/kit/node/polyfills'; import { getRequest, setResponse } from '@sveltejs/kit/node'; import { Server } from 'SERVER'; @@ -8,7 +11,9 @@ installPolyfills(); const server = new Server(manifest); await server.init({ - env: /** @type {Record} */ (process.env) + env: /** @type {Record} */ (process.env), + // @ts-expect-error the types are wrong + readAsset: (file) => Readable.toWeb(fs.createReadStream('.' + file)) }); const DATA_SUFFIX = '/__data.json'; diff --git a/packages/adapter-vercel/index.js b/packages/adapter-vercel/index.js index 6a38d4e77e38..d7a96c5e3942 100644 --- a/packages/adapter-vercel/index.js +++ b/packages/adapter-vercel/index.js @@ -63,6 +63,8 @@ const plugin = function (defaults = {}) { * @param {import('@sveltejs/kit').RouteDefinition[]} routes */ async function generate_serverless_function(name, config, routes) { + const dir = `${dirs.functions}/${name}.func`; + const relativePath = path.posix.relative(tmp, builder.getServerDirectory()); builder.copy(`${files}/serverless.js`, `${tmp}/index.js`, { @@ -77,12 +79,9 @@ const plugin = function (defaults = {}) { `export const manifest = ${builder.generateManifest({ relativePath, routes })};\n` ); - await create_function_bundle( - builder, - `${tmp}/index.js`, - `${dirs.functions}/${name}.func`, - config - ); + await create_function_bundle(builder, `${tmp}/index.js`, dir, config); + + builder.writeServerAssets(dir, routes); } /** diff --git a/packages/kit/src/core/adapt/builder.js b/packages/kit/src/core/adapt/builder.js index a79fde400873..d072433ede6d 100644 --- a/packages/kit/src/core/adapt/builder.js +++ b/packages/kit/src/core/adapt/builder.js @@ -10,6 +10,7 @@ import { get_env } from '../../exports/vite/utils.js'; import generate_fallback from '../postbuild/fallback.js'; import { write } from '../sync/utils.js'; import { list_files } from '../utils.js'; +import { find_server_assets } from '../generate_manifest/find_server_assets.js'; const pipe = promisify(pipeline); const extensions = ['.html', '.js', '.mjs', '.json', '.css', '.svg', '.xml', '.wasm']; @@ -204,6 +205,20 @@ export function create_builder({ writeServer(dest) { return copy(`${config.kit.outDir}/output/server`, dest); + }, + + writeServerAssets(dest, subset) { + const routes = subset + ? subset.map((route) => /** @type {import('types').RouteData} */ (lookup.get(route))) + : route_data.filter((route) => prerender_map.get(route.id) !== true); + + const server_assets = find_server_assets(build_data, routes); + + for (const file of server_assets) { + copy(`${config.kit.outDir}/output/server/${file}`, `${dest}/${file}`); + } + + return server_assets; } }; } diff --git a/packages/kit/src/core/generate_manifest/find_server_assets.js b/packages/kit/src/core/generate_manifest/find_server_assets.js new file mode 100644 index 000000000000..08c9f8a33c6a --- /dev/null +++ b/packages/kit/src/core/generate_manifest/find_server_assets.js @@ -0,0 +1,47 @@ +import { find_deps } from '../../exports/vite/build/utils.js'; + +/** + * Generates the data used to write the server-side manifest.js file. This data is used in the Vite + * build process, to power routing, etc. + * @param {import('types').BuildData} build_data + * @param {import('types').RouteData[]} routes + */ +export function find_server_assets(build_data, routes) { + /** + * All nodes actually used in the routes definition (prerendered routes are omitted). + * Root layout/error is always included as they are needed for 404 and root errors. + * @type {Set} + */ + const used_nodes = new Set([0, 1]); + + // TODO add hooks.server.js asset imports + /** @type {Set} */ + const server_assets = new Set(); + + /** @param {string} id */ + function add_assets(id) { + const deps = find_deps(build_data.server_manifest, id, false); + for (const asset of deps.assets) { + server_assets.add(asset); + } + } + + for (const route of routes) { + if (route.page) { + for (const i of route.page.layouts) used_nodes.add(i); + for (const i of route.page.errors) used_nodes.add(i); + used_nodes.add(route.page.leaf); + } + + if (route.endpoint) { + add_assets(route.endpoint.file); + } + } + + for (const n of used_nodes) { + const node = build_data.manifest_data.nodes[n]; + if (node.server) add_assets(node.server); + } + + return Array.from(server_assets); +} diff --git a/packages/kit/src/core/generate_manifest/index.js b/packages/kit/src/core/generate_manifest/index.js index 5fb15d48dd43..cef280b13b59 100644 --- a/packages/kit/src/core/generate_manifest/index.js +++ b/packages/kit/src/core/generate_manifest/index.js @@ -7,6 +7,7 @@ import { find_deps, resolve_symlinks } from '../../exports/vite/build/utils.js'; import { compact } from '../../utils/array.js'; import { join_relative } from '../../utils/filesystem.js'; import { dedent } from '../sync/utils.js'; +import { find_server_assets } from './find_server_assets.js'; /** * Generates the data used to write the server-side manifest.js file. This data is used in the Vite @@ -29,17 +30,7 @@ export function generate_manifest({ build_data, relative_path, routes }) { */ const used_nodes = new Set([0, 1]); - // TODO add hooks.server.js asset imports - /** @type {Set} */ - const server_assets = new Set(); - - /** @param {string} id */ - function add_assets(id) { - const deps = find_deps(build_data.server_manifest, id, false); - for (const asset of deps.assets) { - server_assets.add(asset); - } - } + const server_assets = find_server_assets(build_data, routes); for (const route of routes) { if (route.page) { @@ -47,15 +38,6 @@ export function generate_manifest({ build_data, relative_path, routes }) { for (const i of route.page.errors) used_nodes.add(i); used_nodes.add(route.page.leaf); } - - if (route.endpoint) { - add_assets(route.endpoint.file); - } - } - - for (const n of used_nodes) { - const node = build_data.manifest_data.nodes[n]; - if (node.server) add_assets(node.server); } const node_paths = compact( @@ -92,7 +74,7 @@ export function generate_manifest({ build_data, relative_path, routes }) { /** @type {Record} */ const files = {}; for (const asset of server_assets) { - files[asset] = fs.statSync(path.resolve(build_data.out_dir, 'server', asset)).size; + files['/' + asset] = fs.statSync(path.resolve(build_data.out_dir, 'server', asset)).size; const ext = path.extname(asset); mime_types[ext] ??= mime.lookup(ext) || ''; diff --git a/packages/kit/src/exports/public.d.ts b/packages/kit/src/exports/public.d.ts index e9d661b343dc..aa5bc31da90d 100644 --- a/packages/kit/src/exports/public.d.ts +++ b/packages/kit/src/exports/public.d.ts @@ -142,6 +142,13 @@ export interface Builder { * @returns an array of files written to `dest` */ writeServer(dest: string): string[]; + /** + * Copy any assets imported by server code to `dest`. + * @param dest the destination folder + * @param routes an array of routes to find asset dependencies for + * @returns an array of files written to `dest` + */ + writeServerAssets(dest: string, routes?: RouteDefinition[]): string[]; /** * Copy a file or directory. * @param from the source file or directory diff --git a/packages/kit/src/runtime/app/server/index.js b/packages/kit/src/runtime/app/server/index.js index 4e8d1420006a..d40f82cc4392 100644 --- a/packages/kit/src/runtime/app/server/index.js +++ b/packages/kit/src/runtime/app/server/index.js @@ -28,7 +28,8 @@ export function readAsset(file) { } if (file in manifest._.files) { - const [length, type] = manifest._.files[file]; + const length = manifest._.files[file]; + const type = manifest.mimeTypes[file.slice(file.lastIndexOf('.'))]; return new Response(read_asset(file), { headers: { diff --git a/packages/kit/types/index.d.ts b/packages/kit/types/index.d.ts index c9d6e16c9139..53462eaf3bfc 100644 --- a/packages/kit/types/index.d.ts +++ b/packages/kit/types/index.d.ts @@ -124,6 +124,13 @@ declare module '@sveltejs/kit' { * @returns an array of files written to `dest` */ writeServer(dest: string): string[]; + /** + * Copy any assets imported by server code to `dest`. + * @param dest the destination folder + * @param routes an array of routes to find asset dependencies for + * @returns an array of files written to `dest` + */ + writeServerAssets(dest: string, routes?: RouteDefinition[]): string[]; /** * Copy a file or directory. * @param from the source file or directory From 5c6f2df748c86b7c971d17a21295a3f74a55327d Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 17 Jan 2024 00:00:40 -0500 Subject: [PATCH 11/57] lint/fix --- packages/adapter-vercel/files/serverless.js | 1 - packages/kit/src/core/generate_manifest/find_server_assets.js | 2 +- packages/kit/src/core/generate_manifest/index.js | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/adapter-vercel/files/serverless.js b/packages/adapter-vercel/files/serverless.js index 230d43a1a4ef..0c67bcde8552 100644 --- a/packages/adapter-vercel/files/serverless.js +++ b/packages/adapter-vercel/files/serverless.js @@ -1,5 +1,4 @@ import fs from 'node:fs'; -import path from 'node:path'; import { Readable } from 'node:stream'; import { installPolyfills } from '@sveltejs/kit/node/polyfills'; import { getRequest, setResponse } from '@sveltejs/kit/node'; diff --git a/packages/kit/src/core/generate_manifest/find_server_assets.js b/packages/kit/src/core/generate_manifest/find_server_assets.js index 08c9f8a33c6a..bce8b7cf182a 100644 --- a/packages/kit/src/core/generate_manifest/find_server_assets.js +++ b/packages/kit/src/core/generate_manifest/find_server_assets.js @@ -40,7 +40,7 @@ export function find_server_assets(build_data, routes) { for (const n of used_nodes) { const node = build_data.manifest_data.nodes[n]; - if (node.server) add_assets(node.server); + if (node?.server) add_assets(node.server); } return Array.from(server_assets); diff --git a/packages/kit/src/core/generate_manifest/index.js b/packages/kit/src/core/generate_manifest/index.js index cef280b13b59..42648f9a9822 100644 --- a/packages/kit/src/core/generate_manifest/index.js +++ b/packages/kit/src/core/generate_manifest/index.js @@ -3,7 +3,7 @@ import path from 'node:path'; import * as mime from 'mrmime'; import { s } from '../../utils/misc.js'; import { get_mime_lookup } from '../utils.js'; -import { find_deps, resolve_symlinks } from '../../exports/vite/build/utils.js'; +import { resolve_symlinks } from '../../exports/vite/build/utils.js'; import { compact } from '../../utils/array.js'; import { join_relative } from '../../utils/filesystem.js'; import { dedent } from '../sync/utils.js'; From c9bfc03547d4fd3dbc7f276ae340fe10a75ac2d5 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 17 Jan 2024 09:24:14 -0500 Subject: [PATCH 12/57] Update packages/adapter-vercel/index.js Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com> --- packages/adapter-vercel/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/adapter-vercel/index.js b/packages/adapter-vercel/index.js index d7a96c5e3942..bcdabde22714 100644 --- a/packages/adapter-vercel/index.js +++ b/packages/adapter-vercel/index.js @@ -81,7 +81,7 @@ const plugin = function (defaults = {}) { await create_function_bundle(builder, `${tmp}/index.js`, dir, config); - builder.writeServerAssets(dir, routes); + builder.writeServerAssets?.(dir, routes); // TODO remove optional chaining once peer dep contains a minimum SvelteKit version that has the method } /** From 41514d3dfee1508801f8ef3f849ef698fb45c276 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 17 Jan 2024 09:54:37 -0500 Subject: [PATCH 13/57] fix --- packages/kit/src/exports/public.d.ts | 3 ++- packages/kit/src/exports/vite/dev/index.js | 2 +- packages/kit/src/runtime/app/server/index.js | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/kit/src/exports/public.d.ts b/packages/kit/src/exports/public.d.ts index aa5bc31da90d..f3078e514121 100644 --- a/packages/kit/src/exports/public.d.ts +++ b/packages/kit/src/exports/public.d.ts @@ -1164,10 +1164,11 @@ export interface SSRManifest { /** private fields */ _: { client: NonNullable; - files: Record; nodes: SSRNodeLoader[]; routes: SSRRoute[]; matchers(): Promise>; + /** A `[file]: size` map of all assets imported by server code */ + server_assets: Record; }; } diff --git a/packages/kit/src/exports/vite/dev/index.js b/packages/kit/src/exports/vite/dev/index.js index 3cf04d28b991..e17c7192f31f 100644 --- a/packages/kit/src/exports/vite/dev/index.js +++ b/packages/kit/src/exports/vite/dev/index.js @@ -125,7 +125,7 @@ export async function dev(vite, vite_config, svelte_config) { fonts: [], uses_env_dynamic_public: true }, - files: new Proxy( + server_assets: new Proxy( {}, { get: (_, /** @type {string} */ file) => { diff --git a/packages/kit/src/runtime/app/server/index.js b/packages/kit/src/runtime/app/server/index.js index d40f82cc4392..dec058e42251 100644 --- a/packages/kit/src/runtime/app/server/index.js +++ b/packages/kit/src/runtime/app/server/index.js @@ -27,8 +27,8 @@ export function readAsset(file) { }); } - if (file in manifest._.files) { - const length = manifest._.files[file]; + if (file in manifest._.server_assets) { + const length = manifest._.server_assets[file]; const type = manifest.mimeTypes[file.slice(file.lastIndexOf('.'))]; return new Response(read_asset(file), { From c4ecda4195541e31ec0cf294c4b0533ff4c8dbed Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 17 Jan 2024 10:08:16 -0500 Subject: [PATCH 14/57] createReadable helper --- packages/adapter-netlify/src/serverless.js | 5 ++--- packages/adapter-node/src/handler.js | 6 ++---- packages/adapter-vercel/files/serverless.js | 7 ++----- packages/kit/src/exports/node/index.js | 11 +++++++++++ packages/kit/src/exports/vite/dev/index.js | 5 ++--- packages/kit/src/exports/vite/preview/index.js | 5 ++--- packages/kit/types/index.d.ts | 7 ++++++- 7 files changed, 27 insertions(+), 19 deletions(-) diff --git a/packages/adapter-netlify/src/serverless.js b/packages/adapter-netlify/src/serverless.js index 0b5876485bb8..effbd394c7c5 100644 --- a/packages/adapter-netlify/src/serverless.js +++ b/packages/adapter-netlify/src/serverless.js @@ -1,7 +1,7 @@ import './shims'; import { Server } from '0SERVER'; import { split_headers } from './headers.js'; -import { Readable } from 'node:stream'; +import { createReadable } from '@sveltejs/kit/node'; /** * @param {import('@sveltejs/kit').SSRManifest} manifest @@ -12,8 +12,7 @@ export function init(manifest) { let init_promise = server.init({ env: process.env, - // @ts-expect-error the types are wrong - readAsset: (file) => Readable.toWeb(fs.createReadStream('.netlify/server' + file)) + readAsset: (file) => createReadable('.netlify/server' + file) }); return async (event, context) => { diff --git a/packages/adapter-node/src/handler.js b/packages/adapter-node/src/handler.js index 0e874b5c2877..a38a4ef15b15 100644 --- a/packages/adapter-node/src/handler.js +++ b/packages/adapter-node/src/handler.js @@ -3,9 +3,8 @@ import fs from 'node:fs'; import path from 'node:path'; import sirv from 'sirv'; import { fileURLToPath } from 'node:url'; -import { Readable } from 'node:stream'; import { parse as polka_url_parser } from '@polka/url'; -import { getRequest, setResponse } from '@sveltejs/kit/node'; +import { getRequest, setResponse, createReadable } from '@sveltejs/kit/node'; import { Server } from 'SERVER'; import { manifest, prerendered } from 'MANIFEST'; import { env } from 'ENV'; @@ -16,8 +15,7 @@ const server = new Server(manifest); await server.init({ env: process.env, - // @ts-expect-error the types are wrong - readAsset: (file) => Readable.toWeb(fs.createReadStream(path.join(dir, 'client', file))) + readAsset: (file) => createReadable(path.join(dir, 'client', file)) }); const origin = env('ORIGIN', undefined); diff --git a/packages/adapter-vercel/files/serverless.js b/packages/adapter-vercel/files/serverless.js index 0c67bcde8552..e13606202dc4 100644 --- a/packages/adapter-vercel/files/serverless.js +++ b/packages/adapter-vercel/files/serverless.js @@ -1,7 +1,5 @@ -import fs from 'node:fs'; -import { Readable } from 'node:stream'; import { installPolyfills } from '@sveltejs/kit/node/polyfills'; -import { getRequest, setResponse } from '@sveltejs/kit/node'; +import { getRequest, setResponse, createReadable } from '@sveltejs/kit/node'; import { Server } from 'SERVER'; import { manifest } from 'MANIFEST'; @@ -11,8 +9,7 @@ const server = new Server(manifest); await server.init({ env: /** @type {Record} */ (process.env), - // @ts-expect-error the types are wrong - readAsset: (file) => Readable.toWeb(fs.createReadStream('.' + file)) + readAsset: (file) => createReadable('.' + file) }); const DATA_SUFFIX = '/__data.json'; diff --git a/packages/kit/src/exports/node/index.js b/packages/kit/src/exports/node/index.js index d15682838aaf..6bf5943bc91a 100644 --- a/packages/kit/src/exports/node/index.js +++ b/packages/kit/src/exports/node/index.js @@ -1,3 +1,5 @@ +import { createReadStream } from 'node:fs'; +import { Readable } from 'node:stream'; import * as set_cookie_parser from 'set-cookie-parser'; import { SvelteKitError } from '../../runtime/control.js'; @@ -189,3 +191,12 @@ export async function setResponse(res, response) { } } } + +/** + * Converts a file on disk to a readable stream + * @param {string} file + * @returns {ReadableStream} + */ +export function createReadable(file) { + return /** @type {ReadableStream} */ (Readable.toWeb(createReadStream(file))); +} diff --git a/packages/kit/src/exports/vite/dev/index.js b/packages/kit/src/exports/vite/dev/index.js index e17c7192f31f..eb49ea14e58a 100644 --- a/packages/kit/src/exports/vite/dev/index.js +++ b/packages/kit/src/exports/vite/dev/index.js @@ -6,7 +6,7 @@ import colors from 'kleur'; import sirv from 'sirv'; import * as mime from 'mrmime'; import { isCSSRequest, loadEnv, buildErrorMessage } from 'vite'; -import { getRequest, setResponse } from '../../../exports/node/index.js'; +import { createReadable, getRequest, setResponse } from '../../../exports/node/index.js'; import { installPolyfills } from '../../../exports/node/polyfills.js'; import { coalesce_to_error } from '../../../utils/error.js'; import { posixify, resolve_entry, to_fs } from '../../../utils/filesystem.js'; @@ -486,8 +486,7 @@ export async function dev(vite, vite_config, svelte_config) { await server.init({ env, - // @ts-expect-error the types are wrong - readAsset: (file) => Readable.toWeb(fs.createReadStream('.' + file)) + readAsset: (file) => createReadable('.' + file) }); const request = await getRequest({ diff --git a/packages/kit/src/exports/vite/preview/index.js b/packages/kit/src/exports/vite/preview/index.js index f60f85cbf1e0..be42bd49443d 100644 --- a/packages/kit/src/exports/vite/preview/index.js +++ b/packages/kit/src/exports/vite/preview/index.js @@ -5,7 +5,7 @@ import { Readable } from 'node:stream'; import { lookup } from 'mrmime'; import sirv from 'sirv'; import { loadEnv, normalizePath } from 'vite'; -import { getRequest, setResponse } from '../../../exports/node/index.js'; +import { createReadable, getRequest, setResponse } from '../../../exports/node/index.js'; import { installPolyfills } from '../../../exports/node/polyfills.js'; import { SVELTE_KIT_ASSETS } from '../../../constants.js'; import { not_found } from '../utils.js'; @@ -49,8 +49,7 @@ export async function preview(vite, vite_config, svelte_config) { const server = new Server(manifest); await server.init({ env: loadEnv(vite_config.mode, svelte_config.kit.env.dir, ''), - // @ts-expect-error the types are wrong - readAsset: (file) => Readable.toWeb(fs.createReadStream(dir + file)) + readAsset: (file) => createReadable(dir + file) }); return () => { diff --git a/packages/kit/types/index.d.ts b/packages/kit/types/index.d.ts index 53462eaf3bfc..7394bd5b96da 100644 --- a/packages/kit/types/index.d.ts +++ b/packages/kit/types/index.d.ts @@ -1146,10 +1146,11 @@ declare module '@sveltejs/kit' { /** private fields */ _: { client: NonNullable; - files: Record; nodes: SSRNodeLoader[]; routes: SSRRoute[]; matchers(): Promise>; + /** A `[file]: size` map of all assets imported by server code */ + server_assets: Record; }; } @@ -1883,6 +1884,10 @@ declare module '@sveltejs/kit/node' { }): Promise; export function setResponse(res: import('http').ServerResponse, response: Response): Promise; + /** + * Converts a file on disk to a readable stream + * */ + export function createReadable(file: string): ReadableStream; } declare module '@sveltejs/kit/node/polyfills' { From 809fafd3626002b12dd9bb56531be13f901d8568 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 17 Jan 2024 10:18:25 -0500 Subject: [PATCH 15/57] more future-proof API --- packages/adapter-vercel/index.js | 8 +++++++- packages/kit/src/core/adapt/builder.js | 21 +++++++-------------- packages/kit/src/exports/public.d.ts | 13 ++++++------- packages/kit/types/index.d.ts | 12 +++++------- 4 files changed, 25 insertions(+), 29 deletions(-) diff --git a/packages/adapter-vercel/index.js b/packages/adapter-vercel/index.js index bcdabde22714..6cca046a098c 100644 --- a/packages/adapter-vercel/index.js +++ b/packages/adapter-vercel/index.js @@ -81,7 +81,13 @@ const plugin = function (defaults = {}) { await create_function_bundle(builder, `${tmp}/index.js`, dir, config); - builder.writeServerAssets?.(dir, routes); // TODO remove optional chaining once peer dep contains a minimum SvelteKit version that has the method + // TODO remove `if` once peer dep contains a minimum SvelteKit version that has the method + if (builder.findServerAssets) { + for (const asset of builder.findServerAssets(routes)) { + // TODO use symlinks, once Build Output API supports doing so + builder.copy(`${builder.getServerDirectory()}/${asset}`, `${dir}/${asset}`); + } + } } /** diff --git a/packages/kit/src/core/adapt/builder.js b/packages/kit/src/core/adapt/builder.js index d072433ede6d..97cdd24a4f2b 100644 --- a/packages/kit/src/core/adapt/builder.js +++ b/packages/kit/src/core/adapt/builder.js @@ -146,6 +146,13 @@ export function create_builder({ } }, + findServerAssets(route_data) { + return find_server_assets( + build_data, + route_data.map((route) => /** @type {import('types').RouteData} */ (lookup.get(route))) + ); + }, + async generateFallback(dest) { const manifest_path = `${config.kit.outDir}/output/server/manifest-full.js`; const env = get_env(config.kit.env, vite_config.mode); @@ -205,20 +212,6 @@ export function create_builder({ writeServer(dest) { return copy(`${config.kit.outDir}/output/server`, dest); - }, - - writeServerAssets(dest, subset) { - const routes = subset - ? subset.map((route) => /** @type {import('types').RouteData} */ (lookup.get(route))) - : route_data.filter((route) => prerender_map.get(route.id) !== true); - - const server_assets = find_server_assets(build_data, routes); - - for (const file of server_assets) { - copy(`${config.kit.outDir}/output/server/${file}`, `${dest}/${file}`); - } - - return server_assets; } }; } diff --git a/packages/kit/src/exports/public.d.ts b/packages/kit/src/exports/public.d.ts index f3078e514121..25dddfd720a9 100644 --- a/packages/kit/src/exports/public.d.ts +++ b/packages/kit/src/exports/public.d.ts @@ -96,6 +96,12 @@ export interface Builder { */ createEntries(fn: (route: RouteDefinition) => AdapterEntry): Promise; + /** + * Find all the assets imported by server files belonging to `routes` + * @param routes + */ + findServerAssets(routes: RouteDefinition[]): string[]; + /** * Generate a fallback page for a static webserver to use when no route is matched. Useful for single-page apps. */ @@ -142,13 +148,6 @@ export interface Builder { * @returns an array of files written to `dest` */ writeServer(dest: string): string[]; - /** - * Copy any assets imported by server code to `dest`. - * @param dest the destination folder - * @param routes an array of routes to find asset dependencies for - * @returns an array of files written to `dest` - */ - writeServerAssets(dest: string, routes?: RouteDefinition[]): string[]; /** * Copy a file or directory. * @param from the source file or directory diff --git a/packages/kit/types/index.d.ts b/packages/kit/types/index.d.ts index 7394bd5b96da..e628bbadd879 100644 --- a/packages/kit/types/index.d.ts +++ b/packages/kit/types/index.d.ts @@ -78,6 +78,11 @@ declare module '@sveltejs/kit' { */ createEntries(fn: (route: RouteDefinition) => AdapterEntry): Promise; + /** + * Find all the assets imported by server files belonging to `routes` + * */ + findServerAssets(routes: RouteDefinition[]): string[]; + /** * Generate a fallback page for a static webserver to use when no route is matched. Useful for single-page apps. */ @@ -124,13 +129,6 @@ declare module '@sveltejs/kit' { * @returns an array of files written to `dest` */ writeServer(dest: string): string[]; - /** - * Copy any assets imported by server code to `dest`. - * @param dest the destination folder - * @param routes an array of routes to find asset dependencies for - * @returns an array of files written to `dest` - */ - writeServerAssets(dest: string, routes?: RouteDefinition[]): string[]; /** * Copy a file or directory. * @param from the source file or directory From d559345902dd5ed724b140362666506c710d3376 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 17 Jan 2024 10:48:35 -0500 Subject: [PATCH 16/57] account for basepath --- packages/adapter-netlify/src/serverless.js | 2 +- packages/adapter-node/ambient.d.ts | 1 + packages/adapter-node/index.js | 7 +++++-- packages/adapter-node/src/handler.js | 14 ++++++++------ packages/adapter-vercel/files/serverless.js | 2 +- packages/kit/src/core/generate_manifest/index.js | 10 +++++----- packages/kit/src/exports/vite/dev/index.js | 11 +++-------- packages/kit/src/exports/vite/preview/index.js | 2 +- packages/kit/src/runtime/app/server/index.js | 11 +++++++---- 9 files changed, 32 insertions(+), 28 deletions(-) diff --git a/packages/adapter-netlify/src/serverless.js b/packages/adapter-netlify/src/serverless.js index effbd394c7c5..019b6313d46b 100644 --- a/packages/adapter-netlify/src/serverless.js +++ b/packages/adapter-netlify/src/serverless.js @@ -12,7 +12,7 @@ export function init(manifest) { let init_promise = server.init({ env: process.env, - readAsset: (file) => createReadable('.netlify/server' + file) + readAsset: (file) => createReadable(`.netlify/server/${file}`) }); return async (event, context) => { diff --git a/packages/adapter-node/ambient.d.ts b/packages/adapter-node/ambient.d.ts index 7d45ea6dc64a..acdc19bb2587 100644 --- a/packages/adapter-node/ambient.d.ts +++ b/packages/adapter-node/ambient.d.ts @@ -9,6 +9,7 @@ declare module 'HANDLER' { declare module 'MANIFEST' { import { SSRManifest } from '@sveltejs/kit'; + export const base: string; export const manifest: SSRManifest; export const prerendered: Set; } diff --git a/packages/adapter-node/index.js b/packages/adapter-node/index.js index 4cd8d42cbe29..a1077b2f8073 100644 --- a/packages/adapter-node/index.js +++ b/packages/adapter-node/index.js @@ -39,8 +39,11 @@ export default function (opts = {}) { writeFileSync( `${tmp}/manifest.js`, - `export const manifest = ${builder.generateManifest({ relativePath: './' })};\n\n` + - `export const prerendered = new Set(${JSON.stringify(builder.prerendered.paths)});\n` + [ + `export const manifest = ${builder.generateManifest({ relativePath: './' })};`, + `export const prerendered = new Set(${JSON.stringify(builder.prerendered.paths)});`, + `export const base = ${JSON.stringify(builder.config.kit.paths.base)};` + ].join('\n\n') ); const pkg = JSON.parse(readFileSync('package.json', 'utf8')); diff --git a/packages/adapter-node/src/handler.js b/packages/adapter-node/src/handler.js index a38a4ef15b15..9eabe7a806c8 100644 --- a/packages/adapter-node/src/handler.js +++ b/packages/adapter-node/src/handler.js @@ -6,18 +6,13 @@ import { fileURLToPath } from 'node:url'; import { parse as polka_url_parser } from '@polka/url'; import { getRequest, setResponse, createReadable } from '@sveltejs/kit/node'; import { Server } from 'SERVER'; -import { manifest, prerendered } from 'MANIFEST'; +import { manifest, prerendered, base } from 'MANIFEST'; import { env } from 'ENV'; /* global ENV_PREFIX */ const server = new Server(manifest); -await server.init({ - env: process.env, - readAsset: (file) => createReadable(path.join(dir, 'client', file)) -}); - const origin = env('ORIGIN', undefined); const xff_depth = parseInt(env('XFF_DEPTH', '1')); const address_header = env('ADDRESS_HEADER', '').toLowerCase(); @@ -34,6 +29,13 @@ if (isNaN(body_size_limit)) { const dir = path.dirname(fileURLToPath(import.meta.url)); +const asset_dir = `${dir}/client${base}`; + +await server.init({ + env: process.env, + readAsset: (file) => createReadable(`${asset_dir}/${file}`) +}); + /** * @param {string} path * @param {boolean} client diff --git a/packages/adapter-vercel/files/serverless.js b/packages/adapter-vercel/files/serverless.js index e13606202dc4..2c05e2a7c677 100644 --- a/packages/adapter-vercel/files/serverless.js +++ b/packages/adapter-vercel/files/serverless.js @@ -9,7 +9,7 @@ const server = new Server(manifest); await server.init({ env: /** @type {Record} */ (process.env), - readAsset: (file) => createReadable('.' + file) + readAsset: createReadable }); const DATA_SUFFIX = '/__data.json'; diff --git a/packages/kit/src/core/generate_manifest/index.js b/packages/kit/src/core/generate_manifest/index.js index 42648f9a9822..a2ef4cd4c619 100644 --- a/packages/kit/src/core/generate_manifest/index.js +++ b/packages/kit/src/core/generate_manifest/index.js @@ -73,10 +73,10 @@ export function generate_manifest({ build_data, relative_path, routes }) { /** @type {Record} */ const files = {}; - for (const asset of server_assets) { - files['/' + asset] = fs.statSync(path.resolve(build_data.out_dir, 'server', asset)).size; + for (const file of server_assets) { + files[file] = fs.statSync(path.resolve(build_data.out_dir, 'server', file)).size; - const ext = path.extname(asset); + const ext = path.extname(file); mime_types[ext] ??= mime.lookup(ext) || ''; } @@ -91,7 +91,6 @@ export function generate_manifest({ build_data, relative_path, routes }) { mimeTypes: ${s(mime_types)}, _: { client: ${s(build_data.client)}, - files: ${s(files)}, nodes: [ ${(node_paths).map(loader).join(',\n')} ], @@ -120,7 +119,8 @@ export function generate_manifest({ build_data, relative_path, routes }) { type => `const { match: ${type} } = await import ('${(join_relative(relative_path, `/entries/matchers/${type}.js`))}')` ).join('\n')} return { ${Array.from(matchers).join(', ')} }; - } + }, + server_assets: ${s(files)} } } `; diff --git a/packages/kit/src/exports/vite/dev/index.js b/packages/kit/src/exports/vite/dev/index.js index eb49ea14e58a..8ae187c29ba0 100644 --- a/packages/kit/src/exports/vite/dev/index.js +++ b/packages/kit/src/exports/vite/dev/index.js @@ -128,13 +128,8 @@ export async function dev(vite, vite_config, svelte_config) { server_assets: new Proxy( {}, { - get: (_, /** @type {string} */ file) => { - const stats = fs.statSync('.' + file); - return [stats.size, mime.lookup(file)]; - }, - has: (_, /** @type {string} */ file) => { - return fs.existsSync('.' + file); - } + has: (_, /** @type {string} */ file) => fs.existsSync(file), + get: (_, /** @type {string} */ file) => fs.statSync(file).size } ), nodes: manifest_data.nodes.map((node, index) => { @@ -486,7 +481,7 @@ export async function dev(vite, vite_config, svelte_config) { await server.init({ env, - readAsset: (file) => createReadable('.' + file) + readAsset: createReadable }); const request = await getRequest({ diff --git a/packages/kit/src/exports/vite/preview/index.js b/packages/kit/src/exports/vite/preview/index.js index be42bd49443d..ee3cf1932e43 100644 --- a/packages/kit/src/exports/vite/preview/index.js +++ b/packages/kit/src/exports/vite/preview/index.js @@ -49,7 +49,7 @@ export async function preview(vite, vite_config, svelte_config) { const server = new Server(manifest); await server.init({ env: loadEnv(vite_config.mode, svelte_config.kit.env.dir, ''), - readAsset: (file) => createReadable(dir + file) + readAsset: (file) => createReadable(`${dir}/${file}`) }); return () => { diff --git a/packages/kit/src/runtime/app/server/index.js b/packages/kit/src/runtime/app/server/index.js index dec058e42251..5c41e8938820 100644 --- a/packages/kit/src/runtime/app/server/index.js +++ b/packages/kit/src/runtime/app/server/index.js @@ -1,11 +1,12 @@ import { read_asset, manifest } from '__sveltekit/server'; +import { base } from '__sveltekit/paths'; /** * TODO docs - * @param {string} file + * @param {string} asset * @returns {Response} */ -export function readAsset(file) { +export function readAsset(asset) { if (!read_asset) { throw new Error( 'No readAsset implementation was provided. Please ensure that your adapter is up to date and supports this feature' @@ -13,8 +14,8 @@ export function readAsset(file) { } // handle inline assets internally - if (file.startsWith('data:')) { - const [prelude, data] = file.split(';'); + if (asset.startsWith('data:')) { + const [prelude, data] = asset.split(';'); const type = prelude.slice(5) || 'application/octet-stream'; const decoded = data.startsWith('base64,') ? atob(data.slice(7)) : decodeURIComponent(data); @@ -27,6 +28,8 @@ export function readAsset(file) { }); } + const file = asset.slice(base.length + 1); + if (file in manifest._.server_assets) { const length = manifest._.server_assets[file]; const type = manifest.mimeTypes[file.slice(file.lastIndexOf('.'))]; From d5b98da5d8cd704667e0ecb0caf03efea86d3666 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 17 Jan 2024 10:53:13 -0500 Subject: [PATCH 17/57] lint --- packages/kit/src/exports/vite/dev/index.js | 2 -- packages/kit/src/exports/vite/preview/index.js | 1 - 2 files changed, 3 deletions(-) diff --git a/packages/kit/src/exports/vite/dev/index.js b/packages/kit/src/exports/vite/dev/index.js index 8ae187c29ba0..568c44dff754 100644 --- a/packages/kit/src/exports/vite/dev/index.js +++ b/packages/kit/src/exports/vite/dev/index.js @@ -1,10 +1,8 @@ import fs from 'node:fs'; import path from 'node:path'; import { URL } from 'node:url'; -import { Readable } from 'node:stream'; import colors from 'kleur'; import sirv from 'sirv'; -import * as mime from 'mrmime'; import { isCSSRequest, loadEnv, buildErrorMessage } from 'vite'; import { createReadable, getRequest, setResponse } from '../../../exports/node/index.js'; import { installPolyfills } from '../../../exports/node/polyfills.js'; diff --git a/packages/kit/src/exports/vite/preview/index.js b/packages/kit/src/exports/vite/preview/index.js index ee3cf1932e43..7c466e52d5f2 100644 --- a/packages/kit/src/exports/vite/preview/index.js +++ b/packages/kit/src/exports/vite/preview/index.js @@ -1,7 +1,6 @@ import fs from 'node:fs'; import { join } from 'node:path'; import { pathToFileURL } from 'node:url'; -import { Readable } from 'node:stream'; import { lookup } from 'mrmime'; import sirv from 'sirv'; import { loadEnv, normalizePath } from 'vite'; From 8f5fd8401179dd174aae4c323854ca2f372abacc Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 17 Jan 2024 11:05:30 -0500 Subject: [PATCH 18/57] rename to just `read` --- packages/adapter-netlify/src/serverless.js | 2 +- packages/adapter-node/src/handler.js | 2 +- packages/adapter-vercel/files/serverless.js | 2 +- packages/kit/src/exports/public.d.ts | 2 +- packages/kit/src/exports/vite/dev/index.js | 2 +- packages/kit/src/exports/vite/preview/index.js | 2 +- packages/kit/src/runtime/app/server/index.js | 14 +++++++++++--- packages/kit/src/runtime/server/index.js | 8 ++++---- packages/kit/types/index.d.ts | 14 +++++++++++--- 9 files changed, 32 insertions(+), 16 deletions(-) diff --git a/packages/adapter-netlify/src/serverless.js b/packages/adapter-netlify/src/serverless.js index 019b6313d46b..ef35768bd9f0 100644 --- a/packages/adapter-netlify/src/serverless.js +++ b/packages/adapter-netlify/src/serverless.js @@ -12,7 +12,7 @@ export function init(manifest) { let init_promise = server.init({ env: process.env, - readAsset: (file) => createReadable(`.netlify/server/${file}`) + read: (file) => createReadable(`.netlify/server/${file}`) }); return async (event, context) => { diff --git a/packages/adapter-node/src/handler.js b/packages/adapter-node/src/handler.js index 9eabe7a806c8..ada79c1e42ba 100644 --- a/packages/adapter-node/src/handler.js +++ b/packages/adapter-node/src/handler.js @@ -33,7 +33,7 @@ const asset_dir = `${dir}/client${base}`; await server.init({ env: process.env, - readAsset: (file) => createReadable(`${asset_dir}/${file}`) + read: (file) => createReadable(`${asset_dir}/${file}`) }); /** diff --git a/packages/adapter-vercel/files/serverless.js b/packages/adapter-vercel/files/serverless.js index 2c05e2a7c677..9fadc3d42c46 100644 --- a/packages/adapter-vercel/files/serverless.js +++ b/packages/adapter-vercel/files/serverless.js @@ -9,7 +9,7 @@ const server = new Server(manifest); await server.init({ env: /** @type {Record} */ (process.env), - readAsset: createReadable + read: createReadable }); const DATA_SUFFIX = '/__data.json'; diff --git a/packages/kit/src/exports/public.d.ts b/packages/kit/src/exports/public.d.ts index 25dddfd720a9..789841d94af8 100644 --- a/packages/kit/src/exports/public.d.ts +++ b/packages/kit/src/exports/public.d.ts @@ -1151,7 +1151,7 @@ export class Server { export interface ServerInitOptions { env: Record; - readAsset?: (file: string) => ReadableStream; + read?: (file: string) => ReadableStream; } export interface SSRManifest { diff --git a/packages/kit/src/exports/vite/dev/index.js b/packages/kit/src/exports/vite/dev/index.js index 568c44dff754..4e08a51c9079 100644 --- a/packages/kit/src/exports/vite/dev/index.js +++ b/packages/kit/src/exports/vite/dev/index.js @@ -479,7 +479,7 @@ export async function dev(vite, vite_config, svelte_config) { await server.init({ env, - readAsset: createReadable + read: createReadable }); const request = await getRequest({ diff --git a/packages/kit/src/exports/vite/preview/index.js b/packages/kit/src/exports/vite/preview/index.js index 7c466e52d5f2..c9550e08dbf1 100644 --- a/packages/kit/src/exports/vite/preview/index.js +++ b/packages/kit/src/exports/vite/preview/index.js @@ -48,7 +48,7 @@ export async function preview(vite, vite_config, svelte_config) { const server = new Server(manifest); await server.init({ env: loadEnv(vite_config.mode, svelte_config.kit.env.dir, ''), - readAsset: (file) => createReadable(`${dir}/${file}`) + read: (file) => createReadable(`${dir}/${file}`) }); return () => { diff --git a/packages/kit/src/runtime/app/server/index.js b/packages/kit/src/runtime/app/server/index.js index 5c41e8938820..8d85246f1e93 100644 --- a/packages/kit/src/runtime/app/server/index.js +++ b/packages/kit/src/runtime/app/server/index.js @@ -2,14 +2,22 @@ import { read_asset, manifest } from '__sveltekit/server'; import { base } from '__sveltekit/paths'; /** - * TODO docs + * Read the contents of an imported asset + * @example + * ```js + * import { read } from '$app/server'; + * import somefile from './somefile.txt'; + * + * const asset = read(somefile); + * const text = await asset.text(); + * ``` * @param {string} asset * @returns {Response} */ -export function readAsset(asset) { +export function read(asset) { if (!read_asset) { throw new Error( - 'No readAsset implementation was provided. Please ensure that your adapter is up to date and supports this feature' + 'No `read` implementation was provided. Please ensure that your adapter is up to date and supports this feature' ); } diff --git a/packages/kit/src/runtime/server/index.js b/packages/kit/src/runtime/server/index.js index 4b83a6cbe2dd..5847f9dff80c 100644 --- a/packages/kit/src/runtime/server/index.js +++ b/packages/kit/src/runtime/server/index.js @@ -34,10 +34,10 @@ export class Server { /** * @param {{ * env: Record; - * readAsset?: (file: string) => ReadableStream; + * read?: (file: string) => ReadableStream; * }} opts */ - async init({ env, readAsset }) { + async init({ env, read }) { // Take care: Some adapters may have to call `Server.init` per-request to set env vars, // so anything that shouldn't be rerun should be wrapped in an `if` block to make sure it hasn't // been done already. @@ -59,8 +59,8 @@ export class Server { ); set_safe_public_env(public_env); - if (readAsset) { - set_read_asset(readAsset); + if (read) { + set_read_asset(read); } if (!this.#options.hooks) { diff --git a/packages/kit/types/index.d.ts b/packages/kit/types/index.d.ts index e628bbadd879..61d57ef0a411 100644 --- a/packages/kit/types/index.d.ts +++ b/packages/kit/types/index.d.ts @@ -1132,7 +1132,7 @@ declare module '@sveltejs/kit' { export interface ServerInitOptions { env: Record; - readAsset?: (file: string) => ReadableStream; + read?: (file: string) => ReadableStream; } export interface SSRManifest { @@ -2122,9 +2122,17 @@ declare module '$app/paths' { declare module '$app/server' { /** - * TODO docs + * Read the contents of an imported asset + * @example + * ```js + * import { read } from '$app/server'; + * import somefile from './somefile.txt'; + * + * const asset = read(somefile); + * const text = await asset.text(); + * ``` * */ - export function readAsset(file: string): Response; + export function read(asset: string): Response; } declare module '$app/stores' { From 08fd1b1d329d6e6ad1de1df5c936925808879949 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 17 Jan 2024 11:11:47 -0500 Subject: [PATCH 19/57] inline docs --- packages/kit/src/exports/public.d.ts | 2 ++ packages/kit/types/index.d.ts | 2 ++ 2 files changed, 4 insertions(+) diff --git a/packages/kit/src/exports/public.d.ts b/packages/kit/src/exports/public.d.ts index 789841d94af8..377745a1cb7a 100644 --- a/packages/kit/src/exports/public.d.ts +++ b/packages/kit/src/exports/public.d.ts @@ -1150,7 +1150,9 @@ export class Server { } export interface ServerInitOptions { + /** A map of environment variables */ env: Record; + /** A function that turns an asset filename into a `ReadableStream`. Required for the `read` export from `$app/server` to work */ read?: (file: string) => ReadableStream; } diff --git a/packages/kit/types/index.d.ts b/packages/kit/types/index.d.ts index 61d57ef0a411..3d728beea22f 100644 --- a/packages/kit/types/index.d.ts +++ b/packages/kit/types/index.d.ts @@ -1131,7 +1131,9 @@ declare module '@sveltejs/kit' { } export interface ServerInitOptions { + /** A map of environment variables */ env: Record; + /** A function that turns an asset filename into a `ReadableStream`. Required for the `read` export from `$app/server` to work */ read?: (file: string) => ReadableStream; } From 8d606a240be13745f69f5a09d963590f4f0a4433 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 17 Jan 2024 11:16:40 -0500 Subject: [PATCH 20/57] it is already deprecated, we just need to remove it --- packages/kit/src/core/adapt/builder.js | 1 - packages/kit/src/exports/public.d.ts | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/kit/src/core/adapt/builder.js b/packages/kit/src/core/adapt/builder.js index 97cdd24a4f2b..76ba2b9c1f5c 100644 --- a/packages/kit/src/core/adapt/builder.js +++ b/packages/kit/src/core/adapt/builder.js @@ -96,7 +96,6 @@ export function create_builder({ ); }, - // TODO this appears to be unused and should probably be deprecated (and removed in 3.0) async createEntries(fn) { const seen = new Set(); diff --git a/packages/kit/src/exports/public.d.ts b/packages/kit/src/exports/public.d.ts index 377745a1cb7a..5e21a6ea672b 100644 --- a/packages/kit/src/exports/public.d.ts +++ b/packages/kit/src/exports/public.d.ts @@ -89,6 +89,7 @@ export interface Builder { /** An array of all routes (including prerendered) */ routes: RouteDefinition[]; + // TODO 3.0 remove this method /** * Create separate functions that map to one or more routes of your app. * @param fn A function that groups a set of routes into an entry point From 7609aa08052a49a5f5dc00699364973b891b2e63 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 17 Jan 2024 11:17:47 -0500 Subject: [PATCH 21/57] read_asset -> read_implementation --- packages/kit/src/core/sync/write_server.js | 4 ++-- packages/kit/src/exports/vite/index.js | 6 +++--- packages/kit/src/runtime/app/server/index.js | 6 +++--- packages/kit/src/runtime/server/index.js | 4 ++-- packages/kit/src/types/ambient-private.d.ts | 4 ++-- packages/kit/src/types/internal.d.ts | 2 +- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/packages/kit/src/core/sync/write_server.js b/packages/kit/src/core/sync/write_server.js index ebccac86cf49..539237bfa9a0 100644 --- a/packages/kit/src/core/sync/write_server.js +++ b/packages/kit/src/core/sync/write_server.js @@ -30,7 +30,7 @@ const server_template = ({ import root from '../root.${isSvelte5Plus() ? 'js' : 'svelte'}'; import { set_building, set_prerendering } from '__sveltekit/environment'; import { set_assets } from '__sveltekit/paths'; -import { set_read_asset } from '__sveltekit/server'; +import { set_read_implementation } from '__sveltekit/server'; import { set_private_env, set_public_env, set_safe_public_env } from '${runtime_directory}/shared-server.js'; export const options = { @@ -69,7 +69,7 @@ export async function get_hooks() { }; } -export { set_assets, set_building, set_prerendering, set_private_env, set_public_env, set_read_asset, set_safe_public_env }; +export { set_assets, set_building, set_prerendering, set_private_env, set_public_env, set_read_implementation, set_safe_public_env }; `; // TODO need to re-run this whenever src/app.html or src/error.html are diff --git a/packages/kit/src/exports/vite/index.js b/packages/kit/src/exports/vite/index.js index 8652397b5deb..f4eb14d97c96 100644 --- a/packages/kit/src/exports/vite/index.js +++ b/packages/kit/src/exports/vite/index.js @@ -496,12 +496,12 @@ async function kit({ svelte_config }) { case sveltekit_server: { return dedent` - export let read_asset = null; + export let read_implementation = null; export let manifest = null; - export function set_read_asset(fn) { - read_asset = fn; + export function set_read_implementation(fn) { + read_implementation = fn; } export function set_manifest(_) { diff --git a/packages/kit/src/runtime/app/server/index.js b/packages/kit/src/runtime/app/server/index.js index 8d85246f1e93..10a8894bf328 100644 --- a/packages/kit/src/runtime/app/server/index.js +++ b/packages/kit/src/runtime/app/server/index.js @@ -1,4 +1,4 @@ -import { read_asset, manifest } from '__sveltekit/server'; +import { read_implementation, manifest } from '__sveltekit/server'; import { base } from '__sveltekit/paths'; /** @@ -15,7 +15,7 @@ import { base } from '__sveltekit/paths'; * @returns {Response} */ export function read(asset) { - if (!read_asset) { + if (!read_implementation) { throw new Error( 'No `read` implementation was provided. Please ensure that your adapter is up to date and supports this feature' ); @@ -42,7 +42,7 @@ export function read(asset) { const length = manifest._.server_assets[file]; const type = manifest.mimeTypes[file.slice(file.lastIndexOf('.'))]; - return new Response(read_asset(file), { + return new Response(read_implementation(file), { headers: { 'Content-Length': '' + length, 'Content-Type': type diff --git a/packages/kit/src/runtime/server/index.js b/packages/kit/src/runtime/server/index.js index 5847f9dff80c..36cbd04be16f 100644 --- a/packages/kit/src/runtime/server/index.js +++ b/packages/kit/src/runtime/server/index.js @@ -4,7 +4,7 @@ import { options, get_hooks } from '__SERVER__/internal.js'; import { DEV } from 'esm-env'; import { filter_private_env, filter_public_env } from '../../utils/env.js'; import { prerendering } from '__sveltekit/environment'; -import { set_read_asset, set_manifest } from '__sveltekit/server'; +import { set_read_implementation, set_manifest } from '__sveltekit/server'; /** @type {ProxyHandler<{ type: 'public' | 'private' }>} */ const prerender_env_handler = { @@ -60,7 +60,7 @@ export class Server { set_safe_public_env(public_env); if (read) { - set_read_asset(read); + set_read_implementation(read); } if (!this.#options.hooks) { diff --git a/packages/kit/src/types/ambient-private.d.ts b/packages/kit/src/types/ambient-private.d.ts index 7b14645ab538..4f1491475355 100644 --- a/packages/kit/src/types/ambient-private.d.ts +++ b/packages/kit/src/types/ambient-private.d.ts @@ -22,7 +22,7 @@ declare module '__sveltekit/server' { import { SSRManifest } from '@sveltejs/kit'; export let manifest: SSRManifest; - export function read_asset(path: string): ReadableStream; + export function read_implementation(path: string): ReadableStream; export function set_manifest(manifest: SSRManifest): void; - export function set_read_asset(fn: (path: string) => ReadableStream): void; + export function set_read_implementation(fn: (path: string) => ReadableStream): void; } diff --git a/packages/kit/src/types/internal.d.ts b/packages/kit/src/types/internal.d.ts index cc05af7200d5..c6b80d851241 100644 --- a/packages/kit/src/types/internal.d.ts +++ b/packages/kit/src/types/internal.d.ts @@ -33,7 +33,7 @@ export interface ServerInternalModule { set_prerendering(): void; set_private_env(environment: Record): void; set_public_env(environment: Record): void; - set_read_asset(implementation: (path: string) => ReadableStream): void; + set_read_implementation(implementation: (path: string) => ReadableStream): void; set_safe_public_env(environment: Record): void; set_version(version: string): void; set_fix_stack_trace(fix_stack_trace: (error: unknown) => string): void; From b3dfa650ae11c255d47fa4dd838b9d8bb223c7aa Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 17 Jan 2024 11:32:53 -0500 Subject: [PATCH 22/57] add test --- .../basics/src/routes/read-file/+page.server.js | 15 +++++++++++++++ .../apps/basics/src/routes/read-file/+page.svelte | 6 ++++++ .../apps/basics/src/routes/read-file/auto.txt | 1 + .../test/apps/basics/src/routes/read-file/url.txt | 1 + packages/kit/test/apps/basics/test/test.js | 12 ++++++++++++ 5 files changed, 35 insertions(+) create mode 100644 packages/kit/test/apps/basics/src/routes/read-file/+page.server.js create mode 100644 packages/kit/test/apps/basics/src/routes/read-file/+page.svelte create mode 100644 packages/kit/test/apps/basics/src/routes/read-file/auto.txt create mode 100644 packages/kit/test/apps/basics/src/routes/read-file/url.txt diff --git a/packages/kit/test/apps/basics/src/routes/read-file/+page.server.js b/packages/kit/test/apps/basics/src/routes/read-file/+page.server.js new file mode 100644 index 000000000000..e6e59d503a8a --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/read-file/+page.server.js @@ -0,0 +1,15 @@ +import { dev } from '$app/environment'; +import { read } from '$app/server'; +import auto from './auto.txt'; +import url from './url.txt?url'; + +export async function load() { + if (!dev && !auto.startsWith('data:')) { + throw new Error('expected auto.txt to be inlined'); + } + + return { + auto: await read(auto).text(), + url: await read(url).text() + }; +} diff --git a/packages/kit/test/apps/basics/src/routes/read-file/+page.svelte b/packages/kit/test/apps/basics/src/routes/read-file/+page.svelte new file mode 100644 index 000000000000..a23efdda786c --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/read-file/+page.svelte @@ -0,0 +1,6 @@ + + +

{data.auto}

+

{data.url}

diff --git a/packages/kit/test/apps/basics/src/routes/read-file/auto.txt b/packages/kit/test/apps/basics/src/routes/read-file/auto.txt new file mode 100644 index 000000000000..f48c4ff3fa09 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/read-file/auto.txt @@ -0,0 +1 @@ +Imported without ?url diff --git a/packages/kit/test/apps/basics/src/routes/read-file/url.txt b/packages/kit/test/apps/basics/src/routes/read-file/url.txt new file mode 100644 index 000000000000..eeb75878cc2e --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/read-file/url.txt @@ -0,0 +1 @@ +Imported with ?url diff --git a/packages/kit/test/apps/basics/test/test.js b/packages/kit/test/apps/basics/test/test.js index ccfdd3d6ca73..e35a7e6f8cec 100644 --- a/packages/kit/test/apps/basics/test/test.js +++ b/packages/kit/test/apps/basics/test/test.js @@ -706,6 +706,18 @@ test.describe('$app/paths', () => { }); }); +test.describe('$app/server', () => { + test('can read a file', async ({ page }) => { + await page.goto('/read-file'); + + const auto = await page.textContent('[data-testid="auto"]'); + const url = await page.textContent('[data-testid="url"]'); + + expect(auto.trim()).toBe('Imported without ?url'); + expect(url.trim()).toBe('Imported with ?url'); + }); +}); + test.describe('$app/stores', () => { test('can access page.url', async ({ baseURL, page }) => { await page.goto('/origin'); From 38e1419218cdaf78b01709ada13a6fe9199cfacb Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 17 Jan 2024 11:34:36 -0500 Subject: [PATCH 23/57] Apply suggestions from code review Co-authored-by: Ben McCann <322311+benmccann@users.noreply.github.com> --- packages/kit/types/index.d.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/kit/types/index.d.ts b/packages/kit/types/index.d.ts index 3d728beea22f..4d9696a98175 100644 --- a/packages/kit/types/index.d.ts +++ b/packages/kit/types/index.d.ts @@ -80,7 +80,7 @@ declare module '@sveltejs/kit' { /** * Find all the assets imported by server files belonging to `routes` - * */ + */ findServerAssets(routes: RouteDefinition[]): string[]; /** @@ -1886,7 +1886,7 @@ declare module '@sveltejs/kit/node' { export function setResponse(res: import('http').ServerResponse, response: Response): Promise; /** * Converts a file on disk to a readable stream - * */ + */ export function createReadable(file: string): ReadableStream; } @@ -2133,7 +2133,7 @@ declare module '$app/server' { * const asset = read(somefile); * const text = await asset.text(); * ``` - * */ + */ export function read(asset: string): Response; } From 2c0f579918788126c35bbadaaa8f3581195b7e68 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 17 Jan 2024 11:36:20 -0500 Subject: [PATCH 24/57] improve searchability --- packages/kit/src/runtime/app/server/index.js | 2 +- packages/kit/types/index.d.ts | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/kit/src/runtime/app/server/index.js b/packages/kit/src/runtime/app/server/index.js index 10a8894bf328..29990bc1c910 100644 --- a/packages/kit/src/runtime/app/server/index.js +++ b/packages/kit/src/runtime/app/server/index.js @@ -2,7 +2,7 @@ import { read_implementation, manifest } from '__sveltekit/server'; import { base } from '__sveltekit/paths'; /** - * Read the contents of an imported asset + * Read the contents of an imported asset from the filesystem * @example * ```js * import { read } from '$app/server'; diff --git a/packages/kit/types/index.d.ts b/packages/kit/types/index.d.ts index 3d728beea22f..636ddc306c3f 100644 --- a/packages/kit/types/index.d.ts +++ b/packages/kit/types/index.d.ts @@ -71,6 +71,7 @@ declare module '@sveltejs/kit' { /** An array of all routes (including prerendered) */ routes: RouteDefinition[]; + // TODO 3.0 remove this method /** * Create separate functions that map to one or more routes of your app. * @param fn A function that groups a set of routes into an entry point @@ -2124,7 +2125,7 @@ declare module '$app/paths' { declare module '$app/server' { /** - * Read the contents of an imported asset + * Read the contents of an imported asset from the filesystem * @example * ```js * import { read } from '$app/server'; From 8ec59dfc99457671344fe77abc28aff70e66750f Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 17 Jan 2024 11:55:09 -0500 Subject: [PATCH 25/57] prevent $app/server being imported client-side --- packages/kit/src/exports/vite/graph_analysis/index.js | 8 ++++++-- packages/kit/src/exports/vite/index.js | 2 +- packages/kit/src/exports/vite/module_ids.js | 6 ++++++ 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/packages/kit/src/exports/vite/graph_analysis/index.js b/packages/kit/src/exports/vite/graph_analysis/index.js index 7ab7cd3cdfa2..a58cc7a72a2a 100644 --- a/packages/kit/src/exports/vite/graph_analysis/index.js +++ b/packages/kit/src/exports/vite/graph_analysis/index.js @@ -1,9 +1,9 @@ import path from 'node:path'; import { posixify } from '../../../utils/filesystem.js'; import { strip_virtual_prefix } from '../utils.js'; -import { env_dynamic_private, env_static_private } from '../module_ids.js'; +import { app_server, env_dynamic_private, env_static_private } from '../module_ids.js'; -const ILLEGAL_IMPORTS = new Set([env_dynamic_private, env_static_private]); +const ILLEGAL_IMPORTS = new Set([env_dynamic_private, env_static_private, app_server]); const ILLEGAL_MODULE_NAME_PATTERN = /.*\.server\..+/; /** @@ -101,5 +101,9 @@ export function normalize_id(id, lib, cwd) { id = path.relative(cwd, id); } + if (id === app_server) { + return '$app/server'; + } + return posixify(id); } diff --git a/packages/kit/src/exports/vite/index.js b/packages/kit/src/exports/vite/index.js index f4eb14d97c96..f339e63a8166 100644 --- a/packages/kit/src/exports/vite/index.js +++ b/packages/kit/src/exports/vite/index.js @@ -515,7 +515,7 @@ async function kit({ svelte_config }) { /** * Ensures that client-side code can't accidentally import server-side code, - * whether in `*.server.js` files, `$lib/server`, or `$env/[static|dynamic]/private` + * whether in `*.server.js` files, `$app/server`, `$lib/server`, or `$env/[static|dynamic]/private` * @type {import('vite').Plugin} */ const plugin_guard = { diff --git a/packages/kit/src/exports/vite/module_ids.js b/packages/kit/src/exports/vite/module_ids.js index 8944635d9cc7..3be16f68a71f 100644 --- a/packages/kit/src/exports/vite/module_ids.js +++ b/packages/kit/src/exports/vite/module_ids.js @@ -1,3 +1,5 @@ +import { fileURLToPath } from 'node:url'; + export const env_static_private = '\0virtual:$env/static/private'; export const env_static_public = '\0virtual:$env/static/public'; export const env_dynamic_private = '\0virtual:$env/dynamic/private'; @@ -8,3 +10,7 @@ export const service_worker = '\0virtual:$service-worker'; export const sveltekit_environment = '\0virtual:__sveltekit/environment'; export const sveltekit_paths = '\0virtual:__sveltekit/paths'; export const sveltekit_server = '\0virtual:__sveltekit/server'; + +export const app_server = fileURLToPath( + new URL('../../runtime/app/server/index.js', import.meta.url) +); From 2c4a2cc882ad5bc07b4d8fa760a2bcb1952cc907 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 17 Jan 2024 12:03:08 -0500 Subject: [PATCH 26/57] regenerate types --- packages/kit/types/index.d.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/kit/types/index.d.ts b/packages/kit/types/index.d.ts index eee89145435b..636ddc306c3f 100644 --- a/packages/kit/types/index.d.ts +++ b/packages/kit/types/index.d.ts @@ -81,7 +81,7 @@ declare module '@sveltejs/kit' { /** * Find all the assets imported by server files belonging to `routes` - */ + * */ findServerAssets(routes: RouteDefinition[]): string[]; /** @@ -1887,7 +1887,7 @@ declare module '@sveltejs/kit/node' { export function setResponse(res: import('http').ServerResponse, response: Response): Promise; /** * Converts a file on disk to a readable stream - */ + * */ export function createReadable(file: string): ReadableStream; } @@ -2134,7 +2134,7 @@ declare module '$app/server' { * const asset = read(somefile); * const text = await asset.text(); * ``` - */ + * */ export function read(asset: string): Response; } From a2c469c63b37d7eb745c37e58c877c466cebce4d Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 17 Jan 2024 15:28:52 -0500 Subject: [PATCH 27/57] add dev time feature tracking mechanism --- packages/adapter-vercel/index.js | 18 +++++++++- packages/kit/src/core/postbuild/analyse.js | 24 ++------------ packages/kit/src/exports/public.d.ts | 10 ++++++ packages/kit/src/exports/vite/dev/index.js | 33 ++++++++++++++++++- packages/kit/src/exports/vite/index.js | 2 +- packages/kit/src/runtime/app/server/index.js | 2 ++ packages/kit/src/runtime/server/page/index.js | 7 ++-- .../runtime/server/page/load_page_nodes.js | 11 +++++++ packages/kit/src/runtime/server/respond.js | 22 ++++++++++--- packages/kit/src/types/global-private.d.ts | 5 +++ packages/kit/src/types/internal.d.ts | 5 ++- packages/kit/src/utils/route_config.js | 20 +++++++++++ 12 files changed, 123 insertions(+), 36 deletions(-) create mode 100644 packages/kit/src/runtime/server/page/load_page_nodes.js create mode 100644 packages/kit/src/utils/route_config.js diff --git a/packages/adapter-vercel/index.js b/packages/adapter-vercel/index.js index 6cca046a098c..b168b6fd7b61 100644 --- a/packages/adapter-vercel/index.js +++ b/packages/adapter-vercel/index.js @@ -5,6 +5,7 @@ import { nodeFileTrace } from '@vercel/nft'; import esbuild from 'esbuild'; import { get_pathname } from './utils.js'; +const name = '@sveltejs/adapter-vercel'; const DEFAULT_FUNCTION_NAME = 'fn'; const get_default_runtime = () => { @@ -24,7 +25,7 @@ const plugin = function (defaults = {}) { } return { - name: '@sveltejs/adapter-vercel', + name, async adapt(builder) { if (!builder.routes) { @@ -340,6 +341,21 @@ const plugin = function (defaults = {}) { builder.log.minor('Writing routes...'); write(`${dir}/config.json`, JSON.stringify(static_config, null, '\t')); + }, + + supports: { + // reading from the filesystem only works in serverless functions + read: ({ config, route }) => { + const runtime = config.runtime ?? defaults.runtime; + + if (runtime === 'edge') { + throw new Error( + `${name}: Cannot use \`read\` from \`$app/server\` in a route configured with \`runtime: 'edge'\`` + ); + } + + return true; + } } }; }; diff --git a/packages/kit/src/core/postbuild/analyse.js b/packages/kit/src/core/postbuild/analyse.js index e4aac23ec08a..78465ed0feca 100644 --- a/packages/kit/src/core/postbuild/analyse.js +++ b/packages/kit/src/core/postbuild/analyse.js @@ -14,6 +14,7 @@ import { installPolyfills } from '../../exports/node/polyfills.js'; import { ENDPOINT_METHODS } from '../../constants.js'; import { filter_private_env, filter_public_env } from '../../utils/env.js'; import { resolve_route } from '../../utils/routing.js'; +import { get_page_config } from '../../utils/route_config.js'; export default forked(import.meta.url, analyse); @@ -163,30 +164,9 @@ function analyse_page(layouts, leaf) { validate_page_exports(leaf.universal, leaf.universal_id); return { - config: get_config([...layouts, leaf]), + config: get_page_config([...layouts, leaf]), entries: leaf.universal?.entries ?? leaf.server?.entries, methods, prerender: get_option([...layouts, leaf], 'prerender') ?? false }; } - -/** - * Do a shallow merge (first level) of the config object - * @param {Array} nodes - */ -function get_config(nodes) { - /** @type {any} */ - let current = {}; - - for (const node of nodes) { - if (!node?.universal?.config && !node?.server?.config) continue; - - current = { - ...current, - ...node?.universal?.config, - ...node?.server?.config - }; - } - - return Object.keys(current).length ? current : undefined; -} diff --git a/packages/kit/src/exports/public.d.ts b/packages/kit/src/exports/public.d.ts index 5e21a6ea672b..4ebe615fe628 100644 --- a/packages/kit/src/exports/public.d.ts +++ b/packages/kit/src/exports/public.d.ts @@ -35,6 +35,16 @@ export interface Adapter { * @param builder An object provided by SvelteKit that contains methods for adapting the app */ adapt(builder: Builder): MaybePromise; + /** + * Functions called during dev and build to determine whether specific features will work in production with this adapter + */ + supports?: { + /** + * Test support for `read` from `$app/server` + * @param config The merged route config + */ + read?: (details: { config: any; route: { id: string } }) => boolean; + }; } export type LoadProperties | void> = input extends void diff --git a/packages/kit/src/exports/vite/dev/index.js b/packages/kit/src/exports/vite/dev/index.js index 4e08a51c9079..042ae199c398 100644 --- a/packages/kit/src/exports/vite/dev/index.js +++ b/packages/kit/src/exports/vite/dev/index.js @@ -1,6 +1,7 @@ import fs from 'node:fs'; import path from 'node:path'; import { URL } from 'node:url'; +import { AsyncLocalStorage } from 'node:async_hooks'; import colors from 'kleur'; import sirv from 'sirv'; import { isCSSRequest, loadEnv, buildErrorMessage } from 'vite'; @@ -27,6 +28,33 @@ const cwd = process.cwd(); export async function dev(vite, vite_config, svelte_config) { installPolyfills(); + const async_local_storage = new AsyncLocalStorage(); + + globalThis.__SVELTEKIT_TRACK__ = (label) => { + switch (label) { + case '$app/server:read': { + const { event, config } = async_local_storage.getStore(); + if (!event) return; + + const { adapter } = svelte_config.kit; + if (!adapter) return; + + const supported = adapter.supports?.read?.({ + route: event.route, + config + }); + + if (!supported) { + throw new Error( + `Cannot use \`read\` from \`$app/server\` in ${event.route.id} when using ${adapter.name}. Upgrading may fix this error` + ); + } + + return; + } + } + }; + const fetch = globalThis.fetch; globalThis.fetch = (info, init) => { if (typeof info === 'string' && !SCHEME.test(info)) { @@ -515,7 +543,10 @@ 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) => fs.readFileSync(path.join(svelte_config.kit.files.assets, file)), + before_handle: (event, config) => { + async_local_storage.enterWith({ event, config }); + } }); if (rendered.status === 404) { diff --git a/packages/kit/src/exports/vite/index.js b/packages/kit/src/exports/vite/index.js index f339e63a8166..2e650435eeae 100644 --- a/packages/kit/src/exports/vite/index.js +++ b/packages/kit/src/exports/vite/index.js @@ -719,7 +719,7 @@ async function kit({ svelte_config }) { */ writeBundle: { sequential: true, - async handler(_options) { + async handler(_options, bundle) { if (secondary_build_started) return; // only run this once const verbose = vite_config.logLevel === 'info'; diff --git a/packages/kit/src/runtime/app/server/index.js b/packages/kit/src/runtime/app/server/index.js index 29990bc1c910..746001630f9b 100644 --- a/packages/kit/src/runtime/app/server/index.js +++ b/packages/kit/src/runtime/app/server/index.js @@ -15,6 +15,8 @@ import { base } from '__sveltekit/paths'; * @returns {Response} */ export function read(asset) { + __SVELTEKIT_TRACK__('$app/server:read'); + if (!read_implementation) { throw new Error( 'No `read` implementation was provided. Please ensure that your adapter is up to date and supports this feature' diff --git a/packages/kit/src/runtime/server/page/index.js b/packages/kit/src/runtime/server/page/index.js index f8878e112328..b90bf67eef58 100644 --- a/packages/kit/src/runtime/server/page/index.js +++ b/packages/kit/src/runtime/server/page/index.js @@ -15,6 +15,7 @@ import { render_response } from './render.js'; import { respond_with_error } from './respond_with_error.js'; import { get_option } from '../../../utils/options.js'; import { get_data_json } from '../data/index.js'; +import { load_page_nodes } from './load_page_nodes.js'; /** * The maximum request depth permitted before assuming we're stuck in an infinite loop @@ -44,11 +45,7 @@ export async function render_page(event, page, options, manifest, state, resolve } try { - const nodes = await Promise.all([ - // we use == here rather than === because [undefined] serializes as "[null]" - ...page.layouts.map((n) => (n == undefined ? n : manifest._.nodes[n]())), - manifest._.nodes[page.leaf]() - ]); + const nodes = await load_page_nodes(page, manifest); const leaf_node = /** @type {import('types').SSRNode} */ (nodes.at(-1)); diff --git a/packages/kit/src/runtime/server/page/load_page_nodes.js b/packages/kit/src/runtime/server/page/load_page_nodes.js new file mode 100644 index 000000000000..2af3f6e4c2c2 --- /dev/null +++ b/packages/kit/src/runtime/server/page/load_page_nodes.js @@ -0,0 +1,11 @@ +/** + * @param {import('types').PageNodeIndexes} page + * @param {import('@sveltejs/kit').SSRManifest} manifest + */ +export function load_page_nodes(page, manifest) { + return Promise.all([ + // we use == here rather than === because [undefined] serializes as "[null]" + ...page.layouts.map((n) => (n == undefined ? n : manifest._.nodes[n]())), + manifest._.nodes[page.leaf]() + ]); +} diff --git a/packages/kit/src/runtime/server/respond.js b/packages/kit/src/runtime/server/respond.js index f80eca2dea5f..f07f7cd8e4bf 100644 --- a/packages/kit/src/runtime/server/respond.js +++ b/packages/kit/src/runtime/server/respond.js @@ -31,6 +31,8 @@ import { json, text } from '../../exports/index.js'; import { action_json_redirect, is_action_json_request } from './page/actions.js'; import { INVALIDATED_PARAM, TRAILING_SLASH_PARAM } from '../shared.js'; import { get_public_env } from './env_module.js'; +import { load_page_nodes } from './page/load_page_nodes.js'; +import { get_page_config } from '../../utils/route_config.js'; /* global __SVELTEKIT_ADAPTER_NAME__ */ @@ -217,11 +219,7 @@ export async function respond(request, options, manifest, state) { if (url.pathname === base || url.pathname === base + '/') { trailing_slash = 'always'; } else if (route.page) { - const nodes = await Promise.all([ - // we use == here rather than === because [undefined] serializes as "[null]" - ...route.page.layouts.map((n) => (n == undefined ? n : manifest._.nodes[n]())), - manifest._.nodes[route.page.leaf]() - ]); + const nodes = await load_page_nodes(route.page, manifest); if (DEV) { const layouts = nodes.slice(0, -1); @@ -272,6 +270,20 @@ export async function respond(request, options, manifest, state) { }); } } + + if (DEV && state.before_handle) { + let config = {}; + + if (route.endpoint) { + const node = await route.endpoint(); + config = node.config; + } else if (route.page) { + const nodes = await load_page_nodes(route.page, manifest); + config = get_page_config(nodes); + } + + state.before_handle(event, config); + } } const { cookies, new_cookies, get_cookie_header, set_internal } = get_cookies( diff --git a/packages/kit/src/types/global-private.d.ts b/packages/kit/src/types/global-private.d.ts index 843cc94d342b..fc807b7b7dc0 100644 --- a/packages/kit/src/types/global-private.d.ts +++ b/packages/kit/src/types/global-private.d.ts @@ -4,6 +4,11 @@ declare global { const __SVELTEKIT_APP_VERSION_POLL_INTERVAL__: number; const __SVELTEKIT_DEV__: boolean; const __SVELTEKIT_EMBEDDED__: boolean; + /** + * This makes the use of specific features visible at both dev and build time, in such a + * way that we can error when they are not supported by the target platform + */ + var __SVELTEKIT_TRACK__: (label: string) => void; var Bun: object; var Deno: object; } diff --git a/packages/kit/src/types/internal.d.ts b/packages/kit/src/types/internal.d.ts index c6b80d851241..920c37918a37 100644 --- a/packages/kit/src/types/internal.d.ts +++ b/packages/kit/src/types/internal.d.ts @@ -13,7 +13,8 @@ import { HandleFetch, Actions, HandleClientError, - Reroute + Reroute, + RequestEvent } from '@sveltejs/kit'; import { HttpMethod, @@ -123,6 +124,7 @@ export class InternalServer extends Server { options: RequestOptions & { prerendering?: PrerenderOptions; read: (file: string) => Buffer; + before_handle?: (event: RequestEvent, config: any) => void; } ): Promise; } @@ -407,6 +409,7 @@ export interface SSRState { */ prerender_default?: PrerenderOption; read?: (file: string) => Buffer; + before_handle?: (event: RequestEvent, config: any) => void; } export type StrictBody = string | ArrayBufferView; diff --git a/packages/kit/src/utils/route_config.js b/packages/kit/src/utils/route_config.js new file mode 100644 index 000000000000..30563018c1d5 --- /dev/null +++ b/packages/kit/src/utils/route_config.js @@ -0,0 +1,20 @@ +/** + * Do a shallow merge (first level) of the config object + * @param {Array} nodes + */ +export function get_page_config(nodes) { + /** @type {any} */ + let current = {}; + + for (const node of nodes) { + if (!node?.universal?.config && !node?.server?.config) continue; + + current = { + ...current, + ...node?.universal?.config, + ...node?.server?.config + }; + } + + return Object.keys(current).length ? current : undefined; +} From e338d78a7c757135e8758d8de9c18291e97aec1a Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 17 Jan 2024 16:31:23 -0500 Subject: [PATCH 28/57] test feature support at build time --- packages/kit/src/core/postbuild/analyse.js | 57 +++++++++++++++++++++- packages/kit/src/exports/vite/dev/index.js | 25 ++-------- packages/kit/src/exports/vite/index.js | 20 +++++++- packages/kit/src/utils/features.js | 24 +++++++++ 4 files changed, 102 insertions(+), 24 deletions(-) create mode 100644 packages/kit/src/utils/features.js diff --git a/packages/kit/src/core/postbuild/analyse.js b/packages/kit/src/core/postbuild/analyse.js index 78465ed0feca..7ec2c7aafb83 100644 --- a/packages/kit/src/core/postbuild/analyse.js +++ b/packages/kit/src/core/postbuild/analyse.js @@ -15,16 +15,20 @@ import { ENDPOINT_METHODS } from '../../constants.js'; import { filter_private_env, filter_public_env } from '../../utils/env.js'; import { resolve_route } from '../../utils/routing.js'; import { get_page_config } from '../../utils/route_config.js'; +import { check_feature } from '../../utils/features.js'; export default forked(import.meta.url, analyse); /** * @param {{ * manifest_path: string; + * manifest_data: import('types').ManifestData; + * server_manifest: import('vite').Manifest; + * tracked_features: Record; * env: Record * }} opts */ -async function analyse({ manifest_path, env }) { +async function analyse({ manifest_path, manifest_data, server_manifest, tracked_features, env }) { /** @type {import('@sveltejs/kit').SSRManifest} */ const manifest = (await import(pathToFileURL(manifest_path).href)).manifest; @@ -90,12 +94,18 @@ async function analyse({ manifest_path, env }) { } } + const route_config = page?.config ?? endpoint?.config; + + for (const feature of list_features(route, manifest_data, server_manifest, tracked_features)) { + check_feature(route.id, route_config, feature, config.adapter); + } + const page_methods = page?.methods ?? []; const api_methods = endpoint?.methods ?? []; const entries = page?.entries ?? endpoint?.entries; metadata.routes.set(route.id, { - config: page?.config ?? endpoint?.config, + config: route_config, methods: Array.from(new Set([...page_methods, ...api_methods])), page: { methods: page_methods @@ -170,3 +180,46 @@ function analyse_page(layouts, leaf) { prerender: get_option([...layouts, leaf], 'prerender') ?? false }; } + +/** + * @param {import('types').SSRRoute} route + * @param {import('types').ManifestData} manifest_data + * @param {import('vite').Manifest} server_manifest + * @param {Record} tracked_features + */ +function list_features(route, manifest_data, server_manifest, tracked_features) { + const features = new Set(); + + const route_data = /** @type {import('types').RouteData} */ ( + manifest_data.routes.find((r) => r.id === route.id) + ); + + /** @param {string} id */ + function visit(id) { + const chunk = server_manifest[id]; + + if (chunk.file in tracked_features) { + for (const feature of tracked_features[chunk.file]) { + features.add(feature); + } + } + + if (chunk.imports) { + for (const id of chunk.imports) { + visit(id); + } + } + } + + let page_node = route_data?.leaf; + while (page_node) { + if (page_node.server) visit(page_node.server); + page_node = page_node.parent ?? null; + } + + if (route_data.endpoint) { + visit(route_data.endpoint.file); + } + + return Array.from(features); +} diff --git a/packages/kit/src/exports/vite/dev/index.js b/packages/kit/src/exports/vite/dev/index.js index 042ae199c398..6e2f68584af7 100644 --- a/packages/kit/src/exports/vite/dev/index.js +++ b/packages/kit/src/exports/vite/dev/index.js @@ -16,6 +16,7 @@ import { get_mime_lookup, runtime_base } from '../../../core/utils.js'; import { compact } from '../../../utils/array.js'; import { not_found } from '../utils.js'; import { SCHEME } from '../../../utils/url.js'; +import { check_feature } from '../../../utils/features.js'; const cwd = process.cwd(); @@ -31,28 +32,10 @@ export async function dev(vite, vite_config, svelte_config) { const async_local_storage = new AsyncLocalStorage(); globalThis.__SVELTEKIT_TRACK__ = (label) => { - switch (label) { - case '$app/server:read': { - const { event, config } = async_local_storage.getStore(); - if (!event) return; + const { event, config } = async_local_storage.getStore(); + if (!event) return; - const { adapter } = svelte_config.kit; - if (!adapter) return; - - const supported = adapter.supports?.read?.({ - route: event.route, - config - }); - - if (!supported) { - throw new Error( - `Cannot use \`read\` from \`$app/server\` in ${event.route.id} when using ${adapter.name}. Upgrading may fix this error` - ); - } - - return; - } - } + check_feature(event.route.id, config, label, svelte_config.kit.adapter); }; const fetch = globalThis.fetch; diff --git a/packages/kit/src/exports/vite/index.js b/packages/kit/src/exports/vite/index.js index 2e650435eeae..84fc1ab74b1d 100644 --- a/packages/kit/src/exports/vite/index.js +++ b/packages/kit/src/exports/vite/index.js @@ -225,6 +225,9 @@ async function kit({ svelte_config }) { const service_worker_entry_file = resolve_entry(kit.files.serviceWorker); const parsed_service_worker = path.parse(kit.files.serviceWorker); + /** @type {Record} */ + const tracked_features = {}; + const sourcemapIgnoreList = /** @param {string} relative_path */ (relative_path) => relative_path.includes('node_modules') || relative_path.includes(kit.outDir); @@ -702,6 +705,18 @@ async function kit({ svelte_config }) { } }, + renderChunk(code, chunk) { + if (code.includes('__SVELTEKIT_TRACK__')) { + return { + code: code.replace(/__SVELTEKIT_TRACK__\('(.+?)'\)/g, (m, label) => { + (tracked_features[chunk.name + '.js'] ??= []).push(label); + return `/* track ${label} */`; + }), + map: null // TODO we may need to generate a sourcemap in future + }; + } + }, + generateBundle() { if (vite_config.build.ssr) return; @@ -719,7 +734,7 @@ async function kit({ svelte_config }) { */ writeBundle: { sequential: true, - async handler(_options, bundle) { + async handler(_options) { if (secondary_build_started) return; // only run this once const verbose = vite_config.logLevel === 'info'; @@ -756,6 +771,9 @@ async function kit({ svelte_config }) { const metadata = await analyse({ manifest_path, + manifest_data, + server_manifest, + tracked_features, env: { ...env.private, ...env.public } }); diff --git a/packages/kit/src/utils/features.js b/packages/kit/src/utils/features.js new file mode 100644 index 000000000000..137c246cc706 --- /dev/null +++ b/packages/kit/src/utils/features.js @@ -0,0 +1,24 @@ +/** + * @param {string} route_id + * @param {any} config + * @param {string} feature + * @param {import('@sveltejs/kit').Adapter | undefined} adapter + */ +export function check_feature(route_id, config, feature, adapter) { + if (!adapter) return; + + switch (feature) { + case '$app/server:read': { + const supported = adapter.supports?.read?.({ + route: { id: route_id }, + config + }); + + if (!supported) { + throw new Error( + `Cannot use \`read\` from \`$app/server\` in ${route_id} when using ${adapter.name}. Upgrading may fix this error` + ); + } + } + } +} From 12c0e464e3068e1051244fc0d263eb00b6fba322 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 17 Jan 2024 16:33:39 -0500 Subject: [PATCH 29/57] lint --- packages/adapter-vercel/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/adapter-vercel/index.js b/packages/adapter-vercel/index.js index b168b6fd7b61..d2d8c9f3f26c 100644 --- a/packages/adapter-vercel/index.js +++ b/packages/adapter-vercel/index.js @@ -350,7 +350,7 @@ const plugin = function (defaults = {}) { if (runtime === 'edge') { throw new Error( - `${name}: Cannot use \`read\` from \`$app/server\` in a route configured with \`runtime: 'edge'\`` + `${name}: Cannot use \`read\` from \`$app/server\` in route \`${route.id}\` configured with \`runtime: 'edge'\`` ); } From 55c96378e13a9f1df3f593753fef546a13cf2cc0 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 17 Jan 2024 16:34:04 -0500 Subject: [PATCH 30/57] lint --- packages/kit/src/exports/vite/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/kit/src/exports/vite/index.js b/packages/kit/src/exports/vite/index.js index 84fc1ab74b1d..3a1f3657f8e2 100644 --- a/packages/kit/src/exports/vite/index.js +++ b/packages/kit/src/exports/vite/index.js @@ -708,7 +708,7 @@ async function kit({ svelte_config }) { renderChunk(code, chunk) { if (code.includes('__SVELTEKIT_TRACK__')) { return { - code: code.replace(/__SVELTEKIT_TRACK__\('(.+?)'\)/g, (m, label) => { + code: code.replace(/__SVELTEKIT_TRACK__\('(.+?)'\)/g, (_, label) => { (tracked_features[chunk.name + '.js'] ??= []).push(label); return `/* track ${label} */`; }), From 2447b56ab608e6a33e0728bc8ab2a22556376f84 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 17 Jan 2024 16:48:13 -0500 Subject: [PATCH 31/57] regenerate types --- packages/kit/types/index.d.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/packages/kit/types/index.d.ts b/packages/kit/types/index.d.ts index 636ddc306c3f..9afb40d50446 100644 --- a/packages/kit/types/index.d.ts +++ b/packages/kit/types/index.d.ts @@ -17,6 +17,16 @@ declare module '@sveltejs/kit' { * @param builder An object provided by SvelteKit that contains methods for adapting the app */ adapt(builder: Builder): MaybePromise; + /** + * Functions called during dev and build to determine whether specific features will work in production with this adapter + */ + supports?: { + /** + * Test support for `read` from `$app/server` + * @param config The merged route config + */ + read?: (details: { config: any; route: { id: string } }) => boolean; + }; } export type LoadProperties | void> = input extends void From 75f22a35e93434daef16af467ce0a1116381bc55 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 17 Jan 2024 17:46:30 -0500 Subject: [PATCH 32/57] account for hooks.server.js, mostly --- .../generate_manifest/find_server_assets.js | 4 ++++ packages/kit/src/core/postbuild/analyse.js | 6 ++++++ .../core/sync/create_manifest_data/index.js | 20 ++++++++++++++++++- packages/kit/src/types/internal.d.ts | 5 +++++ 4 files changed, 34 insertions(+), 1 deletion(-) diff --git a/packages/kit/src/core/generate_manifest/find_server_assets.js b/packages/kit/src/core/generate_manifest/find_server_assets.js index bce8b7cf182a..02a2cc672dda 100644 --- a/packages/kit/src/core/generate_manifest/find_server_assets.js +++ b/packages/kit/src/core/generate_manifest/find_server_assets.js @@ -43,5 +43,9 @@ export function find_server_assets(build_data, routes) { if (node?.server) add_assets(node.server); } + if (build_data.manifest_data.hooks.server) { + add_assets(build_data.manifest_data.hooks.server); + } + return Array.from(server_assets); } diff --git a/packages/kit/src/core/postbuild/analyse.js b/packages/kit/src/core/postbuild/analyse.js index 7ec2c7aafb83..947663bf9ccd 100644 --- a/packages/kit/src/core/postbuild/analyse.js +++ b/packages/kit/src/core/postbuild/analyse.js @@ -221,5 +221,11 @@ function list_features(route, manifest_data, server_manifest, tracked_features) visit(route_data.endpoint.file); } + if (manifest_data.hooks.server) { + // TODO if hooks.server.js imports `read`, it will be in the entry chunk + // we don't currently account for that case + visit(manifest_data.hooks.server); + } + return Array.from(features); } diff --git a/packages/kit/src/core/sync/create_manifest_data/index.js b/packages/kit/src/core/sync/create_manifest_data/index.js index 5d72686db746..ab807f9f2f50 100644 --- a/packages/kit/src/core/sync/create_manifest_data/index.js +++ b/packages/kit/src/core/sync/create_manifest_data/index.js @@ -3,7 +3,7 @@ import path from 'node:path'; import colors from 'kleur'; import { lookup } from 'mrmime'; import { list_files, runtime_directory } from '../../utils.js'; -import { posixify } from '../../../utils/filesystem.js'; +import { posixify, resolve_entry } from '../../../utils/filesystem.js'; import { parse_route_id } from '../../../utils/routing.js'; import { sort_routes } from './sort.js'; @@ -22,6 +22,7 @@ export default function create_manifest_data({ cwd = process.cwd() }) { const assets = create_assets(config); + const hooks = create_hooks(config, cwd); const matchers = create_matchers(config, cwd); const { nodes, routes } = create_routes_and_nodes(cwd, config, fallback); @@ -35,6 +36,7 @@ export default function create_manifest_data({ return { assets, + hooks, matchers, nodes, routes @@ -52,6 +54,22 @@ export function create_assets(config) { })); } +/** + * @param {import('types').ValidatedConfig} config + * @param {string} cwd + */ +function create_hooks(config, cwd) { + const client = resolve_entry(config.kit.files.hooks.client); + const server = resolve_entry(config.kit.files.hooks.server); + const universal = resolve_entry(config.kit.files.hooks.universal); + + return { + client: client && posixify(path.relative(cwd, client)), + server: server && posixify(path.relative(cwd, server)), + universal: universal && posixify(path.relative(cwd, universal)) + }; +} + /** * @param {import('types').ValidatedConfig} config * @param {string} cwd diff --git a/packages/kit/src/types/internal.d.ts b/packages/kit/src/types/internal.d.ts index 920c37918a37..1454b8084ce0 100644 --- a/packages/kit/src/types/internal.d.ts +++ b/packages/kit/src/types/internal.d.ts @@ -131,6 +131,11 @@ export class InternalServer extends Server { export interface ManifestData { assets: Asset[]; + hooks: { + client: string | null; + server: string | null; + universal: string | null; + }; nodes: PageNode[]; routes: RouteData[]; matchers: Record; From 12b91b557ad809ab3827e49139bb02629d39c500 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 17 Jan 2024 17:48:24 -0500 Subject: [PATCH 33/57] regenerate types --- packages/kit/types/index.d.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/kit/types/index.d.ts b/packages/kit/types/index.d.ts index 9afb40d50446..8cb3e87d2220 100644 --- a/packages/kit/types/index.d.ts +++ b/packages/kit/types/index.d.ts @@ -1585,6 +1585,11 @@ declare module '@sveltejs/kit' { interface ManifestData { assets: Asset[]; + hooks: { + client: string | null; + server: string | null; + universal: string | null; + }; nodes: PageNode[]; routes: RouteData[]; matchers: Record; From 501491f7c35f750577b33ffe7520aef3c37d65ef Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 17 Jan 2024 23:24:38 -0500 Subject: [PATCH 34/57] fix --- .../kit/src/core/generate_manifest/find_server_assets.js | 8 +++++--- packages/kit/src/core/postbuild/analyse.js | 1 + 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/kit/src/core/generate_manifest/find_server_assets.js b/packages/kit/src/core/generate_manifest/find_server_assets.js index 02a2cc672dda..abffc3cc87c1 100644 --- a/packages/kit/src/core/generate_manifest/find_server_assets.js +++ b/packages/kit/src/core/generate_manifest/find_server_assets.js @@ -20,9 +20,11 @@ export function find_server_assets(build_data, routes) { /** @param {string} id */ function add_assets(id) { - const deps = find_deps(build_data.server_manifest, id, false); - for (const asset of deps.assets) { - server_assets.add(asset); + if (id in build_data.server_manifest) { + const deps = find_deps(build_data.server_manifest, id, false); + for (const asset of deps.assets) { + server_assets.add(asset); + } } } diff --git a/packages/kit/src/core/postbuild/analyse.js b/packages/kit/src/core/postbuild/analyse.js index 947663bf9ccd..1c272ec2c2d3 100644 --- a/packages/kit/src/core/postbuild/analyse.js +++ b/packages/kit/src/core/postbuild/analyse.js @@ -197,6 +197,7 @@ function list_features(route, manifest_data, server_manifest, tracked_features) /** @param {string} id */ function visit(id) { const chunk = server_manifest[id]; + if (!chunk) return; if (chunk.file in tracked_features) { for (const feature of tracked_features[chunk.file]) { From 5c140b3a445001baed071ed1e928d2106a02f57d Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 17 Jan 2024 23:30:32 -0500 Subject: [PATCH 35/57] bump peerdeps, add changesets --- .changeset/rude-apples-jam.md | 7 +++++++ .changeset/tasty-masks-talk.md | 5 +++++ .changeset/tiny-maps-chew.md | 7 +++++++ packages/adapter-netlify/index.js | 16 +++++++++++++++- packages/adapter-netlify/package.json | 2 +- packages/adapter-node/index.js | 4 ++++ packages/adapter-node/package.json | 2 +- packages/adapter-vercel/package.json | 2 +- 8 files changed, 41 insertions(+), 4 deletions(-) create mode 100644 .changeset/rude-apples-jam.md create mode 100644 .changeset/tasty-masks-talk.md create mode 100644 .changeset/tiny-maps-chew.md diff --git a/.changeset/rude-apples-jam.md b/.changeset/rude-apples-jam.md new file mode 100644 index 000000000000..57dfa17ca204 --- /dev/null +++ b/.changeset/rude-apples-jam.md @@ -0,0 +1,7 @@ +--- +'@sveltejs/adapter-netlify': major +'@sveltejs/adapter-vercel': major +'@sveltejs/adapter-node': major +--- + +breaking: update peer dependency on `@sveltejs/kit` diff --git a/.changeset/tasty-masks-talk.md b/.changeset/tasty-masks-talk.md new file mode 100644 index 000000000000..a7e80644963d --- /dev/null +++ b/.changeset/tasty-masks-talk.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': minor +--- + +feat: add `$app/server` module with `read` function for reading assets from filesystem diff --git a/.changeset/tiny-maps-chew.md b/.changeset/tiny-maps-chew.md new file mode 100644 index 000000000000..597bbff3abc5 --- /dev/null +++ b/.changeset/tiny-maps-chew.md @@ -0,0 +1,7 @@ +--- +'@sveltejs/adapter-netlify': minor +'@sveltejs/adapter-vercel': minor +'@sveltejs/adapter-node': minor +--- + +feat: support `read` from `$app/server` diff --git a/packages/adapter-netlify/index.js b/packages/adapter-netlify/index.js index fafcecfea75a..767644efd892 100644 --- a/packages/adapter-netlify/index.js +++ b/packages/adapter-netlify/index.js @@ -27,6 +27,7 @@ import toml from '@iarna/toml'; * }} HandlerManifest */ +const name = '@sveltejs/adapter-netlify'; const files = fileURLToPath(new URL('./files', import.meta.url).href); const edge_set_in_env_var = @@ -38,7 +39,7 @@ const FUNCTION_PREFIX = 'sveltekit-'; /** @type {import('./index.js').default} */ export default function ({ split = false, edge = edge_set_in_env_var } = {}) { return { - name: '@sveltejs/adapter-netlify', + name, async adapt(builder) { if (!builder.routes) { @@ -92,6 +93,19 @@ export default function ({ split = false, edge = edge_set_in_env_var } = {}) { } else { await generate_lambda_functions({ builder, split, publish }); } + }, + + supports: { + // reading from the filesystem only works in serverless functions + read: ({ route }) => { + if (edge) { + throw new Error( + `${name}: Cannot use \`read\` from \`$app/server\` in route \`${route.id}\` when using edge functions` + ); + } + + return true; + } } }; } diff --git a/packages/adapter-netlify/package.json b/packages/adapter-netlify/package.json index 61ddc8af4897..f67ba70972f5 100644 --- a/packages/adapter-netlify/package.json +++ b/packages/adapter-netlify/package.json @@ -51,6 +51,6 @@ "vitest": "^1.2.0" }, "peerDependencies": { - "@sveltejs/kit": "^2.0.0" + "@sveltejs/kit": "^2.4.0" } } diff --git a/packages/adapter-node/index.js b/packages/adapter-node/index.js index a1077b2f8073..2f9ab8ed441f 100644 --- a/packages/adapter-node/index.js +++ b/packages/adapter-node/index.js @@ -89,6 +89,10 @@ export default function (opts = {}) { ENV_PREFIX: JSON.stringify(envPrefix) } }); + }, + + supports: { + read: () => true } }; } diff --git a/packages/adapter-node/package.json b/packages/adapter-node/package.json index 91aa8eced7cd..895f3146f916 100644 --- a/packages/adapter-node/package.json +++ b/packages/adapter-node/package.json @@ -50,6 +50,6 @@ "rollup": "^4.9.5" }, "peerDependencies": { - "@sveltejs/kit": "^2.0.0" + "@sveltejs/kit": "^2.4.0" } } diff --git a/packages/adapter-vercel/package.json b/packages/adapter-vercel/package.json index b172039a51c1..c35b8bc999f7 100644 --- a/packages/adapter-vercel/package.json +++ b/packages/adapter-vercel/package.json @@ -42,6 +42,6 @@ "vitest": "^1.2.0" }, "peerDependencies": { - "@sveltejs/kit": "^2.0.0" + "@sveltejs/kit": "^2.4.0" } } From 2f2bccc0601a9a6e5f6a2f8b23dc8a60dc8e3b27 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 17 Jan 2024 23:31:36 -0500 Subject: [PATCH 36/57] createReadable -> createReadableStream --- packages/adapter-netlify/src/serverless.js | 4 ++-- packages/adapter-node/src/handler.js | 4 ++-- packages/adapter-vercel/files/serverless.js | 4 ++-- packages/kit/src/exports/node/index.js | 2 +- packages/kit/src/exports/vite/dev/index.js | 4 ++-- packages/kit/src/exports/vite/preview/index.js | 4 ++-- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/adapter-netlify/src/serverless.js b/packages/adapter-netlify/src/serverless.js index ef35768bd9f0..e14c8189dee4 100644 --- a/packages/adapter-netlify/src/serverless.js +++ b/packages/adapter-netlify/src/serverless.js @@ -1,7 +1,7 @@ import './shims'; import { Server } from '0SERVER'; import { split_headers } from './headers.js'; -import { createReadable } from '@sveltejs/kit/node'; +import { createReadableStream } from '@sveltejs/kit/node'; /** * @param {import('@sveltejs/kit').SSRManifest} manifest @@ -12,7 +12,7 @@ export function init(manifest) { let init_promise = server.init({ env: process.env, - read: (file) => createReadable(`.netlify/server/${file}`) + read: (file) => createReadableStream(`.netlify/server/${file}`) }); return async (event, context) => { diff --git a/packages/adapter-node/src/handler.js b/packages/adapter-node/src/handler.js index ada79c1e42ba..d73b1c874831 100644 --- a/packages/adapter-node/src/handler.js +++ b/packages/adapter-node/src/handler.js @@ -4,7 +4,7 @@ import path from 'node:path'; import sirv from 'sirv'; import { fileURLToPath } from 'node:url'; import { parse as polka_url_parser } from '@polka/url'; -import { getRequest, setResponse, createReadable } from '@sveltejs/kit/node'; +import { getRequest, setResponse, createReadableStream } from '@sveltejs/kit/node'; import { Server } from 'SERVER'; import { manifest, prerendered, base } from 'MANIFEST'; import { env } from 'ENV'; @@ -33,7 +33,7 @@ const asset_dir = `${dir}/client${base}`; await server.init({ env: process.env, - read: (file) => createReadable(`${asset_dir}/${file}`) + read: (file) => createReadableStream(`${asset_dir}/${file}`) }); /** diff --git a/packages/adapter-vercel/files/serverless.js b/packages/adapter-vercel/files/serverless.js index 9fadc3d42c46..1a76ba8a6f29 100644 --- a/packages/adapter-vercel/files/serverless.js +++ b/packages/adapter-vercel/files/serverless.js @@ -1,5 +1,5 @@ import { installPolyfills } from '@sveltejs/kit/node/polyfills'; -import { getRequest, setResponse, createReadable } from '@sveltejs/kit/node'; +import { getRequest, setResponse, createReadableStream } from '@sveltejs/kit/node'; import { Server } from 'SERVER'; import { manifest } from 'MANIFEST'; @@ -9,7 +9,7 @@ const server = new Server(manifest); await server.init({ env: /** @type {Record} */ (process.env), - read: createReadable + read: createReadableStream }); const DATA_SUFFIX = '/__data.json'; diff --git a/packages/kit/src/exports/node/index.js b/packages/kit/src/exports/node/index.js index 6bf5943bc91a..26c0f406bebd 100644 --- a/packages/kit/src/exports/node/index.js +++ b/packages/kit/src/exports/node/index.js @@ -197,6 +197,6 @@ export async function setResponse(res, response) { * @param {string} file * @returns {ReadableStream} */ -export function createReadable(file) { +export function createReadableStream(file) { return /** @type {ReadableStream} */ (Readable.toWeb(createReadStream(file))); } diff --git a/packages/kit/src/exports/vite/dev/index.js b/packages/kit/src/exports/vite/dev/index.js index 6e2f68584af7..52ef48648eb2 100644 --- a/packages/kit/src/exports/vite/dev/index.js +++ b/packages/kit/src/exports/vite/dev/index.js @@ -5,7 +5,7 @@ import { AsyncLocalStorage } from 'node:async_hooks'; import colors from 'kleur'; import sirv from 'sirv'; import { isCSSRequest, loadEnv, buildErrorMessage } from 'vite'; -import { createReadable, getRequest, setResponse } from '../../../exports/node/index.js'; +import { createReadableStream, getRequest, setResponse } from '../../../exports/node/index.js'; import { installPolyfills } from '../../../exports/node/polyfills.js'; import { coalesce_to_error } from '../../../utils/error.js'; import { posixify, resolve_entry, to_fs } from '../../../utils/filesystem.js'; @@ -490,7 +490,7 @@ export async function dev(vite, vite_config, svelte_config) { await server.init({ env, - read: createReadable + read: createReadableStream }); const request = await getRequest({ diff --git a/packages/kit/src/exports/vite/preview/index.js b/packages/kit/src/exports/vite/preview/index.js index c9550e08dbf1..c34af3121e1d 100644 --- a/packages/kit/src/exports/vite/preview/index.js +++ b/packages/kit/src/exports/vite/preview/index.js @@ -4,7 +4,7 @@ import { pathToFileURL } from 'node:url'; import { lookup } from 'mrmime'; import sirv from 'sirv'; import { loadEnv, normalizePath } from 'vite'; -import { createReadable, getRequest, setResponse } from '../../../exports/node/index.js'; +import { createReadableStream, getRequest, setResponse } from '../../../exports/node/index.js'; import { installPolyfills } from '../../../exports/node/polyfills.js'; import { SVELTE_KIT_ASSETS } from '../../../constants.js'; import { not_found } from '../utils.js'; @@ -48,7 +48,7 @@ export async function preview(vite, vite_config, svelte_config) { const server = new Server(manifest); await server.init({ env: loadEnv(vite_config.mode, svelte_config.kit.env.dir, ''), - read: (file) => createReadable(`${dir}/${file}`) + read: (file) => createReadableStream(`${dir}/${file}`) }); return () => { From 73bd6823d7054c779e4705bd0568f2cd8e04ba2d Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 18 Jan 2024 10:13:37 -0500 Subject: [PATCH 37/57] Apply suggestions from code review Co-authored-by: Ben McCann <322311+benmccann@users.noreply.github.com> --- packages/kit/src/exports/public.d.ts | 3 +-- packages/kit/types/index.d.ts | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/kit/src/exports/public.d.ts b/packages/kit/src/exports/public.d.ts index 4ebe615fe628..6b21eaae8e46 100644 --- a/packages/kit/src/exports/public.d.ts +++ b/packages/kit/src/exports/public.d.ts @@ -36,7 +36,7 @@ export interface Adapter { */ adapt(builder: Builder): MaybePromise; /** - * Functions called during dev and build to determine whether specific features will work in production with this adapter + * Checks called during dev and build to determine whether specific features will work in production with this adapter */ supports?: { /** @@ -109,7 +109,6 @@ export interface Builder { /** * Find all the assets imported by server files belonging to `routes` - * @param routes */ findServerAssets(routes: RouteDefinition[]): string[]; diff --git a/packages/kit/types/index.d.ts b/packages/kit/types/index.d.ts index 8cb3e87d2220..2a7acf68e392 100644 --- a/packages/kit/types/index.d.ts +++ b/packages/kit/types/index.d.ts @@ -1903,7 +1903,7 @@ declare module '@sveltejs/kit/node' { /** * Converts a file on disk to a readable stream * */ - export function createReadable(file: string): ReadableStream; + export function createReadableStream(file: string): ReadableStream; } declare module '@sveltejs/kit/node/polyfills' { From 39d4e6d85731080fd8d1f5eed7f3df56dd26f215 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 18 Jan 2024 11:01:52 -0500 Subject: [PATCH 38/57] remove unnecessary if --- packages/adapter-vercel/index.js | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/packages/adapter-vercel/index.js b/packages/adapter-vercel/index.js index d2d8c9f3f26c..683df1757073 100644 --- a/packages/adapter-vercel/index.js +++ b/packages/adapter-vercel/index.js @@ -82,12 +82,9 @@ const plugin = function (defaults = {}) { await create_function_bundle(builder, `${tmp}/index.js`, dir, config); - // TODO remove `if` once peer dep contains a minimum SvelteKit version that has the method - if (builder.findServerAssets) { - for (const asset of builder.findServerAssets(routes)) { - // TODO use symlinks, once Build Output API supports doing so - builder.copy(`${builder.getServerDirectory()}/${asset}`, `${dir}/${asset}`); - } + for (const asset of builder.findServerAssets(routes)) { + // TODO use symlinks, once Build Output API supports doing so + builder.copy(`${builder.getServerDirectory()}/${asset}`, `${dir}/${asset}`); } } From 3353c1f4341b82dd2cedbdc8bb9f0cd3f086585f Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 18 Jan 2024 11:03:58 -0500 Subject: [PATCH 39/57] replace docs for find_server_assets --- packages/kit/src/core/generate_manifest/find_server_assets.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/kit/src/core/generate_manifest/find_server_assets.js b/packages/kit/src/core/generate_manifest/find_server_assets.js index abffc3cc87c1..3a562f4b53a0 100644 --- a/packages/kit/src/core/generate_manifest/find_server_assets.js +++ b/packages/kit/src/core/generate_manifest/find_server_assets.js @@ -1,8 +1,7 @@ import { find_deps } from '../../exports/vite/build/utils.js'; /** - * Generates the data used to write the server-side manifest.js file. This data is used in the Vite - * build process, to power routing, etc. + * Finds all the assets that are imported by server files associated with `routes` * @param {import('types').BuildData} build_data * @param {import('types').RouteData[]} routes */ From 8409574d459d257af7517f737ee2a61618838e09 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 18 Jan 2024 11:06:35 -0500 Subject: [PATCH 40/57] update adapter author docs --- .../docs/25-build-and-deploy/99-writing-adapters.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/documentation/docs/25-build-and-deploy/99-writing-adapters.md b/documentation/docs/25-build-and-deploy/99-writing-adapters.md index 451d474ef5a9..ba4a5126e557 100644 --- a/documentation/docs/25-build-and-deploy/99-writing-adapters.md +++ b/documentation/docs/25-build-and-deploy/99-writing-adapters.md @@ -19,6 +19,13 @@ export default function (options) { name: 'adapter-package-name', async adapt(builder) { // adapter implementation + }, + supports: { + read: ({ config, route }) => { + // return `true` if the route with the given `config` can use `read` + // from `$app/server` in production, return false if it can't. + // or throw a descriptive error describing how to configure the deployment + } } }; From f8df6cb47e34ba584c46be2ffe9626a9677e9f16 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 18 Jan 2024 11:08:16 -0500 Subject: [PATCH 41/57] regenerate types --- packages/kit/types/index.d.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/kit/types/index.d.ts b/packages/kit/types/index.d.ts index 2a7acf68e392..ce72a7fe5269 100644 --- a/packages/kit/types/index.d.ts +++ b/packages/kit/types/index.d.ts @@ -18,7 +18,7 @@ declare module '@sveltejs/kit' { */ adapt(builder: Builder): MaybePromise; /** - * Functions called during dev and build to determine whether specific features will work in production with this adapter + * Checks called during dev and build to determine whether specific features will work in production with this adapter */ supports?: { /** @@ -91,7 +91,7 @@ declare module '@sveltejs/kit' { /** * Find all the assets imported by server files belonging to `routes` - * */ + */ findServerAssets(routes: RouteDefinition[]): string[]; /** From aea29926a98f742485637d89a027ed9af10fa29e Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 18 Jan 2024 11:14:53 -0500 Subject: [PATCH 42/57] explain what __SVELTEKIT_TRACK__ does --- packages/kit/src/types/global-private.d.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/kit/src/types/global-private.d.ts b/packages/kit/src/types/global-private.d.ts index fc807b7b7dc0..66e1d41edd1f 100644 --- a/packages/kit/src/types/global-private.d.ts +++ b/packages/kit/src/types/global-private.d.ts @@ -6,7 +6,17 @@ declare global { const __SVELTEKIT_EMBEDDED__: boolean; /** * This makes the use of specific features visible at both dev and build time, in such a - * way that we can error when they are not supported by the target platform + * way that we can error when they are not supported by the target platform. + * + * During dev, `globalThis.__SVELTEKIT_TRACK__` is a function that grabs the current `event` + * and route `config` (from an AsyncLocalStorage instance) and calls the relevant `supports` + * function on the adapter (e.g. `adapter.supports.read(...)`). + * + * At build time, if the function containing the `__SVELTEKIT_TRACK__` call is untreeshaken, + * we locate it in the `renderChunk` build hook and a) make a note of the chunk that contains + * it and b) replace it with a comment. Later, we can use this information to establish + * which routes use which feature, and use the same `adapter.supports.read(...)` function + * to throw an error if the feature would fail in production. */ var __SVELTEKIT_TRACK__: (label: string) => void; var Bun: object; From 2b44e599b32b323cebf97c01963283b8b23cd4a9 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 18 Jan 2024 11:17:33 -0500 Subject: [PATCH 43/57] mention `$app/server` on server-only modules page --- documentation/docs/30-advanced/50-server-only-modules.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/documentation/docs/30-advanced/50-server-only-modules.md b/documentation/docs/30-advanced/50-server-only-modules.md index 78f3a75da261..22e141d489d4 100644 --- a/documentation/docs/30-advanced/50-server-only-modules.md +++ b/documentation/docs/30-advanced/50-server-only-modules.md @@ -8,6 +8,10 @@ Like a good friend, SvelteKit keeps your secrets. When writing your backend and The `$env/static/private` and `$env/dynamic/private` modules, which are covered in the [modules](modules) section, can only be imported into modules that only run on the server, such as [`hooks.server.js`](hooks#server-hooks) or [`+page.server.js`](routing#page-page-server-js). +## Server-only utilities + +The [`$app/server`](/docs/modules#$app-server) module, which contains a `read` function for reading assets from the filesystem, can likewise only be imported by code that runs on the server. + ## Your modules You can make your own modules server-only in two ways: From 2e38c71362c0f40824062d58cb20c695168ef99e Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 18 Jan 2024 11:19:09 -0500 Subject: [PATCH 44/57] minor details --- documentation/docs/25-build-and-deploy/99-writing-adapters.md | 1 + packages/kit/src/exports/node/index.js | 1 + packages/kit/src/runtime/app/server/index.js | 1 + 3 files changed, 3 insertions(+) diff --git a/documentation/docs/25-build-and-deploy/99-writing-adapters.md b/documentation/docs/25-build-and-deploy/99-writing-adapters.md index ba4a5126e557..fc7a2f4fbe73 100644 --- a/documentation/docs/25-build-and-deploy/99-writing-adapters.md +++ b/documentation/docs/25-build-and-deploy/99-writing-adapters.md @@ -7,6 +7,7 @@ If an adapter for your preferred environment doesn't yet exist, you can build yo Adapters packages must implement the following API, which creates an `Adapter`: ```js +// @errors: 2322 // @filename: ambient.d.ts type AdapterSpecificOptions = any; diff --git a/packages/kit/src/exports/node/index.js b/packages/kit/src/exports/node/index.js index 26c0f406bebd..f38a09358084 100644 --- a/packages/kit/src/exports/node/index.js +++ b/packages/kit/src/exports/node/index.js @@ -196,6 +196,7 @@ export async function setResponse(res, response) { * Converts a file on disk to a readable stream * @param {string} file * @returns {ReadableStream} + * @since 2.4.0 */ export function createReadableStream(file) { return /** @type {ReadableStream} */ (Readable.toWeb(createReadStream(file))); diff --git a/packages/kit/src/runtime/app/server/index.js b/packages/kit/src/runtime/app/server/index.js index 746001630f9b..308c83a0ab57 100644 --- a/packages/kit/src/runtime/app/server/index.js +++ b/packages/kit/src/runtime/app/server/index.js @@ -13,6 +13,7 @@ import { base } from '__sveltekit/paths'; * ``` * @param {string} asset * @returns {Response} + * @since 2.4.0 */ export function read(asset) { __SVELTEKIT_TRACK__('$app/server:read'); From 36a39fb71b686fbd49abd0bc59d167bfac87567b Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 18 Jan 2024 11:24:46 -0500 Subject: [PATCH 45/57] oh ffs --- packages/kit/types/index.d.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/kit/types/index.d.ts b/packages/kit/types/index.d.ts index ce72a7fe5269..231e2ea8ee23 100644 --- a/packages/kit/types/index.d.ts +++ b/packages/kit/types/index.d.ts @@ -1902,7 +1902,8 @@ declare module '@sveltejs/kit/node' { export function setResponse(res: import('http').ServerResponse, response: Response): Promise; /** * Converts a file on disk to a readable stream - * */ + * @since 2.4.0 + */ export function createReadableStream(file: string): ReadableStream; } @@ -2149,7 +2150,8 @@ declare module '$app/server' { * const asset = read(somefile); * const text = await asset.text(); * ``` - * */ + * @since 2.4.0 + */ export function read(asset: string): Response; } From 2bd0faa10651c8de6a7fdcc5f375e4d4bfea4500 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 18 Jan 2024 12:03:13 -0500 Subject: [PATCH 46/57] exclude prerendered routes from feature detection, handle /@fs assets in dev --- packages/kit/src/exports/vite/dev/index.js | 14 +++++++------- packages/kit/src/runtime/app/server/index.js | 3 ++- packages/kit/src/runtime/server/respond.js | 9 +++++++-- packages/kit/src/types/internal.d.ts | 4 ++-- 4 files changed, 18 insertions(+), 12 deletions(-) diff --git a/packages/kit/src/exports/vite/dev/index.js b/packages/kit/src/exports/vite/dev/index.js index 52ef48648eb2..c9be8e2847ed 100644 --- a/packages/kit/src/exports/vite/dev/index.js +++ b/packages/kit/src/exports/vite/dev/index.js @@ -32,8 +32,8 @@ export async function dev(vite, vite_config, svelte_config) { const async_local_storage = new AsyncLocalStorage(); globalThis.__SVELTEKIT_TRACK__ = (label) => { - const { event, config } = async_local_storage.getStore(); - if (!event) return; + const { event, config, prerender } = async_local_storage.getStore(); + if (!event || prerender === true) return; check_feature(event.route.id, config, label, svelte_config.kit.adapter); }; @@ -137,8 +137,8 @@ export async function dev(vite, vite_config, svelte_config) { server_assets: new Proxy( {}, { - has: (_, /** @type {string} */ file) => fs.existsSync(file), - get: (_, /** @type {string} */ file) => fs.statSync(file).size + has: (_, /** @type {string} */ file) => fs.existsSync(file.replace(/^\/@fs/, '')), + get: (_, /** @type {string} */ file) => fs.statSync(file.replace(/^\/@fs/, '')).size } ), nodes: manifest_data.nodes.map((node, index) => { @@ -490,7 +490,7 @@ export async function dev(vite, vite_config, svelte_config) { await server.init({ env, - read: createReadableStream + read: (file) => createReadableStream(file.replace(/^\/@fs/, '')) }); const request = await getRequest({ @@ -527,8 +527,8 @@ export async function dev(vite, vite_config, svelte_config) { throw new Error('Could not determine clientAddress'); }, read: (file) => fs.readFileSync(path.join(svelte_config.kit.files.assets, file)), - before_handle: (event, config) => { - async_local_storage.enterWith({ event, config }); + before_handle: (event, config, prerender) => { + async_local_storage.enterWith({ event, config, prerender }); } }); diff --git a/packages/kit/src/runtime/app/server/index.js b/packages/kit/src/runtime/app/server/index.js index 308c83a0ab57..9865bd8cb4dd 100644 --- a/packages/kit/src/runtime/app/server/index.js +++ b/packages/kit/src/runtime/app/server/index.js @@ -1,5 +1,6 @@ import { read_implementation, manifest } from '__sveltekit/server'; import { base } from '__sveltekit/paths'; +import { DEV } from 'esm-env'; /** * Read the contents of an imported asset from the filesystem @@ -39,7 +40,7 @@ export function read(asset) { }); } - const file = asset.slice(base.length + 1); + const file = DEV && asset.startsWith('/@fs') ? asset : asset.slice(base.length + 1); if (file in manifest._.server_assets) { const length = manifest._.server_assets[file]; diff --git a/packages/kit/src/runtime/server/respond.js b/packages/kit/src/runtime/server/respond.js index f07f7cd8e4bf..bf59ba4cfd30 100644 --- a/packages/kit/src/runtime/server/respond.js +++ b/packages/kit/src/runtime/server/respond.js @@ -274,15 +274,20 @@ export async function respond(request, options, manifest, state) { if (DEV && state.before_handle) { let config = {}; + /** @type {import('types').PrerenderOption} */ + let prerender = false; + if (route.endpoint) { const node = await route.endpoint(); - config = node.config; + config = node.config ?? config; + prerender = node.prerender ?? prerender; } else if (route.page) { const nodes = await load_page_nodes(route.page, manifest); config = get_page_config(nodes); + prerender = get_option(nodes, 'prerender') ?? false; } - state.before_handle(event, config); + state.before_handle(event, config, prerender); } } diff --git a/packages/kit/src/types/internal.d.ts b/packages/kit/src/types/internal.d.ts index 1454b8084ce0..26aa392935eb 100644 --- a/packages/kit/src/types/internal.d.ts +++ b/packages/kit/src/types/internal.d.ts @@ -124,7 +124,7 @@ export class InternalServer extends Server { options: RequestOptions & { prerendering?: PrerenderOptions; read: (file: string) => Buffer; - before_handle?: (event: RequestEvent, config: any) => void; + before_handle?: (event: RequestEvent, config: any, prerender: PrerenderOption) => void; } ): Promise; } @@ -414,7 +414,7 @@ export interface SSRState { */ prerender_default?: PrerenderOption; read?: (file: string) => Buffer; - before_handle?: (event: RequestEvent, config: any) => void; + before_handle?: (event: RequestEvent, config: any, prerender: PrerenderOption) => void; } export type StrictBody = string | ArrayBufferView; From 501fa0ebb57830785a2c06c41058665dbc2179c9 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 18 Jan 2024 12:04:52 -0500 Subject: [PATCH 47/57] use read to populate content.json --- .../src/routes/content.json/content.server.js | 23 ++++++++----------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/sites/kit.svelte.dev/src/routes/content.json/content.server.js b/sites/kit.svelte.dev/src/routes/content.json/content.server.js index b24e7ea2ae50..3a7169e973b1 100644 --- a/sites/kit.svelte.dev/src/routes/content.json/content.server.js +++ b/sites/kit.svelte.dev/src/routes/content.json/content.server.js @@ -1,3 +1,4 @@ +import { read } from '$app/server'; import { modules } from '$lib/generated/type-info.js'; import { extractFrontmatter, @@ -5,9 +6,6 @@ import { replaceExportTypePlaceholders, slugify } from '@sveltejs/site-kit/markdown'; -import { readFile } from 'node:fs/promises'; -import path from 'node:path'; -import glob from 'tiny-glob'; const categories = [ { @@ -18,6 +16,11 @@ const categories = [ } ]; +const files = import.meta.glob('../../../../../documentation/*/*/*.md', { + as: 'url', + eager: true +}); + export async function content() { /** @type {import('@sveltejs/site-kit/search').Block[]} */ const blocks = []; @@ -25,18 +28,10 @@ export async function content() { for (const category of categories) { const breadcrumbs = category.label ? [category.label] : []; - for (const file of await glob('**/*.md', { cwd: `../../documentation/${category.slug}` })) { - const basename = path.basename(file); - const match = /\d{2}-(.+)\.md/.exec(basename); - if (!match) continue; - - const slug = match[1]; + for (const [file, asset] of Object.entries(files)) { + const slug = /\/\d{2}-(.+)\.md$/.exec(file)[1]; - const filepath = `../../documentation/${category.slug}/${file}`; - const markdown = await replaceExportTypePlaceholders( - await readFile(filepath, 'utf-8'), - modules - ); + const markdown = await replaceExportTypePlaceholders(await read(asset).text(), modules); const { body, metadata } = extractFrontmatter(markdown); From 1a35f7f2cd17b419b892636200407dea01724588 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 18 Jan 2024 12:12:36 -0500 Subject: [PATCH 48/57] fix prerendering --- packages/kit/src/core/postbuild/analyse.js | 16 ++++++++++++---- packages/kit/src/core/postbuild/prerender.js | 6 +++++- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/packages/kit/src/core/postbuild/analyse.js b/packages/kit/src/core/postbuild/analyse.js index 1c272ec2c2d3..f3d46e160ebd 100644 --- a/packages/kit/src/core/postbuild/analyse.js +++ b/packages/kit/src/core/postbuild/analyse.js @@ -95,9 +95,17 @@ async function analyse({ manifest_path, manifest_data, server_manifest, tracked_ } const route_config = page?.config ?? endpoint?.config; - - for (const feature of list_features(route, manifest_data, server_manifest, tracked_features)) { - check_feature(route.id, route_config, feature, config.adapter); + const prerender = page?.prerender ?? endpoint?.prerender; + + if (prerender !== true) { + for (const feature of list_features( + route, + manifest_data, + server_manifest, + tracked_features + )) { + check_feature(route.id, route_config, feature, config.adapter); + } } const page_methods = page?.methods ?? []; @@ -113,7 +121,7 @@ async function analyse({ manifest_path, manifest_data, server_manifest, tracked_ api: { methods: api_methods }, - prerender: page?.prerender ?? endpoint?.prerender, + prerender, entries: entries && (await entries()).map((entry_object) => resolve_route(route.id, entry_object)) }); diff --git a/packages/kit/src/core/postbuild/prerender.js b/packages/kit/src/core/postbuild/prerender.js index 4cf3053fe2bb..c29ff168f562 100644 --- a/packages/kit/src/core/postbuild/prerender.js +++ b/packages/kit/src/core/postbuild/prerender.js @@ -12,6 +12,7 @@ import { queue } from './queue.js'; import { crawl } from './crawl.js'; import { forked } from '../../utils/fork.js'; import * as devalue from 'devalue'; +import { createReadableStream } from '@sveltejs/kit/node'; export default forked(import.meta.url, prerender); @@ -430,7 +431,10 @@ async function prerender({ out, manifest_path, metadata, verbose, env }) { log.info('Prerendering'); const server = new Server(manifest); - await server.init({ env }); + await server.init({ + env, + read: (file) => createReadableStream(`${config.outDir}/output/server/${file}`) + }); for (const entry of config.prerender.entries) { if (entry === '*') { From 10be71ff123996b1b0e9ffd93368656a72046537 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 18 Jan 2024 12:15:11 -0500 Subject: [PATCH 49/57] simplify --- sites/kit.svelte.dev/src/constants.js | 6 ------ sites/kit.svelte.dev/src/lib/server/docs/index.js | 5 +++-- 2 files changed, 3 insertions(+), 8 deletions(-) delete mode 100644 sites/kit.svelte.dev/src/constants.js diff --git a/sites/kit.svelte.dev/src/constants.js b/sites/kit.svelte.dev/src/constants.js deleted file mode 100644 index a50a135a6641..000000000000 --- a/sites/kit.svelte.dev/src/constants.js +++ /dev/null @@ -1,6 +0,0 @@ -export const CONTENT_BASE = '../../documentation'; - -/** All the paths are relative to the project root when being run on server or built */ -export const CONTENT_BASE_PATHS = { - DOCS: `${CONTENT_BASE}/docs` -}; diff --git a/sites/kit.svelte.dev/src/lib/server/docs/index.js b/sites/kit.svelte.dev/src/lib/server/docs/index.js index d78eba9b767b..0c7bc167615f 100644 --- a/sites/kit.svelte.dev/src/lib/server/docs/index.js +++ b/sites/kit.svelte.dev/src/lib/server/docs/index.js @@ -9,7 +9,6 @@ import { replaceExportTypePlaceholders } from '@sveltejs/site-kit/markdown'; import { readFile, readdir } from 'node:fs/promises'; -import { CONTENT_BASE_PATHS } from '../../../constants.js'; import { render_content } from '../renderer'; /** @@ -29,8 +28,10 @@ export async function get_parsed_docs(docs_data, slug) { } } +const base = '../../documentation/docs'; + /** @return {Promise} */ -export async function get_docs_data(base = CONTENT_BASE_PATHS.DOCS) { +export async function get_docs_data() { /** @type {import('./types.js').DocsData} */ const docs_data = []; From 984e9b4977f094466091bc54ed235a0fc9cc1869 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 18 Jan 2024 14:33:50 -0500 Subject: [PATCH 50/57] simplify docs logic --- packages/kit/src/core/postbuild/analyse.js | 3 + packages/kit/src/core/sync/write_server.js | 4 +- packages/kit/src/exports/vite/dev/index.js | 6 +- packages/kit/src/types/internal.d.ts | 4 +- sites/kit.svelte.dev/package.json | 1 - .../src/lib/server/docs/index.js | 140 ++++++++---------- .../src/routes/+layout.server.js | 2 +- .../src/routes/docs/+layout.server.js | 4 +- .../src/routes/docs/[slug]/+page.server.js | 9 +- .../src/routes/nav.json/+server.js | 27 ++-- 10 files changed, 88 insertions(+), 112 deletions(-) diff --git a/packages/kit/src/core/postbuild/analyse.js b/packages/kit/src/core/postbuild/analyse.js index f3d46e160ebd..ea2c0637dd2d 100644 --- a/packages/kit/src/core/postbuild/analyse.js +++ b/packages/kit/src/core/postbuild/analyse.js @@ -16,6 +16,7 @@ import { filter_private_env, filter_public_env } from '../../utils/env.js'; import { resolve_route } from '../../utils/routing.js'; import { get_page_config } from '../../utils/route_config.js'; import { check_feature } from '../../utils/features.js'; +import { createReadableStream } from '@sveltejs/kit/node'; export default forked(import.meta.url, analyse); @@ -53,6 +54,8 @@ async function analyse({ manifest_path, manifest_data, server_manifest, tracked_ internal.set_private_env(private_env); internal.set_public_env(public_env); internal.set_safe_public_env(public_env); + internal.set_manifest(manifest); + internal.set_read_implementation((file) => createReadableStream(`${server_root}/server/${file}`)); /** @type {import('types').ServerMetadata} */ const metadata = { diff --git a/packages/kit/src/core/sync/write_server.js b/packages/kit/src/core/sync/write_server.js index 539237bfa9a0..3aa65f9ebe0b 100644 --- a/packages/kit/src/core/sync/write_server.js +++ b/packages/kit/src/core/sync/write_server.js @@ -30,7 +30,7 @@ const server_template = ({ import root from '../root.${isSvelte5Plus() ? 'js' : 'svelte'}'; import { set_building, set_prerendering } from '__sveltekit/environment'; import { set_assets } from '__sveltekit/paths'; -import { set_read_implementation } from '__sveltekit/server'; +import { set_manifest, set_read_implementation } from '__sveltekit/server'; import { set_private_env, set_public_env, set_safe_public_env } from '${runtime_directory}/shared-server.js'; export const options = { @@ -69,7 +69,7 @@ export async function get_hooks() { }; } -export { set_assets, set_building, set_prerendering, set_private_env, set_public_env, set_read_implementation, set_safe_public_env }; +export { set_assets, set_building, set_manifest, set_prerendering, set_private_env, set_public_env, set_read_implementation, set_safe_public_env }; `; // TODO need to re-run this whenever src/app.html or src/error.html are diff --git a/packages/kit/src/exports/vite/dev/index.js b/packages/kit/src/exports/vite/dev/index.js index c9be8e2847ed..cfe846dbe326 100644 --- a/packages/kit/src/exports/vite/dev/index.js +++ b/packages/kit/src/exports/vite/dev/index.js @@ -32,10 +32,10 @@ export async function dev(vite, vite_config, svelte_config) { const async_local_storage = new AsyncLocalStorage(); globalThis.__SVELTEKIT_TRACK__ = (label) => { - const { event, config, prerender } = async_local_storage.getStore(); - if (!event || prerender === true) return; + const context = async_local_storage.getStore(); + if (!context || context.prerender === true) return; - check_feature(event.route.id, config, label, svelte_config.kit.adapter); + check_feature(context.event.route.id, context.config, label, svelte_config.kit.adapter); }; const fetch = globalThis.fetch; diff --git a/packages/kit/src/types/internal.d.ts b/packages/kit/src/types/internal.d.ts index 26aa392935eb..02a11e894723 100644 --- a/packages/kit/src/types/internal.d.ts +++ b/packages/kit/src/types/internal.d.ts @@ -14,7 +14,8 @@ import { Actions, HandleClientError, Reroute, - RequestEvent + RequestEvent, + SSRManifest } from '@sveltejs/kit'; import { HttpMethod, @@ -31,6 +32,7 @@ export interface ServerModule { export interface ServerInternalModule { set_assets(path: string): void; set_building(): void; + set_manifest(manifest: SSRManifest): void; set_prerendering(): void; set_private_env(environment: Record): void; set_public_env(environment: Record): void; diff --git a/sites/kit.svelte.dev/package.json b/sites/kit.svelte.dev/package.json index fa9fc16254b7..c782f65b42f0 100644 --- a/sites/kit.svelte.dev/package.json +++ b/sites/kit.svelte.dev/package.json @@ -31,7 +31,6 @@ "prismjs": "^1.29.0", "shiki-twoslash": "^3.1.2", "svelte": "^4.2.8", - "tiny-glob": "^0.2.9", "typescript": "5.0.4", "vite": "^5.0.11", "vitest": "^1.2.0" diff --git a/sites/kit.svelte.dev/src/lib/server/docs/index.js b/sites/kit.svelte.dev/src/lib/server/docs/index.js index 0c7bc167615f..e72118556f1c 100644 --- a/sites/kit.svelte.dev/src/lib/server/docs/index.js +++ b/sites/kit.svelte.dev/src/lib/server/docs/index.js @@ -1,4 +1,4 @@ -import { base as app_base } from '$app/paths'; +import { base } from '$app/paths'; import { modules } from '$lib/generated/type-info.js'; import { escape, @@ -8,96 +8,78 @@ import { removeMarkdown, replaceExportTypePlaceholders } from '@sveltejs/site-kit/markdown'; -import { readFile, readdir } from 'node:fs/promises'; import { render_content } from '../renderer'; +import { read } from '$app/server'; +import { error } from '@sveltejs/kit'; -/** - * @param {import('./types.js').DocsData} docs_data - * @param {string} slug - */ -export async function get_parsed_docs(docs_data, slug) { - for (const { pages } of docs_data) { - for (const page of pages) { - if (page.slug === slug) { - return { - ...page, - content: await render_content(page.file, page.content) - }; - } - } - } -} - -const base = '../../documentation/docs'; - -/** @return {Promise} */ -export async function get_docs_data() { - /** @type {import('./types.js').DocsData} */ - const docs_data = []; - - for (const category_dir of await readdir(base)) { - const match = /\d{2}-(.+)/.exec(category_dir); - if (!match) continue; - - const category_slug = match[1]; +const meta = import.meta.glob('../../../../../../documentation/docs/*/meta.json', { + as: 'url', + eager: true +}); - // Read the meta.json - const { title: category_title, draft = 'false' } = JSON.parse( - await readFile(`${base}/${category_dir}/meta.json`, 'utf-8') - ); +const markdown = import.meta.glob('../../../../../../documentation/docs/*/*.md', { + as: 'url', + eager: true +}); - if (draft === 'true') continue; +export const categories = {}; +export const pages = {}; - /** @type {import('./types.js').Category} */ - const category = { - title: category_title, - slug: category_slug, - pages: [] - }; +for (const [file, asset] of Object.entries(meta)) { + const slug = /\/\d{2}-(.+)\/meta\.json$/.exec(file)[1]; - for (const page_md of (await readdir(`${base}/${category_dir}`)).filter( - (filename) => filename !== 'meta.json' - )) { - const match = /\d{2}-(.+)/.exec(page_md); - if (!match) continue; + const { title, draft } = await read(asset).json(); - const page_slug = match[1].replace('.md', ''); + if (draft) continue; - const page_data = extractFrontmatter( - await readFile(`${base}/${category_dir}/${page_md}`, 'utf-8') - ); - - if (page_data.metadata.draft === 'true') continue; - - const page_title = page_data.metadata.title; - const page_content = page_data.body; - - category.pages.push({ - title: page_title, - slug: page_slug, - content: page_content, - category: category_title, - sections: await get_sections(page_content), - path: `${app_base}/docs/${page_slug}`, - file: `${category_dir}/${page_md}` - }); - } + categories[slug] = { + title, + pages: [] + }; +} - docs_data.push(category); - } +for (const [file, asset] of Object.entries(markdown)) { + const [, category_dir, basename] = /\/(\d{2}-.+?)\/(\d{2}-.+\.md)$/.exec(file); + const category_slug = category_dir.slice(3); + const slug = basename.slice(3, -3); // strip the number prefix and .md suffix + + const { + metadata: { draft, title }, + body + } = extractFrontmatter(await read(asset).text()); + + // skip draft pages/categories + if (draft === 'true' && !(category_slug in categories)) continue; + + categories[category_slug].pages.push({ + title, + path: `${base}/docs/${slug}` + }); + + pages[slug] = { + title, + file: `${category_dir}/${basename}`, + sections: await get_sections(body), + body + }; +} - return docs_data; +/** @param {string} slug */ +export async function get_parsed_docs(slug) { + const page = pages[slug]; + if (!page) error(404); + + // TODO this should probably use a type from site-kit + return { + title: page.title, + file: page.file, + sections: page.sections, + content: await render_content(page.file, page.body) + }; } -/** @param {import('./types.js').DocsData} docs_data */ -export function get_docs_list(docs_data) { - return docs_data.map((category) => ({ - title: category.title, - pages: category.pages.map((page) => ({ - title: page.title, - path: page.path - })) - })); +export function get_docs_list() { + return Object.values(categories); } /** @param {string} markdown */ diff --git a/sites/kit.svelte.dev/src/routes/+layout.server.js b/sites/kit.svelte.dev/src/routes/+layout.server.js index 274c04f7f124..8479779fcf81 100644 --- a/sites/kit.svelte.dev/src/routes/+layout.server.js +++ b/sites/kit.svelte.dev/src/routes/+layout.server.js @@ -3,7 +3,7 @@ import { fetchBanner } from '@sveltejs/site-kit/components'; export const prerender = true; export const load = async ({ url, fetch }) => { - const nav_links = fetch('/nav.json').then((r) => r.json()); + const nav_links = fetch('/nav.json').then((r) => r.json()); // TODO why is this behind a `fetch`? does `nav.json` need to be exposed publicly? const banner = fetchBanner('kit.svelte.dev', fetch); return { diff --git a/sites/kit.svelte.dev/src/routes/docs/+layout.server.js b/sites/kit.svelte.dev/src/routes/docs/+layout.server.js index 2e3ec09c82c9..77f6b8ffcfb1 100644 --- a/sites/kit.svelte.dev/src/routes/docs/+layout.server.js +++ b/sites/kit.svelte.dev/src/routes/docs/+layout.server.js @@ -1,9 +1,9 @@ -import { get_docs_data, get_docs_list } from '$lib/server/docs/index.js'; +import { get_docs_list } from '$lib/server/docs/index.js'; export const prerender = true; export async function load() { return { - sections: get_docs_list(await get_docs_data()) + sections: get_docs_list() }; } diff --git a/sites/kit.svelte.dev/src/routes/docs/[slug]/+page.server.js b/sites/kit.svelte.dev/src/routes/docs/[slug]/+page.server.js index fb156a880399..5d0fdb672a75 100644 --- a/sites/kit.svelte.dev/src/routes/docs/[slug]/+page.server.js +++ b/sites/kit.svelte.dev/src/routes/docs/[slug]/+page.server.js @@ -1,12 +1,7 @@ -import { get_docs_data, get_parsed_docs } from '$lib/server/docs/index.js'; -import { error } from '@sveltejs/kit'; +import { get_parsed_docs } from '$lib/server/docs/index.js'; export const prerender = true; export async function load({ params }) { - const processed_page = await get_parsed_docs(await get_docs_data(), params.slug); - - if (!processed_page) error(404); - - return { page: processed_page }; + return { page: await get_parsed_docs(params.slug) }; } diff --git a/sites/kit.svelte.dev/src/routes/nav.json/+server.js b/sites/kit.svelte.dev/src/routes/nav.json/+server.js index f4752b31a056..c6f78ca01153 100644 --- a/sites/kit.svelte.dev/src/routes/nav.json/+server.js +++ b/sites/kit.svelte.dev/src/routes/nav.json/+server.js @@ -1,23 +1,13 @@ -import { get_docs_data, get_docs_list } from '$lib/server/docs/index.js'; +import { get_docs_list } from '$lib/server/docs/index.js'; import { json } from '@sveltejs/kit'; export const prerender = true; export const GET = async () => { - return json(await get_nav_list()); -}; - -/** - * @returns {Promise} - */ -async function get_nav_list() { - const docs_list = get_docs_list(await get_docs_data()); - const processed_docs_list = docs_list.map(({ title, pages }) => ({ - title, - sections: pages.map(({ title, path }) => ({ title, path })) - })); + const docs_list = get_docs_list(); - return [ + /** @type {import('@sveltejs/site-kit').NavigationLink[]} */ + const nav_list = [ { title: 'Docs', prefix: 'docs', @@ -25,9 +15,14 @@ async function get_nav_list() { sections: [ { title: 'DOCS', - sections: processed_docs_list + sections: docs_list.map(({ title, pages }) => ({ + title, + sections: pages.map(({ title, path }) => ({ title, path })) + })) } ] } ]; -} + + return json(nav_list); +}; From 0eebe9ff8a48517058fbbfc7339798e5bf63e24b Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 18 Jan 2024 14:38:14 -0500 Subject: [PATCH 51/57] fix --- sites/kit.svelte.dev/src/lib/server/docs/index.js | 10 +++++++--- sites/kit.svelte.dev/src/routes/docs/+layout.svelte | 3 +-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/sites/kit.svelte.dev/src/lib/server/docs/index.js b/sites/kit.svelte.dev/src/lib/server/docs/index.js index e72118556f1c..030b2361350e 100644 --- a/sites/kit.svelte.dev/src/lib/server/docs/index.js +++ b/sites/kit.svelte.dev/src/lib/server/docs/index.js @@ -43,20 +43,23 @@ for (const [file, asset] of Object.entries(markdown)) { const category_slug = category_dir.slice(3); const slug = basename.slice(3, -3); // strip the number prefix and .md suffix + const category = categories[category_slug]; + if (!category) continue; // draft + const { metadata: { draft, title }, body } = extractFrontmatter(await read(asset).text()); - // skip draft pages/categories - if (draft === 'true' && !(category_slug in categories)) continue; + if (draft === 'true') continue; - categories[category_slug].pages.push({ + category.pages.push({ title, path: `${base}/docs/${slug}` }); pages[slug] = { + category: category.title, title, file: `${category_dir}/${basename}`, sections: await get_sections(body), @@ -71,6 +74,7 @@ export async function get_parsed_docs(slug) { // TODO this should probably use a type from site-kit return { + category: page.category, title: page.title, file: page.file, sections: page.sections, diff --git a/sites/kit.svelte.dev/src/routes/docs/+layout.svelte b/sites/kit.svelte.dev/src/routes/docs/+layout.svelte index 2b328c7fad91..d95c0a861249 100644 --- a/sites/kit.svelte.dev/src/routes/docs/+layout.svelte +++ b/sites/kit.svelte.dev/src/routes/docs/+layout.svelte @@ -4,8 +4,7 @@ export let data; - $: pageData = $page.data.page; - $: category = pageData?.category; + $: category = $page.data.page?.category;
From b85d699a63bdb1a8e061cc23716dbb3832d74460 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 18 Jan 2024 14:39:40 -0500 Subject: [PATCH 52/57] style --- sites/kit.svelte.dev/src/lib/server/docs/index.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/sites/kit.svelte.dev/src/lib/server/docs/index.js b/sites/kit.svelte.dev/src/lib/server/docs/index.js index 030b2361350e..b17451db3aef 100644 --- a/sites/kit.svelte.dev/src/lib/server/docs/index.js +++ b/sites/kit.svelte.dev/src/lib/server/docs/index.js @@ -88,15 +88,16 @@ export function get_docs_list() { /** @param {string} markdown */ async function get_sections(markdown) { - const headingRegex = /^##\s+(.*)$/gm; /** @type {import('./types.js').Section[]} */ - const secondLevelHeadings = []; + const second_level_headings = []; + + const pattern = /^##\s+(.*)$/gm; let match; const placeholders_rendered = await replaceExportTypePlaceholders(markdown, modules); - while ((match = headingRegex.exec(placeholders_rendered)) !== null) { - secondLevelHeadings.push({ + while ((match = pattern.exec(placeholders_rendered)) !== null) { + second_level_headings.push({ title: removeMarkdown( escape(await markedTransform(match[1], { paragraph: (txt) => txt })) .replace(/<\/?code>/g, '') @@ -110,5 +111,5 @@ async function get_sections(markdown) { }); } - return secondLevelHeadings; + return second_level_headings; } From 92fb9f63ab8a019610a71e03bdd35a44a63fdc6b Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 18 Jan 2024 15:05:36 -0500 Subject: [PATCH 53/57] simplify --- .../src/lib/server/docs/index.js | 3 ++- .../src/routes/content.json/content.server.js | 24 ++++++------------- 2 files changed, 9 insertions(+), 18 deletions(-) diff --git a/sites/kit.svelte.dev/src/lib/server/docs/index.js b/sites/kit.svelte.dev/src/lib/server/docs/index.js index b17451db3aef..c0f721e719e4 100644 --- a/sites/kit.svelte.dev/src/lib/server/docs/index.js +++ b/sites/kit.svelte.dev/src/lib/server/docs/index.js @@ -47,7 +47,7 @@ for (const [file, asset] of Object.entries(markdown)) { if (!category) continue; // draft const { - metadata: { draft, title }, + metadata: { draft, title, rank }, body } = extractFrontmatter(await read(asset).text()); @@ -59,6 +59,7 @@ for (const [file, asset] of Object.entries(markdown)) { }); pages[slug] = { + rank: +rank || undefined, category: category.title, title, file: `${category_dir}/${basename}`, diff --git a/sites/kit.svelte.dev/src/routes/content.json/content.server.js b/sites/kit.svelte.dev/src/routes/content.json/content.server.js index 3a7169e973b1..788b95f65dc7 100644 --- a/sites/kit.svelte.dev/src/routes/content.json/content.server.js +++ b/sites/kit.svelte.dev/src/routes/content.json/content.server.js @@ -1,7 +1,6 @@ -import { read } from '$app/server'; import { modules } from '$lib/generated/type-info.js'; +import { pages } from '$lib/server/docs/index.js'; import { - extractFrontmatter, markedTransform, replaceExportTypePlaceholders, slugify @@ -16,11 +15,6 @@ const categories = [ } ]; -const files = import.meta.glob('../../../../../documentation/*/*/*.md', { - as: 'url', - eager: true -}); - export async function content() { /** @type {import('@sveltejs/site-kit/search').Block[]} */ const blocks = []; @@ -28,19 +22,15 @@ export async function content() { for (const category of categories) { const breadcrumbs = category.label ? [category.label] : []; - for (const [file, asset] of Object.entries(files)) { - const slug = /\/\d{2}-(.+)\.md$/.exec(file)[1]; - - const markdown = await replaceExportTypePlaceholders(await read(asset).text(), modules); - - const { body, metadata } = extractFrontmatter(markdown); + for (const [slug, page] of Object.entries(pages)) { + const body = await replaceExportTypePlaceholders(page.body, modules); const sections = body.trim().split(/^## /m); const intro = sections.shift().trim(); - const rank = +metadata.rank || undefined; + const rank = page.rank; blocks.push({ - breadcrumbs: [...breadcrumbs, metadata.title], + breadcrumbs: [...breadcrumbs, page.title], href: category.href([slug]), content: await plaintext(intro), rank @@ -56,7 +46,7 @@ export async function content() { const intro = subsections.shift().trim(); blocks.push({ - breadcrumbs: [...breadcrumbs, metadata.title, h3], + breadcrumbs: [...breadcrumbs, page.title, h3], href: category.href([slug, slugify(h3)]), content: await plaintext(intro), rank @@ -67,7 +57,7 @@ export async function content() { const h4 = lines.shift(); blocks.push({ - breadcrumbs: [...breadcrumbs, metadata.title, h3, h4], + breadcrumbs: [...breadcrumbs, page.title, h3, h4], href: category.href([slug, slugify(h3), slugify(h4)]), content: await plaintext(lines.join('\n').trim()), rank From 71ba727882be9ac44f4661327ff213b4df510537 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 18 Jan 2024 15:06:16 -0500 Subject: [PATCH 54/57] lockfile --- pnpm-lock.yaml | 52 +++++++++++++++++++++++++++----------------------- 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 585750c2e8cf..a3bb0b8f3f44 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -289,7 +289,7 @@ importers: dependencies: '@sveltejs/kit': specifier: ^1.0.0 || ^2.0.0 - version: link:../kit + version: 2.3.5(@sveltejs/vite-plugin-svelte@3.0.1)(svelte@4.2.8)(vite@5.0.11) packages/create-svelte: dependencies: @@ -1266,9 +1266,6 @@ importers: svelte: specifier: ^4.2.8 version: 4.2.8 - tiny-glob: - specifier: ^0.2.9 - version: 0.2.9 typescript: specifier: 5.0.4 version: 5.0.4 @@ -2317,6 +2314,33 @@ packages: typescript: 5.3.3 dev: true + /@sveltejs/kit@2.3.5(@sveltejs/vite-plugin-svelte@3.0.1)(svelte@4.2.8)(vite@5.0.11): + resolution: {integrity: sha512-Vu+ckGQu/t+pcuueryaHYacgoYsYq6KB1k1p4w1xDp1eQ2aYz3POcx3GSuqxWjJfOwMMM83xiuAnDWBKyOn1Tg==} + engines: {node: '>=18.13'} + hasBin: true + requiresBuild: true + peerDependencies: + '@sveltejs/vite-plugin-svelte': ^3.0.0 + svelte: ^4.0.0 || ^5.0.0-next.0 + vite: ^5.0.3 + dependencies: + '@sveltejs/vite-plugin-svelte': 3.0.1(svelte@4.2.8)(vite@5.0.11) + '@types/cookie': 0.6.0 + cookie: 0.6.0 + devalue: 4.3.2 + esm-env: 1.0.0 + import-meta-resolve: 4.0.0 + kleur: 4.1.5 + magic-string: 0.30.5 + mrmime: 2.0.0 + sade: 1.8.1 + set-cookie-parser: 2.6.0 + sirv: 2.0.4 + svelte: 4.2.8 + tiny-glob: 0.2.9 + vite: 5.0.11(@types/node@18.19.3)(lightningcss@1.22.1) + dev: false + /@sveltejs/site-kit@6.0.0-next.59(@sveltejs/kit@packages+kit)(svelte@4.2.8): resolution: {integrity: sha512-nAUCuunhN0DmurQBxbsauqvdvv4mL0F/Aluxq0hFf6gB3iSn9WdaUZdPMXoujy+8cy+m6UvKuyhkgApZhmOLvw==} peerDependencies: @@ -2343,7 +2367,6 @@ packages: vite: 5.0.11(@types/node@18.19.3)(lightningcss@1.22.1) transitivePeerDependencies: - supports-color - dev: true /@sveltejs/vite-plugin-svelte@3.0.1(svelte@4.2.8)(vite@5.0.11): resolution: {integrity: sha512-CGURX6Ps+TkOovK6xV+Y2rn8JKa8ZPUHPZ/NKgCxAmgBrXReavzFl8aOSCj3kQ1xqT7yGJj53hjcV/gqwDAaWA==} @@ -2363,7 +2386,6 @@ packages: vitefu: 0.2.5(vite@5.0.11) transitivePeerDependencies: - supports-color - dev: true /@svitejs/changesets-changelog-github-compact@1.1.0: resolution: {integrity: sha512-qhUGGDHcpbY2zpjW3SwqchuW8J/5EzlPFud7xNntHKA7f3a/mx5+g+ruJKFHSAiVZYo30PALt+AyhmPUNKH/Og==} @@ -2435,7 +2457,6 @@ packages: resolution: {integrity: sha512-k5fggr14DwAytoA/t8rPrIz++lXK7/DqckthCmoZOKNsEbJkId4Z//BqgApXBUGrGddrigYa1oqheo/7YmW4rg==} dependencies: undici-types: 5.26.5 - dev: true /@types/normalize-package-data@2.4.4: resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} @@ -3354,7 +3375,6 @@ packages: resolution: {integrity: sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==} engines: {node: '>=0.10'} hasBin: true - dev: true /detect-libc@2.0.2: resolution: {integrity: sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==} @@ -4517,7 +4537,6 @@ packages: cpu: [arm64] os: [darwin] requiresBuild: true - dev: true optional: true /lightningcss-darwin-x64@1.22.1: @@ -4526,7 +4545,6 @@ packages: cpu: [x64] os: [darwin] requiresBuild: true - dev: true optional: true /lightningcss-freebsd-x64@1.22.1: @@ -4535,7 +4553,6 @@ packages: cpu: [x64] os: [freebsd] requiresBuild: true - dev: true optional: true /lightningcss-linux-arm-gnueabihf@1.22.1: @@ -4544,7 +4561,6 @@ packages: cpu: [arm] os: [linux] requiresBuild: true - dev: true optional: true /lightningcss-linux-arm64-gnu@1.22.1: @@ -4553,7 +4569,6 @@ packages: cpu: [arm64] os: [linux] requiresBuild: true - dev: true optional: true /lightningcss-linux-arm64-musl@1.22.1: @@ -4562,7 +4577,6 @@ packages: cpu: [arm64] os: [linux] requiresBuild: true - dev: true optional: true /lightningcss-linux-x64-gnu@1.22.1: @@ -4571,7 +4585,6 @@ packages: cpu: [x64] os: [linux] requiresBuild: true - dev: true optional: true /lightningcss-linux-x64-musl@1.22.1: @@ -4580,7 +4593,6 @@ packages: cpu: [x64] os: [linux] requiresBuild: true - dev: true optional: true /lightningcss-win32-x64-msvc@1.22.1: @@ -4589,7 +4601,6 @@ packages: cpu: [x64] os: [win32] requiresBuild: true - dev: true optional: true /lightningcss@1.22.1: @@ -4607,7 +4618,6 @@ packages: lightningcss-linux-x64-gnu: 1.22.1 lightningcss-linux-x64-musl: 1.22.1 lightningcss-win32-x64-msvc: 1.22.1 - dev: true /lilconfig@2.1.0: resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} @@ -4889,7 +4899,6 @@ packages: resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true - dev: true /natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} @@ -5279,7 +5288,6 @@ packages: nanoid: 3.3.7 picocolors: 1.0.0 source-map-js: 1.0.2 - dev: true /preferred-pm@3.1.2: resolution: {integrity: sha512-nk7dKrcW8hfCZ4H6klWcdRknBOXWzNQByJ0oJyX97BOupsYD+FzLS4hflgEu/uPUEHZCuRfMxzCBsuWd7OzT8Q==} @@ -5984,7 +5992,6 @@ packages: svelte: ^3.19.0 || ^4.0.0 dependencies: svelte: 4.2.8 - dev: true /svelte-local-storage-store@0.6.4(svelte@4.2.8): resolution: {integrity: sha512-45WoY2vSGPQM1sIQJ9jTkPPj20hYeqm+af6mUGRFSPP5WglZf36YYoZqwmZZ8Dt/2SU8lem+BTA8/Z/8TkqNLg==} @@ -6322,7 +6329,6 @@ packages: /undici-types@5.26.5: resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} - dev: true /universalify@0.1.2: resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} @@ -6446,7 +6452,6 @@ packages: rollup: 4.9.5 optionalDependencies: fsevents: 2.3.3 - dev: true /vitefu@0.2.5(vite@5.0.11): resolution: {integrity: sha512-SgHtMLoqaeeGnd2evZ849ZbACbnwQCIwRH57t18FxcXoZop0uQu0uzlIhJBlF/eWVzuce0sHeqPcDo+evVcg8Q==} @@ -6457,7 +6462,6 @@ packages: optional: true dependencies: vite: 5.0.11(@types/node@18.19.3)(lightningcss@1.22.1) - dev: true /vitest@1.2.0(@types/node@18.19.3)(lightningcss@1.22.1): resolution: {integrity: sha512-Ixs5m7BjqvLHXcibkzKRQUvD/XLw0E3rvqaCMlrm/0LMsA0309ZqYvTlPzkhh81VlEyVZXFlwWnkhb6/UMtcaQ==} From 86c22cc799775ad4af759be126a17455ca6144a6 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 18 Jan 2024 16:46:41 -0500 Subject: [PATCH 55/57] Apply suggestions from code review Co-authored-by: Ben McCann <322311+benmccann@users.noreply.github.com> --- packages/kit/src/exports/vite/index.js | 6 +++++- packages/kit/src/types/internal.d.ts | 1 + packages/kit/src/utils/features.js | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/kit/src/exports/vite/index.js b/packages/kit/src/exports/vite/index.js index 3a1f3657f8e2..3f108800a4a8 100644 --- a/packages/kit/src/exports/vite/index.js +++ b/packages/kit/src/exports/vite/index.js @@ -225,7 +225,11 @@ async function kit({ svelte_config }) { const service_worker_entry_file = resolve_entry(kit.files.serviceWorker); const parsed_service_worker = path.parse(kit.files.serviceWorker); - /** @type {Record} */ + /** + * A map showing which features (such as `$app/server:read`) are defined + * in which chunks, so that we can later determine which routes use which features + * @type {Record} + */ const tracked_features = {}; const sourcemapIgnoreList = /** @param {string} relative_path */ (relative_path) => diff --git a/packages/kit/src/types/internal.d.ts b/packages/kit/src/types/internal.d.ts index 02a11e894723..22e11e906ea8 100644 --- a/packages/kit/src/types/internal.d.ts +++ b/packages/kit/src/types/internal.d.ts @@ -126,6 +126,7 @@ export class InternalServer extends Server { options: RequestOptions & { prerendering?: PrerenderOptions; read: (file: string) => Buffer; + /** A hook called before `handle` during dev, so that `AsyncLocalStorage` can be populated */ before_handle?: (event: RequestEvent, config: any, prerender: PrerenderOption) => void; } ): Promise; diff --git a/packages/kit/src/utils/features.js b/packages/kit/src/utils/features.js index 137c246cc706..4a8530d22bbb 100644 --- a/packages/kit/src/utils/features.js +++ b/packages/kit/src/utils/features.js @@ -16,7 +16,7 @@ export function check_feature(route_id, config, feature, adapter) { if (!supported) { throw new Error( - `Cannot use \`read\` from \`$app/server\` in ${route_id} when using ${adapter.name}. Upgrading may fix this error` + `Cannot use \`read\` from \`$app/server\` in ${route_id} when using ${adapter.name}. Please ensure that your adapter is up to date and supports this feature.` ); } } From a53c0b6e6f96c0486ab46fbb89f8823b82660f06 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 18 Jan 2024 16:47:56 -0500 Subject: [PATCH 56/57] capitalize --- .../docs/25-build-and-deploy/99-writing-adapters.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/documentation/docs/25-build-and-deploy/99-writing-adapters.md b/documentation/docs/25-build-and-deploy/99-writing-adapters.md index fc7a2f4fbe73..aac887374d31 100644 --- a/documentation/docs/25-build-and-deploy/99-writing-adapters.md +++ b/documentation/docs/25-build-and-deploy/99-writing-adapters.md @@ -23,9 +23,9 @@ export default function (options) { }, supports: { read: ({ config, route }) => { - // return `true` if the route with the given `config` can use `read` - // from `$app/server` in production, return false if it can't. - // or throw a descriptive error describing how to configure the deployment + // Return `true` if the route with the given `config` can use `read` + // from `$app/server` in production, return `false`` if it can't. + // Or throw a descriptive error describing how to configure the deployment } } }; From 2b3746c70c422fe0245e4eb8b33308b519318a8c Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 18 Jan 2024 17:11:46 -0500 Subject: [PATCH 57/57] Apply suggestions from code review Co-authored-by: Ben McCann <322311+benmccann@users.noreply.github.com> --- documentation/docs/25-build-and-deploy/99-writing-adapters.md | 2 +- packages/kit/src/exports/vite/index.js | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/documentation/docs/25-build-and-deploy/99-writing-adapters.md b/documentation/docs/25-build-and-deploy/99-writing-adapters.md index aac887374d31..183d4c727981 100644 --- a/documentation/docs/25-build-and-deploy/99-writing-adapters.md +++ b/documentation/docs/25-build-and-deploy/99-writing-adapters.md @@ -24,7 +24,7 @@ export default function (options) { supports: { read: ({ config, route }) => { // Return `true` if the route with the given `config` can use `read` - // from `$app/server` in production, return `false`` if it can't. + // from `$app/server` in production, return `false` if it can't. // Or throw a descriptive error describing how to configure the deployment } } diff --git a/packages/kit/src/exports/vite/index.js b/packages/kit/src/exports/vite/index.js index 3f108800a4a8..7c8297faf3be 100644 --- a/packages/kit/src/exports/vite/index.js +++ b/packages/kit/src/exports/vite/index.js @@ -714,6 +714,7 @@ async function kit({ svelte_config }) { return { code: code.replace(/__SVELTEKIT_TRACK__\('(.+?)'\)/g, (_, label) => { (tracked_features[chunk.name + '.js'] ??= []).push(label); + // put extra whitespace at the end of the comment to preserve the source size and avoid interfering with source maps return `/* track ${label} */`; }), map: null // TODO we may need to generate a sourcemap in future