diff --git a/.changeset/hungry-singers-share.md b/.changeset/hungry-singers-share.md new file mode 100644 index 0000000000..e804144119 --- /dev/null +++ b/.changeset/hungry-singers-share.md @@ -0,0 +1,5 @@ +--- +"svelte": patch +--- + +feat: improve ssr html mismatch validation diff --git a/packages/svelte/src/compiler/phases/1-parse/utils/html.js b/packages/svelte/src/compiler/phases/1-parse/utils/html.js index 1c80aa3554..3cc9a5a207 100644 --- a/packages/svelte/src/compiler/phases/1-parse/utils/html.js +++ b/packages/svelte/src/compiler/phases/1-parse/utils/html.js @@ -1,3 +1,4 @@ +import { interactive_elements } from '../../../../constants.js'; import entities from './entities.js'; const windows_1252 = [ @@ -121,16 +122,6 @@ function validate_code(code) { // based on http://developers.whatwg.org/syntax.html#syntax-tag-omission -// while `input` is also an interactive element, it is never moved by the browser, so we don't need to check for it -export const interactive_elements = new Set([ - 'a', - 'button', - 'iframe', - 'embed', - 'select', - 'textarea' -]); - /** @type {Record>} */ const disallowed_contents = { li: new Set(['li']), @@ -153,36 +144,6 @@ const disallowed_contents = { th: new Set(['td', 'th', 'tr']) }; -export const disallowed_parapgraph_contents = [ - 'address', - 'article', - 'aside', - 'blockquote', - 'details', - 'div', - 'dl', - 'fieldset', - 'figcapture', - 'figure', - 'footer', - 'form', - 'h1', - 'h2', - 'h3', - 'h4', - 'h5', - 'h6', - 'header', - 'hr', - 'menu', - 'nav', - 'ol', - 'pre', - 'section', - 'table', - 'ul' -]; - for (const interactive_element of interactive_elements) { disallowed_contents[interactive_element] = interactive_elements; } diff --git a/packages/svelte/src/compiler/phases/2-analyze/validation.js b/packages/svelte/src/compiler/phases/2-analyze/validation.js index 1790e073ec..55d523ffdc 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/validation.js +++ b/packages/svelte/src/compiler/phases/2-analyze/validation.js @@ -1,3 +1,8 @@ +import { + disallowed_parapgraph_contents, + interactive_elements, + is_tag_valid_with_parent +} from '../../../constants.js'; import { error } from '../../errors.js'; import { extract_identifiers, @@ -8,7 +13,6 @@ import { } from '../../utils/ast.js'; import { warn } from '../../warnings.js'; import fuzzymatch from '../1-parse/utils/fuzzymatch.js'; -import { disallowed_parapgraph_contents, interactive_elements } from '../1-parse/utils/html.js'; import { binding_properties } from '../bindings.js'; import { ContentEditableBindings, EventModifiers, SVGElements } from '../constants.js'; import { is_custom_element_node } from '../nodes.js'; @@ -226,127 +230,6 @@ function validate_slot_attribute(context, attribute) { } } -// https://html.spec.whatwg.org/multipage/syntax.html#generate-implied-end-tags -const implied_end_tags = ['dd', 'dt', 'li', 'option', 'optgroup', 'p', 'rp', 'rt']; - -/** - * @param {string} tag - * @param {string} parent_tag - * @returns {boolean} - */ -function is_tag_valid_with_parent(tag, parent_tag) { - // First, let's check if we're in an unusual parsing mode... - switch (parent_tag) { - // https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-inselect - case 'select': - return tag === 'option' || tag === 'optgroup' || tag === '#text'; - case 'optgroup': - return tag === 'option' || tag === '#text'; - // Strictly speaking, seeing an