diff --git a/packages/svelte/src/internal/client/dom/blocks/async.js b/packages/svelte/src/internal/client/dom/blocks/async.js index 5ec50a5988..dc9ef73edc 100644 --- a/packages/svelte/src/internal/client/dom/blocks/async.js +++ b/packages/svelte/src/internal/client/dom/blocks/async.js @@ -1,6 +1,14 @@ /** @import { TemplateNode, Value } from '#client' */ import { flatten } from '../../reactivity/async.js'; import { get } from '../../runtime.js'; +import { + hydrate_next, + hydrate_node, + hydrating, + set_hydrate_node, + set_hydrating, + remove_nodes +} from '../hydration.js'; import { get_boundary } from './boundary.js'; /** @@ -13,7 +21,22 @@ export function async(node, expressions, fn) { boundary.update_pending_count(1); + var was_hydrating = hydrating; + + if (was_hydrating) { + hydrate_next(); + + var previous_hydrate_node = hydrate_node; + var end = remove_nodes(false); + set_hydrate_node(end); + } + flatten([], expressions, (values) => { + if (was_hydrating) { + set_hydrating(true); + set_hydrate_node(previous_hydrate_node); + } + try { // get values eagerly to avoid creating blocks if they reject for (const d of values) get(d); @@ -22,5 +45,9 @@ export function async(node, expressions, fn) { } finally { boundary.update_pending_count(-1); } + + if (was_hydrating) { + set_hydrating(false); + } }); } diff --git a/packages/svelte/src/internal/client/dom/hydration.js b/packages/svelte/src/internal/client/dom/hydration.js index 1f80b7922b..f648019118 100644 --- a/packages/svelte/src/internal/client/dom/hydration.js +++ b/packages/svelte/src/internal/client/dom/hydration.js @@ -81,9 +81,10 @@ export function next(count = 1) { } /** - * Removes all nodes starting at `hydrate_node` up until the next hydration end comment + * Skips or removes (depending on {@link remove}) all nodes starting at `hydrate_node` up until the next hydration end comment + * @param {boolean} remove */ -export function remove_nodes() { +export function remove_nodes(remove = true) { var depth = 0; var node = hydrate_node; @@ -100,7 +101,7 @@ export function remove_nodes() { } var next = /** @type {TemplateNode} */ (get_next_sibling(node)); - node.remove(); + if (remove) node.remove(); node = next; } } diff --git a/packages/svelte/src/internal/server/renderer.js b/packages/svelte/src/internal/server/renderer.js index 26f7ac2b3c..c78c2fd7d4 100644 --- a/packages/svelte/src/internal/server/renderer.js +++ b/packages/svelte/src/internal/server/renderer.js @@ -98,7 +98,6 @@ export class Renderer { */ child(fn, type) { const child = new Renderer(this.global, this, type); - this.#out.push(child); const parent = ssr_context; @@ -120,6 +119,12 @@ export class Renderer { // just to avoid unhandled promise rejections -- we'll end up throwing in `collect_async` if something fails result.catch(() => {}); child.promises.initial = result; + + // add hydration boundaries that `$.async` handles in the client + this.#out.push(BLOCK_OPEN, child, BLOCK_CLOSE); + } else { + // TODO should we always put hydration boundaries around the child? + this.#out.push(child); } return child; diff --git a/packages/svelte/tests/runtime-runes/samples/async-no-pending-await-in-block/_config.js b/packages/svelte/tests/runtime-runes/samples/async-no-pending-await-in-block/_config.js new file mode 100644 index 0000000000..35d88f6c8b --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/async-no-pending-await-in-block/_config.js @@ -0,0 +1,33 @@ +import { tick } from 'svelte'; +import { test } from '../../test'; + +export default test({ + mode: ['hydrate'], + + server_props: { + browser: false + }, + + ssrHtml: ` +

hello from the server

+

hello from the server

+

hello from the server

+ `, + + props: { + browser: true + }, + + async test({ assert, target }) { + await tick(); + + assert.htmlEqual( + target.innerHTML, + ` +

hello from the browser

+

hello from the browser

+

hello from the browser

+ ` + ); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/async-no-pending-await-in-block/main.svelte b/packages/svelte/tests/runtime-runes/samples/async-no-pending-await-in-block/main.svelte new file mode 100644 index 0000000000..82e6a57a18 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/async-no-pending-await-in-block/main.svelte @@ -0,0 +1,10 @@ + + +{#if await true} +

hello from the {browser ? 'browser' : 'server'}

+{/if} + +

hello from the {browser ? 'browser' : 'server'}

+

hello from the {browser ? 'browser' : 'server'}