diff --git a/.changeset/bright-pants-send.md b/.changeset/bright-pants-send.md new file mode 100644 index 0000000000..8a877961ec --- /dev/null +++ b/.changeset/bright-pants-send.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: properly hydrate await in `{@html}` diff --git a/packages/svelte/src/internal/client/dom/blocks/async.js b/packages/svelte/src/internal/client/dom/blocks/async.js index c2ae07fd17..d85c611d42 100644 --- a/packages/svelte/src/internal/client/dom/blocks/async.js +++ b/packages/svelte/src/internal/client/dom/blocks/async.js @@ -1,4 +1,6 @@ /** @import { Blocker, TemplateNode, Value } from '#client' */ +import { COMMENT_NODE } from '#client/constants'; +import { HYDRATION_START, HYDRATION_START_ELSE } from '../../../../constants.js'; import { flatten } from '../../reactivity/async.js'; import { Batch, current_batch } from '../../reactivity/batch.js'; import { get } from '../../runtime.js'; @@ -34,10 +36,17 @@ export function async(node, blockers = [], expressions = [], fn) { var was_hydrating = hydrating; if (was_hydrating) { + // Check if this is an `@html` block by looking at the current comment + // `@html` uses a hash comment (not `[` or `[!`) with empty comment as end marker + var is_html = + hydrate_node.nodeType === COMMENT_NODE && + /** @type {Comment} */ (hydrate_node).data !== HYDRATION_START && + /** @type {Comment} */ (hydrate_node).data !== HYDRATION_START_ELSE; + hydrate_next(); var previous_hydrate_node = hydrate_node; - var end = skip_nodes(false); + var end = skip_nodes(false, is_html); set_hydrate_node(end); } diff --git a/packages/svelte/src/internal/client/dom/hydration.js b/packages/svelte/src/internal/client/dom/hydration.js index 7a9ef33d1b..261fab2e03 100644 --- a/packages/svelte/src/internal/client/dom/hydration.js +++ b/packages/svelte/src/internal/client/dom/hydration.js @@ -83,8 +83,9 @@ export function next(count = 1) { /** * Skips or removes (depending on {@link remove}) all nodes starting at `hydrate_node` up until the next hydration end comment * @param {boolean} remove + * @param {boolean} [is_html] - If true, look for empty comment end marker (used by `@html` blocks) */ -export function skip_nodes(remove = true) { +export function skip_nodes(remove = true, is_html = false) { var depth = 0; var node = hydrate_node; @@ -97,6 +98,9 @@ export function skip_nodes(remove = true) { depth -= 1; } else if (data === HYDRATION_START || data === HYDRATION_START_ELSE) { depth += 1; + } else if (is_html && data === '' && depth === 0) { + // `@html` blocks use an empty comment as end marker + return node; } } diff --git a/packages/svelte/tests/runtime-runes/samples/await-html-hydration/_config.js b/packages/svelte/tests/runtime-runes/samples/await-html-hydration/_config.js new file mode 100644 index 0000000000..8e8f097feb --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/await-html-hydration/_config.js @@ -0,0 +1,6 @@ +import { test } from '../../test'; + +export default test({ + mode: ['hydrate'], + async test() {} +}); diff --git a/packages/svelte/tests/runtime-runes/samples/await-html-hydration/main.svelte b/packages/svelte/tests/runtime-runes/samples/await-html-hydration/main.svelte new file mode 100644 index 0000000000..3b9ce94e61 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/await-html-hydration/main.svelte @@ -0,0 +1 @@ +