From 99e1665ce18410c4a3266ca6cd489a53b1d7fcf3 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Wed, 28 Feb 2024 09:16:31 +0000 Subject: [PATCH] feat: improve ssr html mismatch validation (#10658) * feat: improve ssr html mismatch validation * update types * Update packages/svelte/src/internal/server/index.js Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com> * Update packages/svelte/src/compiler/validate-options.js Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com> * feedback --------- Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com> --- .changeset/hungry-singers-share.md | 5 + .../src/compiler/phases/1-parse/utils/html.js | 41 +---- .../compiler/phases/2-analyze/validation.js | 127 +------------- .../3-transform/server/transform-server.js | 18 ++ packages/svelte/src/constants.js | 161 ++++++++++++++++++ packages/svelte/src/internal/server/index.js | 71 +++++++- packages/svelte/src/legacy/legacy-client.js | 2 +- .../svelte/tests/runtime-legacy/shared.ts | 3 +- .../samples/invalid-html-ssr/Component.svelte | 1 + .../samples/invalid-html-ssr/_config.js | 41 +++++ .../samples/invalid-html-ssr/main.svelte | 7 + packages/svelte/types/index.d.ts | 2 +- 12 files changed, 313 insertions(+), 166 deletions(-) create mode 100644 .changeset/hungry-singers-share.md create mode 100644 packages/svelte/tests/runtime-runes/samples/invalid-html-ssr/Component.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/invalid-html-ssr/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/invalid-html-ssr/main.svelte diff --git a/.changeset/hungry-singers-share.md b/.changeset/hungry-singers-share.md new file mode 100644 index 000000000..e80414411 --- /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 1c80aa355..3cc9a5a20 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 1790e073e..55d523ffd 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