diff --git a/packages/svelte/src/compiler/phases/3-transform/server/transform-server.js b/packages/svelte/src/compiler/phases/3-transform/server/transform-server.js index 1800479a68..d1646154f2 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/transform-server.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/transform-server.js @@ -38,6 +38,7 @@ import { filename, locator } from '../../../state.js'; export const block_open = t_string(``); export const block_close = t_string(``); +export const block_anchor = t_string(``); /** * @param {string} value @@ -1470,8 +1471,6 @@ const template_visitors = { } }; - context.state.template.push(block_open); - const main = create_block(node, node.fragment.nodes, { ...context, state: { ...context.state, metadata } @@ -1506,7 +1505,7 @@ const template_visitors = { ) ) ), - block_close + block_anchor ); if (context.state.options.dev) { context.state.template.push(t_statement(b.stmt(b.call('$.pop_element')))); diff --git a/packages/svelte/src/internal/client/dom/blocks/svelte-element.js b/packages/svelte/src/internal/client/dom/blocks/svelte-element.js index 49d6ddeff7..d01610d0f8 100644 --- a/packages/svelte/src/internal/client/dom/blocks/svelte-element.js +++ b/packages/svelte/src/internal/client/dom/blocks/svelte-element.js @@ -1,5 +1,5 @@ import { namespace_svg } from '../../../../constants.js'; -import { hydrate_anchor, hydrate_start, hydrating } from '../hydration.js'; +import { hydrate_anchor, hydrating } from '../hydration.js'; import { empty } from '../operations.js'; import { block, @@ -14,7 +14,7 @@ import { current_component_context } from '../../runtime.js'; import { DEV } from 'esm-env'; /** - * @param {Comment} anchor + * @param {Comment | Element} node * @param {() => string} get_tag * @param {boolean} is_svg * @param {undefined | ((element: Element, anchor: Node | null) => void)} render_fn, @@ -22,7 +22,7 @@ import { DEV } from 'esm-env'; * @param {undefined | [number, number]} location * @returns {void} */ -export function element(anchor, get_tag, is_svg, render_fn, get_namespace, location) { +export function element(node, get_tag, is_svg, render_fn, get_namespace, location) { const filename = DEV && location && current_component_context?.function.filename; /** @type {string | null} */ @@ -32,7 +32,9 @@ export function element(anchor, get_tag, is_svg, render_fn, get_namespace, locat let current_tag; /** @type {null | Element} */ - let element = null; + let element = hydrating && node.nodeType === 1 ? /** @type {Element} */ (node) : null; + + let anchor = /** @type {Comment} */ (hydrating && element ? element.nextSibling : node); /** @type {import('#client').Effect | null} */ let effect; @@ -51,6 +53,7 @@ export function element(anchor, get_tag, is_svg, render_fn, get_namespace, locat : is_svg || next_tag === 'svg' ? namespace_svg : null; + // Assumption: Noone changes the namespace but not the tag (what would that even mean?) if (next_tag === tag) return; @@ -79,7 +82,7 @@ export function element(anchor, get_tag, is_svg, render_fn, get_namespace, locat if (next_tag && next_tag !== current_tag) { effect = branch(() => { element = hydrating - ? /** @type {Element} */ (hydrate_start) + ? /** @type {Element} */ (element) : ns ? document.createElementNS(ns, next_tag) : document.createElement(next_tag); diff --git a/packages/svelte/src/internal/client/dom/operations.js b/packages/svelte/src/internal/client/dom/operations.js index 7fb84aa44c..0d3159a063 100644 --- a/packages/svelte/src/internal/client/dom/operations.js +++ b/packages/svelte/src/internal/client/dom/operations.js @@ -1,6 +1,7 @@ import { hydrate_anchor, hydrate_start, hydrating } from './hydration.js'; import { DEV } from 'esm-env'; import { init_array_prototype_warnings } from '../dev/equality.js'; +import { HYDRATION_END } from '../../../constants.js'; // export these for reference in the compiled code, making global name deduplication unnecessary /** @type {Window} */ @@ -98,12 +99,16 @@ export function first_child(fragment, is_text) { */ /*#__NO_SIDE_EFFECTS__*/ export function sibling(node, is_text = false) { - const next_sibling = node.nextSibling; + var next_sibling = /** @type {import('#client').TemplateNode} */ (node.nextSibling); if (!hydrating) { return next_sibling; } + if (next_sibling.nodeType === 8 && /** @type {Comment} */ (next_sibling).data === '') { + return sibling(next_sibling, is_text); + } + // if a sibling {expression} is empty during SSR, there might be no // text node to hydrate — we must therefore create one if (is_text && next_sibling?.nodeType !== 3) { diff --git a/packages/svelte/tests/server-side-rendering/samples/head-svelte-components-raw-content/_expected.html b/packages/svelte/tests/server-side-rendering/samples/head-svelte-components-raw-content/_expected.html index 3eca221c36..48da62b4de 100644 --- a/packages/svelte/tests/server-side-rendering/samples/head-svelte-components-raw-content/_expected.html +++ b/packages/svelte/tests/server-side-rendering/samples/head-svelte-components-raw-content/_expected.html @@ -1,16 +1,13 @@ -