From 253f91a16bb1be0ef5e4d9e7458f3960d129ac56 Mon Sep 17 00:00:00 2001 From: Simon Holthausen Date: Tue, 26 Nov 2024 17:15:46 +0100 Subject: [PATCH 01/11] fix: better error messages for invalid HTML trees closes #13331 --- .changeset/green-pumpkins-ring.md | 5 ++ .../98-reference/.generated/compile-errors.md | 2 +- .../.generated/compile-warnings.md | 2 +- .../messages/compile-errors/template.md | 2 +- .../messages/compile-warnings/template.md | 2 +- packages/svelte/src/compiler/errors.js | 9 ++-- .../2-analyze/visitors/ExpressionTag.js | 5 +- .../2-analyze/visitors/RegularElement.js | 18 +++---- .../phases/2-analyze/visitors/Text.js | 5 +- packages/svelte/src/compiler/warnings.js | 9 ++-- packages/svelte/src/html-tree-validation.js | 53 +++++++++++-------- packages/svelte/src/internal/server/dev.js | 17 +++--- .../samples/invalid-html-ssr/_config.js | 4 +- .../invalid-node-placement-2/errors.json | 2 +- .../invalid-node-placement-4/errors.json | 2 +- .../invalid-node-placement-5/warnings.json | 2 +- .../invalid-node-placement-6/errors.json | 2 +- .../invalid-node-placement-7/errors.json | 2 +- .../invalid-node-placement/errors.json | 2 +- 19 files changed, 81 insertions(+), 64 deletions(-) create mode 100644 .changeset/green-pumpkins-ring.md diff --git a/.changeset/green-pumpkins-ring.md b/.changeset/green-pumpkins-ring.md new file mode 100644 index 000000000000..20bed8e3471e --- /dev/null +++ b/.changeset/green-pumpkins-ring.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: better error messages for invalid HTML trees diff --git a/documentation/docs/98-reference/.generated/compile-errors.md b/documentation/docs/98-reference/.generated/compile-errors.md index 16cd361e52c5..2b69f263eac2 100644 --- a/documentation/docs/98-reference/.generated/compile-errors.md +++ b/documentation/docs/98-reference/.generated/compile-errors.md @@ -481,7 +481,7 @@ A component cannot have a default export ### node_invalid_placement ``` -%thing% is invalid inside `<%parent%>` +%message% ``` HTML restricts where certain elements can appear. In case of a violation the browser will 'repair' the HTML in a way that breaks Svelte's assumptions about the structure of your components. Some examples: diff --git a/documentation/docs/98-reference/.generated/compile-warnings.md b/documentation/docs/98-reference/.generated/compile-warnings.md index 775e0681c94f..406b5a375d22 100644 --- a/documentation/docs/98-reference/.generated/compile-warnings.md +++ b/documentation/docs/98-reference/.generated/compile-warnings.md @@ -643,7 +643,7 @@ Svelte 5 components are no longer classes. Instantiate them using `mount` or `hy ### node_invalid_placement_ssr ``` -%thing% is invalid inside `<%parent%>`. When rendering this component on the server, the resulting HTML will be modified by the browser, likely resulting in a `hydration_mismatch` warning +%message%. When rendering this component on the server, the resulting HTML will be modified by the browser, likely resulting in a `hydration_mismatch` warning ``` HTML restricts where certain elements can appear. In case of a violation the browser will 'repair' the HTML in a way that breaks Svelte's assumptions about the structure of your components. Some examples: diff --git a/packages/svelte/messages/compile-errors/template.md b/packages/svelte/messages/compile-errors/template.md index 38ff87d505a5..614bc28cf55d 100644 --- a/packages/svelte/messages/compile-errors/template.md +++ b/packages/svelte/messages/compile-errors/template.md @@ -190,7 +190,7 @@ ## node_invalid_placement -> %thing% is invalid inside `<%parent%>` +> %message% HTML restricts where certain elements can appear. In case of a violation the browser will 'repair' the HTML in a way that breaks Svelte's assumptions about the structure of your components. Some examples: diff --git a/packages/svelte/messages/compile-warnings/template.md b/packages/svelte/messages/compile-warnings/template.md index bfa75ac7f02e..05e0bc18f9f0 100644 --- a/packages/svelte/messages/compile-warnings/template.md +++ b/packages/svelte/messages/compile-warnings/template.md @@ -40,7 +40,7 @@ ## node_invalid_placement_ssr -> %thing% is invalid inside `<%parent%>`. When rendering this component on the server, the resulting HTML will be modified by the browser, likely resulting in a `hydration_mismatch` warning +> %message%. When rendering this component on the server, the resulting HTML will be modified by the browser, likely resulting in a `hydration_mismatch` warning HTML restricts where certain elements can appear. In case of a violation the browser will 'repair' the HTML in a way that breaks Svelte's assumptions about the structure of your components. Some examples: diff --git a/packages/svelte/src/compiler/errors.js b/packages/svelte/src/compiler/errors.js index ad2919b34bd8..ff874d3e967d 100644 --- a/packages/svelte/src/compiler/errors.js +++ b/packages/svelte/src/compiler/errors.js @@ -1024,14 +1024,13 @@ export function mixed_event_handler_syntaxes(node, name) { } /** - * %thing% is invalid inside `<%parent%>` + * %message% * @param {null | number | NodeLike} node - * @param {string} thing - * @param {string} parent + * @param {string} message * @returns {never} */ -export function node_invalid_placement(node, thing, parent) { - e(node, "node_invalid_placement", `${thing} is invalid inside \`<${parent}>\``); +export function node_invalid_placement(node, message) { + e(node, "node_invalid_placement", `${message}`); } /** diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/ExpressionTag.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/ExpressionTag.js index 7e21d5ca1461..c37f0bbb78fe 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/ExpressionTag.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/ExpressionTag.js @@ -12,8 +12,9 @@ export function ExpressionTag(node, context) { const in_attribute = context.path.at(-1)?.type === 'Attribute'; if (!in_attribute && context.state.parent_element) { - if (!is_tag_valid_with_parent('#text', context.state.parent_element)) { - e.node_invalid_placement(node, '`{expression}`', context.state.parent_element); + const message = is_tag_valid_with_parent('#text', context.state.parent_element); + if (message) { + e.node_invalid_placement(node, message); } } diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/RegularElement.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/RegularElement.js index fa6ca0f6e970..7454ab810354 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/RegularElement.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/RegularElement.js @@ -114,15 +114,12 @@ export function RegularElement(node, context) { if (!past_parent) { if (ancestor.type === 'RegularElement' && ancestor.name === context.state.parent_element) { - if (!is_tag_valid_with_parent(node.name, context.state.parent_element)) { + const message = is_tag_valid_with_parent(node.name, context.state.parent_element); + if (message) { if (only_warn) { - w.node_invalid_placement_ssr( - node, - `\`<${node.name}>\``, - context.state.parent_element - ); + w.node_invalid_placement_ssr(node, message); } else { - e.node_invalid_placement(node, `\`<${node.name}>\``, context.state.parent_element); + e.node_invalid_placement(node, message); } } @@ -131,11 +128,12 @@ export function RegularElement(node, context) { } else if (ancestor.type === 'RegularElement') { ancestors.push(ancestor.name); - if (!is_tag_valid_with_ancestor(node.name, ancestors)) { + const message = is_tag_valid_with_ancestor(node.name, ancestors); + if (message) { if (only_warn) { - w.node_invalid_placement_ssr(node, `\`<${node.name}>\``, ancestor.name); + w.node_invalid_placement_ssr(node, message); } else { - e.node_invalid_placement(node, `\`<${node.name}>\``, ancestor.name); + e.node_invalid_placement(node, message); } } } else if ( diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/Text.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/Text.js index 2eca7d43e90a..b18b300eb3ed 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/Text.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/Text.js @@ -12,8 +12,9 @@ export function Text(node, context) { const in_attribute = context.path.at(-1)?.type === 'Attribute'; if (!in_attribute && context.state.parent_element && regex_not_whitespace.test(node.data)) { - if (!is_tag_valid_with_parent('#text', context.state.parent_element)) { - e.node_invalid_placement(node, 'Text node', context.state.parent_element); + const message = is_tag_valid_with_parent('#text', context.state.parent_element); + if (message) { + e.node_invalid_placement(node, message); } } } diff --git a/packages/svelte/src/compiler/warnings.js b/packages/svelte/src/compiler/warnings.js index bd2895623050..842d751890f1 100644 --- a/packages/svelte/src/compiler/warnings.js +++ b/packages/svelte/src/compiler/warnings.js @@ -763,13 +763,12 @@ export function event_directive_deprecated(node, name) { } /** - * %thing% is invalid inside `<%parent%>`. When rendering this component on the server, the resulting HTML will be modified by the browser, likely resulting in a `hydration_mismatch` warning + * %message%. When rendering this component on the server, the resulting HTML will be modified by the browser, likely resulting in a `hydration_mismatch` warning * @param {null | NodeLike} node - * @param {string} thing - * @param {string} parent + * @param {string} message */ -export function node_invalid_placement_ssr(node, thing, parent) { - w(node, "node_invalid_placement_ssr", `${thing} is invalid inside \`<${parent}>\`. When rendering this component on the server, the resulting HTML will be modified by the browser, likely resulting in a \`hydration_mismatch\` warning`); +export function node_invalid_placement_ssr(node, message) { + w(node, "node_invalid_placement_ssr", `${message}. When rendering this component on the server, the resulting HTML will be modified by the browser, likely resulting in a \`hydration_mismatch\` warning`); } /** diff --git a/packages/svelte/src/html-tree-validation.js b/packages/svelte/src/html-tree-validation.js index 0ebf45e166c2..4f1c9e51182e 100644 --- a/packages/svelte/src/html-tree-validation.js +++ b/packages/svelte/src/html-tree-validation.js @@ -135,58 +135,70 @@ const disallowed_children = { }; /** - * Returns false if the tag is not allowed inside the ancestor tag (which is grandparent and above) such that it will result + * Returns an error message if the tag is not allowed inside the ancestor tag (which is grandparent and above) 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[]} ancestors All nodes starting with the parent, up until the ancestor, which means two entries minimum - * @returns {boolean} + * @returns {string | null} */ export function is_tag_valid_with_ancestor(tag, ancestors) { - if (tag.includes('-')) return true; // custom elements can be anything + if (tag.includes('-')) return null; // custom elements can be anything const target = ancestors[ancestors.length - 1]; const disallowed = disallowed_children[target]; - if (!disallowed) return true; + if (!disallowed) return null; if ('reset_by' in disallowed && disallowed.reset_by) { for (let i = ancestors.length - 2; i >= 0; i--) { const ancestor = ancestors[i]; - if (ancestor.includes('-')) return true; // custom elements can be anything + if (ancestor.includes('-')) return null; // custom elements can be anything // A reset means that forbidden descendants are allowed again if (disallowed.reset_by.includes(ancestors[i])) { - return true; + return null; } } } - return 'descendant' in disallowed ? !disallowed.descendant.includes(tag) : true; + if ('descendant' in disallowed && disallowed.descendant.includes(tag)) { + return `\`<${tag}>\` cannot be a child of \`<${target}>\`. \`<${target}>\` disallows these children: ${disallowed.descendant.map((d) => `\`<${d}>\``).join(', ')}`; + } + + return null; } /** - * Returns false if the tag is not allowed inside the parent tag such that it will result + * Returns an error message 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 - * @returns {boolean} + * @returns {string | null} */ export function is_tag_valid_with_parent(tag, parent_tag) { - if (tag.includes('-') || parent_tag?.includes('-')) return true; // custom elements can be anything + if (tag.includes('-') || parent_tag?.includes('-')) return null; // custom elements can be anything const disallowed = disallowed_children[parent_tag]; if (disallowed) { if ('direct' in disallowed && disallowed.direct.includes(tag)) { - return false; + return `\`<${tag}>\` cannot be a direct child of \`<${parent_tag}>\``; } if ('descendant' in disallowed && disallowed.descendant.includes(tag)) { - return false; + return `\`<${tag}>\` cannot be a child of \`<${parent_tag}>\``; } if ('only' in disallowed && disallowed.only) { - return disallowed.only.includes(tag); + if (disallowed.only.includes(tag)) { + return null; + } else { + return `\`<${tag}>\` cannot be a child of \`<${parent_tag}>\`. \`<${parent_tag}>\` only allows these children: ${disallowed.only.map((d) => `\`<${d}>\``).join(', ')}`; + } } } + // These tags are only valid with a few parents that have special child + // parsing rules - if we're down here, then none of those matched and + // so we allow it only if we don't know what the parent is, as all other + // cases are invalid (and we only get into this function if we know the parent). switch (tag) { case 'body': case 'caption': @@ -196,18 +208,17 @@ export function is_tag_valid_with_parent(tag, parent_tag) { case 'frame': case 'head': case 'html': + return `\`<${tag}>\` cannot be a child of \`<${parent_tag}>\``; + case 'thead': case 'tbody': - case 'td': case 'tfoot': + return `\`<${tag}>\` must be the child of a \`\` (but got \`<${parent_tag}>\`)`; + case 'td': case 'th': - case 'thead': + return `\`<${tag}>\` must be the child of a \`\` (but got \`<${parent_tag}>\`)`; case 'tr': - // These tags are only valid with a few parents that have special child - // parsing rules - if we're down here, then none of those matched and - // so we allow it only if we don't know what the parent is, as all other - // cases are invalid (and we only get into this function if we know the parent). - return false; + return `\`\` must be the child of a \`\`, \`\`, or \`\` (but got \`<${parent_tag}>\`)`; } - return true; + return null; } diff --git a/packages/svelte/src/internal/server/dev.js b/packages/svelte/src/internal/server/dev.js index 145b37479b3f..da24d82256c2 100644 --- a/packages/svelte/src/internal/server/dev.js +++ b/packages/svelte/src/internal/server/dev.js @@ -36,10 +36,11 @@ function stringify(element) { * @param {Payload} payload * @param {Element} parent * @param {Element} child + * @param {string} message */ -function print_error(payload, parent, child) { - var message = - `node_invalid_placement_ssr: ${stringify(parent)} cannot contain ${stringify(child)}\n\n` + +function print_error(payload, parent, child, message) { + message = + `node_invalid_placement_ssr: ${stringify(parent)} cannot contain ${stringify(child)} (${message})\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; @@ -72,14 +73,16 @@ export function push_element(payload, tag, line, column) { var ancestor = parent.parent; var ancestors = [parent.tag]; - if (!is_tag_valid_with_parent(tag, parent.tag)) { - print_error(payload, parent, child); + const message = is_tag_valid_with_parent(tag, parent.tag); + if (message) { + print_error(payload, parent, child, message); } while (ancestor != null) { ancestors.push(ancestor.tag); - if (!is_tag_valid_with_ancestor(tag, ancestors)) { - print_error(payload, ancestor, child); + const message = is_tag_valid_with_ancestor(tag, ancestors); + if (message) { + print_error(payload, parent, child, message); } ancestor = ancestor.parent; } diff --git a/packages/svelte/tests/runtime-runes/samples/invalid-html-ssr/_config.js b/packages/svelte/tests/runtime-runes/samples/invalid-html-ssr/_config.js index d815d10fc73e..e73c2f8e8c7e 100644 --- a/packages/svelte/tests/runtime-runes/samples/invalid-html-ssr/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/invalid-html-ssr/_config.js @@ -12,8 +12,8 @@ export default test({ mode: ['hydrate'], errors: [ - 'node_invalid_placement_ssr: `

` (main.svelte:6:0) cannot contain `

` (h1.svelte:1:0)\n\nThis can cause content to shift around as the browser repairs the HTML, and will likely result in a `hydration_mismatch` warning.', - 'node_invalid_placement_ssr: `
` (main.svelte:9:0) cannot contain `` (form.svelte:1:0)\n\nThis can cause content to shift around as the browser repairs the HTML, and will likely result in a `hydration_mismatch` warning.' + 'node_invalid_placement_ssr: `

` (main.svelte:6:0) cannot contain `

` (h1.svelte:1:0) (`

` cannot be a child of `

`)\n\nThis can cause content to shift around as the browser repairs the HTML, and will likely result in a `hydration_mismatch` warning.', + 'node_invalid_placement_ssr: `` (main.svelte:9:0) cannot contain `` (form.svelte:1:0) (`` cannot be a child of ``)\n\nThis can cause content to shift around as the browser repairs the HTML, and will likely result in a `hydration_mismatch` warning.' ], warnings: [ diff --git a/packages/svelte/tests/validator/samples/invalid-node-placement-2/errors.json b/packages/svelte/tests/validator/samples/invalid-node-placement-2/errors.json index 3d9786582343..03ee860efc04 100644 --- a/packages/svelte/tests/validator/samples/invalid-node-placement-2/errors.json +++ b/packages/svelte/tests/validator/samples/invalid-node-placement-2/errors.json @@ -1,7 +1,7 @@ [ { "code": "node_invalid_placement", - "message": "`

` is invalid inside `

`", + "message": "`

` cannot be a child of `

`. `

` disallows these children: `

`, `
`, `

`, `` is invalid inside `
`", + "message": "`
` must be the child of a `
` (but got `
`)", "start": { "line": 8, "column": 1 diff --git a/packages/svelte/tests/validator/samples/invalid-node-placement/errors.json b/packages/svelte/tests/validator/samples/invalid-node-placement/errors.json index b9bdad5e72b9..7d52e0d11c05 100644 --- a/packages/svelte/tests/validator/samples/invalid-node-placement/errors.json +++ b/packages/svelte/tests/validator/samples/invalid-node-placement/errors.json @@ -1,7 +1,7 @@ [ { "code": "node_invalid_placement", - "message": "`` is invalid inside ``", + "message": "`` cannot be a child of ``. `` disallows these children: ``", "start": { "line": 4, "column": 6 From 98bb5ee5b8a81fcb0e9d131762b55b877a9353ee Mon Sep 17 00:00:00 2001 From: Simon Holthausen Date: Tue, 26 Nov 2024 17:54:47 +0100 Subject: [PATCH 02/11] fix test --- .../samples/invalid-nested-svelte-element/_config.js | 2 +- .../samples/invalid-nested-svelte-element/_expected_head.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/svelte/tests/server-side-rendering/samples/invalid-nested-svelte-element/_config.js b/packages/svelte/tests/server-side-rendering/samples/invalid-nested-svelte-element/_config.js index 9b2688711ded..172c695a7beb 100644 --- a/packages/svelte/tests/server-side-rendering/samples/invalid-nested-svelte-element/_config.js +++ b/packages/svelte/tests/server-side-rendering/samples/invalid-nested-svelte-element/_config.js @@ -6,6 +6,6 @@ export default test({ }, errors: [ - 'node_invalid_placement_ssr: `

` (packages/svelte/tests/server-side-rendering/samples/invalid-nested-svelte-element/main.svelte:1:0) cannot contain `

` (packages/svelte/tests/server-side-rendering/samples/invalid-nested-svelte-element/main.svelte:2:1)\n\nThis can cause content to shift around as the browser repairs the HTML, and will likely result in a `hydration_mismatch` warning.' + 'node_invalid_placement_ssr: `

` (packages/svelte/tests/server-side-rendering/samples/invalid-nested-svelte-element/main.svelte:1:0) cannot contain `

` (packages/svelte/tests/server-side-rendering/samples/invalid-nested-svelte-element/main.svelte:2:1) (`

` cannot be a child of `

`)\n\nThis can cause content to shift around as the browser repairs the HTML, and will likely result in a `hydration_mismatch` warning.' ] }); diff --git a/packages/svelte/tests/server-side-rendering/samples/invalid-nested-svelte-element/_expected_head.html b/packages/svelte/tests/server-side-rendering/samples/invalid-nested-svelte-element/_expected_head.html index 27c37f693baa..bf6e59d5780e 100644 --- a/packages/svelte/tests/server-side-rendering/samples/invalid-nested-svelte-element/_expected_head.html +++ b/packages/svelte/tests/server-side-rendering/samples/invalid-nested-svelte-element/_expected_head.html @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file From 658c6ab9f60b4c2de9b502432e7c4e00965cd0bc Mon Sep 17 00:00:00 2001 From: Simon Holthausen Date: Wed, 27 Nov 2024 11:17:18 +0100 Subject: [PATCH 03/11] more concise --- packages/svelte/src/html-tree-validation.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/svelte/src/html-tree-validation.js b/packages/svelte/src/html-tree-validation.js index 4f1c9e51182e..a354b7de6a8a 100644 --- a/packages/svelte/src/html-tree-validation.js +++ b/packages/svelte/src/html-tree-validation.js @@ -161,7 +161,7 @@ export function is_tag_valid_with_ancestor(tag, ancestors) { } if ('descendant' in disallowed && disallowed.descendant.includes(tag)) { - return `\`<${tag}>\` cannot be a child of \`<${target}>\`. \`<${target}>\` disallows these children: ${disallowed.descendant.map((d) => `\`<${d}>\``).join(', ')}`; + return `\`<${tag}>\` cannot be a child of \`<${target}>\``; } return null; From 77107fa3863535350fc28da4e18df22926c7d07b Mon Sep 17 00:00:00 2001 From: Simon Holthausen Date: Wed, 27 Nov 2024 11:22:52 +0100 Subject: [PATCH 04/11] tweak --- packages/svelte/src/html-tree-validation.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/svelte/src/html-tree-validation.js b/packages/svelte/src/html-tree-validation.js index a354b7de6a8a..8cb47d7b960b 100644 --- a/packages/svelte/src/html-tree-validation.js +++ b/packages/svelte/src/html-tree-validation.js @@ -161,7 +161,7 @@ export function is_tag_valid_with_ancestor(tag, ancestors) { } if ('descendant' in disallowed && disallowed.descendant.includes(tag)) { - return `\`<${tag}>\` cannot be a child of \`<${target}>\``; + return `\`<${tag}>\` cannot be a descendant of \`<${target}>\``; } return null; From 4639bb5f6ab2c3f1da32cb64efaad8003545ae59 Mon Sep 17 00:00:00 2001 From: Simon Holthausen Date: Wed, 27 Nov 2024 11:29:07 +0100 Subject: [PATCH 05/11] tweak messages --- documentation/docs/98-reference/.generated/compile-errors.md | 4 ++-- .../docs/98-reference/.generated/compile-warnings.md | 4 ++-- packages/svelte/messages/compile-errors/template.md | 4 ++-- packages/svelte/messages/compile-warnings/template.md | 4 ++-- packages/svelte/src/compiler/errors.js | 4 ++-- packages/svelte/src/compiler/warnings.js | 4 ++-- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/documentation/docs/98-reference/.generated/compile-errors.md b/documentation/docs/98-reference/.generated/compile-errors.md index 2b69f263eac2..fe71b0eef6e4 100644 --- a/documentation/docs/98-reference/.generated/compile-errors.md +++ b/documentation/docs/98-reference/.generated/compile-errors.md @@ -481,12 +481,12 @@ A component cannot have a default export ### node_invalid_placement ``` -%message% +%message%. The browser will 'repair' the HTML (by moving, removing, or inserting elements) which breaks Svelte's assumptions about the structure of your components. ``` HTML restricts where certain elements can appear. In case of a violation the browser will 'repair' the HTML in a way that breaks Svelte's assumptions about the structure of your components. Some examples: -- `

hello

world

` will result in `

hello

world

` for example (the `
` autoclosed the `

` because `

` cannot contain block-level elements) +- `

hello

world

` will result in `

hello

world

` (the `
` autoclosed the `

` because `

` cannot contain block-level elements) - `

option a
` will result in `` (the `
` is removed) - `
cell
` will result in `
cell
` (a `` is auto-inserted) diff --git a/documentation/docs/98-reference/.generated/compile-warnings.md b/documentation/docs/98-reference/.generated/compile-warnings.md index 406b5a375d22..f30244511455 100644 --- a/documentation/docs/98-reference/.generated/compile-warnings.md +++ b/documentation/docs/98-reference/.generated/compile-warnings.md @@ -643,12 +643,12 @@ Svelte 5 components are no longer classes. Instantiate them using `mount` or `hy ### node_invalid_placement_ssr ``` -%message%. When rendering this component on the server, the resulting HTML will be modified by the browser, likely resulting in a `hydration_mismatch` warning +%message%. When rendering this component on the server, the resulting HTML will be modified by the browser (by moving, removing, or inserting elements), likely resulting in a `hydration_mismatch` warning ``` HTML restricts where certain elements can appear. In case of a violation the browser will 'repair' the HTML in a way that breaks Svelte's assumptions about the structure of your components. Some examples: -- `

hello

world

` will result in `

hello

world

` for example (the `
` autoclosed the `

` because `

` cannot contain block-level elements) +- `

hello

world

` will result in `

hello

world

` (the `
` autoclosed the `

` because `

` cannot contain block-level elements) - `

option a
` will result in `` (the `
` is removed) - `
cell
` will result in `
cell
` (a `` is auto-inserted) diff --git a/packages/svelte/messages/compile-errors/template.md b/packages/svelte/messages/compile-errors/template.md index 614bc28cf55d..97bf0c30cc86 100644 --- a/packages/svelte/messages/compile-errors/template.md +++ b/packages/svelte/messages/compile-errors/template.md @@ -190,11 +190,11 @@ ## node_invalid_placement -> %message% +> %message%. The browser will 'repair' the HTML (by moving, removing, or inserting elements) which breaks Svelte's assumptions about the structure of your components. HTML restricts where certain elements can appear. In case of a violation the browser will 'repair' the HTML in a way that breaks Svelte's assumptions about the structure of your components. Some examples: -- `

hello

world

` will result in `

hello

world

` for example (the `
` autoclosed the `

` because `

` cannot contain block-level elements) +- `

hello

world

` will result in `

hello

world

` (the `
` autoclosed the `

` because `

` cannot contain block-level elements) - `

option a
` will result in `` (the `
` is removed) - `
cell
` will result in `
cell
` (a `` is auto-inserted) diff --git a/packages/svelte/messages/compile-warnings/template.md b/packages/svelte/messages/compile-warnings/template.md index 05e0bc18f9f0..690681c172a3 100644 --- a/packages/svelte/messages/compile-warnings/template.md +++ b/packages/svelte/messages/compile-warnings/template.md @@ -40,11 +40,11 @@ ## node_invalid_placement_ssr -> %message%. When rendering this component on the server, the resulting HTML will be modified by the browser, likely resulting in a `hydration_mismatch` warning +> %message%. When rendering this component on the server, the resulting HTML will be modified by the browser (by moving, removing, or inserting elements), likely resulting in a `hydration_mismatch` warning HTML restricts where certain elements can appear. In case of a violation the browser will 'repair' the HTML in a way that breaks Svelte's assumptions about the structure of your components. Some examples: -- `

hello

world

` will result in `

hello

world

` for example (the `
` autoclosed the `

` because `

` cannot contain block-level elements) +- `

hello

world

` will result in `

hello

world

` (the `
` autoclosed the `

` because `

` cannot contain block-level elements) - `

option a
` will result in `` (the `
` is removed) - `
cell
` will result in `
cell
` (a `` is auto-inserted) diff --git a/packages/svelte/src/compiler/errors.js b/packages/svelte/src/compiler/errors.js index ff874d3e967d..d23acf8bda29 100644 --- a/packages/svelte/src/compiler/errors.js +++ b/packages/svelte/src/compiler/errors.js @@ -1024,13 +1024,13 @@ export function mixed_event_handler_syntaxes(node, name) { } /** - * %message% + * %message%. The browser will 'repair' the HTML (by moving, removing, or inserting elements) which breaks Svelte's assumptions about the structure of your components. * @param {null | number | NodeLike} node * @param {string} message * @returns {never} */ export function node_invalid_placement(node, message) { - e(node, "node_invalid_placement", `${message}`); + e(node, "node_invalid_placement", `${message}. The browser will 'repair' the HTML (by moving, removing, or inserting elements) which breaks Svelte's assumptions about the structure of your components.`); } /** diff --git a/packages/svelte/src/compiler/warnings.js b/packages/svelte/src/compiler/warnings.js index 842d751890f1..de0805d7014f 100644 --- a/packages/svelte/src/compiler/warnings.js +++ b/packages/svelte/src/compiler/warnings.js @@ -763,12 +763,12 @@ export function event_directive_deprecated(node, name) { } /** - * %message%. When rendering this component on the server, the resulting HTML will be modified by the browser, likely resulting in a `hydration_mismatch` warning + * %message%. When rendering this component on the server, the resulting HTML will be modified by the browser (by moving, removing, or inserting elements), likely resulting in a `hydration_mismatch` warning * @param {null | NodeLike} node * @param {string} message */ export function node_invalid_placement_ssr(node, message) { - w(node, "node_invalid_placement_ssr", `${message}. When rendering this component on the server, the resulting HTML will be modified by the browser, likely resulting in a \`hydration_mismatch\` warning`); + w(node, "node_invalid_placement_ssr", `${message}. When rendering this component on the server, the resulting HTML will be modified by the browser (by moving, removing, or inserting elements), likely resulting in a \`hydration_mismatch\` warning`); } /** From 3f93de6472248f99ed56b5cf379861f9124d9b61 Mon Sep 17 00:00:00 2001 From: Simon Holthausen Date: Wed, 27 Nov 2024 11:34:34 +0100 Subject: [PATCH 06/11] adjust tests --- .../validator/samples/invalid-node-placement-2/errors.json | 2 +- .../validator/samples/invalid-node-placement-4/errors.json | 2 +- .../validator/samples/invalid-node-placement-5/warnings.json | 2 +- .../validator/samples/invalid-node-placement-6/errors.json | 2 +- .../validator/samples/invalid-node-placement-7/errors.json | 2 +- .../tests/validator/samples/invalid-node-placement/errors.json | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/svelte/tests/validator/samples/invalid-node-placement-2/errors.json b/packages/svelte/tests/validator/samples/invalid-node-placement-2/errors.json index 03ee860efc04..abbded296af1 100644 --- a/packages/svelte/tests/validator/samples/invalid-node-placement-2/errors.json +++ b/packages/svelte/tests/validator/samples/invalid-node-placement-2/errors.json @@ -1,7 +1,7 @@ [ { "code": "node_invalid_placement", - "message": "`
` cannot be a child of `

`. `

` disallows these children: `

`, `
`, `