diff --git a/packages/kit/src/exports/vite/dev/index.js b/packages/kit/src/exports/vite/dev/index.js index e130e77a01b3..11427baa3df6 100644 --- a/packages/kit/src/exports/vite/dev/index.js +++ b/packages/kit/src/exports/vite/dev/index.js @@ -465,7 +465,27 @@ export async function dev(vite, vite_config, svelte_config) { const { set_assets } = await vite.ssrLoadModule('__sveltekit/paths'); set_assets(assets); - const server = new Server(manifest); + const server = new Server( + { + manifest + }, + // @ts-expect-error internal second argument + /** @type {import('types').ServerDevOptions} */ ({ + on_error: (route_id, e) => { + const error = coalesce_to_error(e); + // In a timeout, else might be sent too soon before SvelteKit reloads and therefore "deletes" the dialog + setTimeout(() => { + vite.ws.send({ + type: 'error', + err: { + message: `Error while loading data for route ${route_id}: ${error.message}`, + stack: error.stack || '' + } + }); + }, 500); + } + }) + ); await server.init({ env }); diff --git a/packages/kit/src/runtime/server/index.js b/packages/kit/src/runtime/server/index.js index 70d3dc1239dd..a739a64f62d4 100644 --- a/packages/kit/src/runtime/server/index.js +++ b/packages/kit/src/runtime/server/index.js @@ -10,11 +10,18 @@ export class Server { /** @type {import('types').SSRManifest} */ #manifest; - /** @param {import('types').SSRManifest} manifest */ - constructor(manifest) { + /** @type {import('types').ServerDevOptions | undefined} */ + #dev; + + /** + * @param {import('types').SSRManifest} manifest + * @param {import('types').ServerDevOptions} [dev] + */ + constructor(manifest, dev) { /** @type {import('types').SSROptions} */ this.#options = options; this.#manifest = manifest; + this.#dev = dev; } /** @@ -45,6 +52,14 @@ export class Server { handleError: module.handleError || (({ error }) => console.error(error?.stack)), handleFetch: module.handleFetch || (({ request, fetch }) => fetch(request)) }; + if (DEV && this.#dev) { + const handle_error = this.#options.hooks.handleError; + const on_error = this.#dev.on_error; + this.#options.hooks.handleError = (event) => { + on_error(event.event.route.id, event.error); + return handle_error(event); + }; + } } catch (error) { if (DEV) { this.#options.hooks = { diff --git a/packages/kit/types/internal.d.ts b/packages/kit/types/internal.d.ts index d85545cb58f0..6ff26374ae22 100644 --- a/packages/kit/types/internal.d.ts +++ b/packages/kit/types/internal.d.ts @@ -349,6 +349,10 @@ export interface SSROptions { version_hash: string; } +export interface ServerDevOptions { + on_error: (route_id: string | null, error: unknown) => void; +} + export interface PageNodeIndexes { errors: Array; layouts: Array;