diff --git a/.changeset/strange-trains-destroy.md b/.changeset/strange-trains-destroy.md new file mode 100644 index 0000000000..136ed82bd1 --- /dev/null +++ b/.changeset/strange-trains-destroy.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +chore: improve ssr parent validation diff --git a/packages/svelte/src/html-tree-validation.js b/packages/svelte/src/html-tree-validation.js index fb4fbeadf5..9a191ba3f1 100644 --- a/packages/svelte/src/html-tree-validation.js +++ b/packages/svelte/src/html-tree-validation.js @@ -162,21 +162,23 @@ export function is_tag_valid_with_ancestor(tag, ancestors) { * Returns false if the tag is not allowed inside the parent tag such that it will result * in the browser repairing the HTML, which will likely result in an error during hydration. * @param {string} tag - * @param {string} parent_tag + * @param {string | null} parent_tag * @returns {boolean} */ export function is_tag_valid_with_parent(tag, parent_tag) { - const disallowed = disallowed_children[parent_tag]; + if (parent_tag !== null) { + const disallowed = disallowed_children[parent_tag]; - if (disallowed) { - if ('direct' in disallowed && disallowed.direct.includes(tag)) { - return false; - } - if ('descendant' in disallowed && disallowed.descendant.includes(tag)) { - return false; - } - if ('only' in disallowed && disallowed.only) { - return disallowed.only.includes(tag); + if (disallowed) { + if ('direct' in disallowed && disallowed.direct.includes(tag)) { + return false; + } + if ('descendant' in disallowed && disallowed.descendant.includes(tag)) { + return false; + } + if ('only' in disallowed && disallowed.only) { + return disallowed.only.includes(tag); + } } } diff --git a/packages/svelte/src/internal/server/dev.js b/packages/svelte/src/internal/server/dev.js index 16ca9a494b..ffa5c31aa0 100644 --- a/packages/svelte/src/internal/server/dev.js +++ b/packages/svelte/src/internal/server/dev.js @@ -34,12 +34,14 @@ function stringify(element) { /** * @param {Payload} payload - * @param {Element} parent + * @param {Element | null} parent * @param {Element} child */ function print_error(payload, parent, child) { var message = - `node_invalid_placement_ssr: ${stringify(parent)} cannot contain ${stringify(child)}\n\n` + + (parent === null + ? `node_invalid_placement_ssr: ${stringify(child)} needs a valid parent element\n\n` + : `node_invalid_placement_ssr: ${stringify(parent)} cannot contain ${stringify(child)}\n\n`) + 'This can cause content to shift around as the browser repairs the HTML, and will likely result in a `hydration_mismatch` warning.'; if ((seen ??= new Set()).has(message)) return; @@ -79,6 +81,8 @@ export function push_element(payload, tag, line, column) { } ancestor = ancestor.parent; } + } else if (!is_tag_valid_with_parent(tag, null)) { + print_error(payload, null, child); } parent = child;