From 78481862fb033bfaa1c31d84eba96460b0dfe19e Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Tue, 7 Oct 2025 18:00:05 +0200 Subject: [PATCH] fix: hydrate each blocks inside element correctly (#16908) We have an each block optimization that omits the comment when the each block is the sole child of an element. This optimization clashes with async which wants to skip ahead to the sibling closing comment. For now we therefore remove that optimization when the each block is async. In the long run we should instead optimize _all_ cases where _any_ block is the sole child of an element, in both async and sync mode, consistently. fixes #16905 fixes #16907 --- .changeset/chilly-bats-build.md | 5 +++++ .../client/visitors/shared/fragment.js | 9 ++++++++- .../samples/async-each-sibling/_config.js | 16 ++++++++++++++++ .../samples/async-each-sibling/main.svelte | 11 +++++++++++ 4 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 .changeset/chilly-bats-build.md create mode 100644 packages/svelte/tests/runtime-runes/samples/async-each-sibling/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/async-each-sibling/main.svelte diff --git a/.changeset/chilly-bats-build.md b/.changeset/chilly-bats-build.md new file mode 100644 index 0000000000..872e4c79c4 --- /dev/null +++ b/.changeset/chilly-bats-build.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: hydrate each blocks inside element correctly diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/fragment.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/fragment.js index 62d07014ee..3588f2843a 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/fragment.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/fragment.js @@ -99,7 +99,14 @@ export function process_children(nodes, initial, is_element, context) { if (is_static_element(node, context.state)) { skipped += 1; - } else if (node.type === 'EachBlock' && nodes.length === 1 && is_element) { + } else if ( + node.type === 'EachBlock' && + nodes.length === 1 && + is_element && + // In case it's wrapped in async the async logic will want to skip sibling nodes up until the end, hence we cannot make this controlled + // TODO switch this around and instead optimize for elements with a single block child and not require extra comments (neither for async nor normally) + !(node.body.metadata.has_await || node.metadata.expression.has_await) + ) { node.metadata.is_controlled = true; } else { const id = flush_node(false, node.type === 'RegularElement' ? node.name : 'node'); diff --git a/packages/svelte/tests/runtime-runes/samples/async-each-sibling/_config.js b/packages/svelte/tests/runtime-runes/samples/async-each-sibling/_config.js new file mode 100644 index 0000000000..ff10a48f11 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/async-each-sibling/_config.js @@ -0,0 +1,16 @@ +import { tick } from 'svelte'; +import { test } from '../../test'; + +export default test({ + mode: ['async-server', 'hydrate', 'client'], + ssrHtml: `