From f6d3894ea8f0e10c80da601115f81fb35a425db2 Mon Sep 17 00:00:00 2001 From: "S. Elliott Johnson" Date: Tue, 16 Sep 2025 16:30:38 -0600 Subject: [PATCH] I have lost the plot --- .../docs/98-reference/.generated/server-errors.md | 8 ++++++++ packages/svelte/messages/server-errors/async.md | 6 ++++++ packages/svelte/src/internal/server/errors.js | 12 ++++++++++++ packages/svelte/src/internal/server/index.js | 6 ++++++ packages/svelte/src/internal/server/payload.test.ts | 6 ++++-- packages/svelte/tests/runtime-legacy/shared.ts | 3 ++- .../samples/async-no-pending-throws-sync/_config.js | 2 +- packages/svelte/tests/server-side-rendering/test.ts | 2 ++ 8 files changed, 41 insertions(+), 4 deletions(-) diff --git a/documentation/docs/98-reference/.generated/server-errors.md b/documentation/docs/98-reference/.generated/server-errors.md index fb87cec1f4..e56886b603 100644 --- a/documentation/docs/98-reference/.generated/server-errors.md +++ b/documentation/docs/98-reference/.generated/server-errors.md @@ -15,3 +15,11 @@ You (or the framework you're using) used `render` with an async component. Eithe ``` Certain methods such as `mount` cannot be invoked while running in a server context. Avoid calling them eagerly, i.e. not during render. + +### missing_experimental_flag + +``` +Attempted to use `renderAsync` without `experimental.async` enabled +``` + +Set `experimental.async: true` in your compiler options to use async server rendering. diff --git a/packages/svelte/messages/server-errors/async.md b/packages/svelte/messages/server-errors/async.md index 7389228b29..164c77f943 100644 --- a/packages/svelte/messages/server-errors/async.md +++ b/packages/svelte/messages/server-errors/async.md @@ -3,3 +3,9 @@ > Encountered asynchronous work while rendering synchronously. You (or the framework you're using) used `render` with an async component. Either use `renderAsync` or wrap the async component in a `svelte:boundary` with a `pending` snippet. + +## missing_experimental_flag + +> Attempted to use `renderAsync` without `experimental.async` enabled + +Set `experimental.async: true` in your compiler options to use async server rendering. diff --git a/packages/svelte/src/internal/server/errors.js b/packages/svelte/src/internal/server/errors.js index 4bfdd6f0c6..79211e7054 100644 --- a/packages/svelte/src/internal/server/errors.js +++ b/packages/svelte/src/internal/server/errors.js @@ -14,6 +14,18 @@ export function async_in_sync() { throw error; } +/** + * Attempted to use `renderAsync` without `experimental.async` enabled + * @returns {never} + */ +export function missing_experimental_flag() { + const error = new Error(`missing_experimental_flag\nAttempted to use \`renderAsync\` without \`experimental.async\` enabled\nhttps://svelte.dev/e/missing_experimental_flag`); + + error.name = 'Svelte error'; + + throw error; +} + /** * `%name%(...)` is not available on the server * @param {string} name diff --git a/packages/svelte/src/internal/server/index.js b/packages/svelte/src/internal/server/index.js index 0841588477..f1b6dbf970 100644 --- a/packages/svelte/src/internal/server/index.js +++ b/packages/svelte/src/internal/server/index.js @@ -20,6 +20,8 @@ import { validate_store } from '../shared/validate.js'; import { is_boolean_attribute, is_raw_text_element, is_void } from '../../utils.js'; import { Payload, TreeState } from './payload.js'; import { abort } from './abort-signal.js'; +import { async_mode_flag } from '../flags/index.js'; +import * as e from './errors.js'; // https://html.spec.whatwg.org/multipage/syntax.html#attributes-2 // https://infra.spec.whatwg.org/#noncharacter @@ -116,6 +118,10 @@ export function render(component, options = {}) { * @returns {Promise} */ export async function render_async(component, options = {}) { + if (!async_mode_flag) { + e.missing_experimental_flag(); + } + var previous_context = ssr_context; try { diff --git a/packages/svelte/src/internal/server/payload.test.ts b/packages/svelte/src/internal/server/payload.test.ts index 0997a5675b..dfba397021 100644 --- a/packages/svelte/src/internal/server/payload.test.ts +++ b/packages/svelte/src/internal/server/payload.test.ts @@ -70,7 +70,7 @@ test('creating an async child in a sync context throws', () => { await Promise.resolve(); $$payload.push('x'); }) - ).toThrow('Encountered an asynchronous component while rendering synchronously'); + ).toThrow('async_in_sync'); }); test('collect_async allows awaiting payload to get aggregated content', async () => { @@ -200,7 +200,9 @@ test('subsume refuses to switch modes', () => { b.local.select_value = 'B'; b.promises.initial = Promise.resolve(); - expect(() => a.subsume(b)).toThrow('invariant: a payload cannot switch modes'); + expect(() => a.subsume(b)).toThrow( + "invariant: A payload cannot switch modes. If you're seeing this, there's a compiler bug. File an issue!" + ); }); test('TreeState uid generator uses prefix and is shared by copy()', () => { diff --git a/packages/svelte/tests/runtime-legacy/shared.ts b/packages/svelte/tests/runtime-legacy/shared.ts index b8d6a990c9..df4a118065 100644 --- a/packages/svelte/tests/runtime-legacy/shared.ts +++ b/packages/svelte/tests/runtime-legacy/shared.ts @@ -167,6 +167,7 @@ export function runtime_suite(runes: boolean) { } if (variant === 'async-ssr') { + if (!runes) return 'no-test'; if ( (config.mode && !config.mode.includes('async-server')) || (!config.test_ssr && @@ -334,7 +335,7 @@ async function run_test_variant( // ssr into target const SsrSvelteComponent = (await import(`${cwd}/_output/server/main.svelte.js`)).default; const rendered = - variant === 'async-ssr' || variant === 'hydrate' + variant === 'async-ssr' || (variant === 'hydrate' && compileOptions.experimental?.async) ? await renderAsync(SsrSvelteComponent, { props: config.server_props ?? config.props ?? {}, idPrefix: config.id_prefix diff --git a/packages/svelte/tests/runtime-runes/samples/async-no-pending-throws-sync/_config.js b/packages/svelte/tests/runtime-runes/samples/async-no-pending-throws-sync/_config.js index 6211eec854..78e772cd70 100644 --- a/packages/svelte/tests/runtime-runes/samples/async-no-pending-throws-sync/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/async-no-pending-throws-sync/_config.js @@ -3,5 +3,5 @@ import { test } from '../../test'; export default test({ mode: ['server'], - error: 'Encountered an asynchronous component while rendering synchronously' + error: 'async_in_sync' }); diff --git a/packages/svelte/tests/server-side-rendering/test.ts b/packages/svelte/tests/server-side-rendering/test.ts index 3aa3d3a8d8..95609ccd67 100644 --- a/packages/svelte/tests/server-side-rendering/test.ts +++ b/packages/svelte/tests/server-side-rendering/test.ts @@ -59,6 +59,8 @@ const { test, run } = suite_with_variants