fix: throw runtime error when template returns different html

invalid-html-structure-error
paoloricciuti 6 months ago
parent 32ee6c1bc2
commit a9b26c3ffe

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: throw runtime error when template returns different html

@ -80,6 +80,12 @@ Maximum update depth exceeded. This can happen when a reactive block or effect r
Failed to hydrate the application
```
### invalid_html_structure
```
This html structure `%html_input%` would be corrected like this `%html_output%` by the browser making this component impossible to hydrate properly
```
### invalid_snippet
```

@ -52,6 +52,10 @@ See the [migration guide](/docs/svelte/v5-migration-guide#Components-are-no-long
> Failed to hydrate the application
## invalid_html_structure
> This html structure `%html_input%` would be corrected like this `%html_output%` by the browser making this component impossible to hydrate properly
## invalid_snippet
> Could not `{@render}` snippet due to the expression being `null` or `undefined`. Consider using optional chaining `{@render snippet?.()}`

@ -99,7 +99,7 @@ export function html(node, get_value, svg, mathml, skip_warning) {
// Don't use create_fragment_with_script_from_html here because that would mean script tags are executed.
// @html is basically `.innerHTML = ...` and that doesn't execute scripts either due to security reasons.
/** @type {DocumentFragment | Element} */
var node = create_fragment_from_html(html);
var node = create_fragment_from_html(html, false);
if (svg || mathml) {
node = /** @type {Element} */ (get_first_child(node));

@ -1,6 +1,29 @@
/** @param {string} html */
export function create_fragment_from_html(html) {
import { DEV } from 'esm-env';
import * as e from '../errors.js';
/**
* @param {string} html
* @param {boolean} [check_structure]
*/
export function create_fragment_from_html(html, check_structure = true) {
var elem = document.createElement('template');
elem.innerHTML = html;
if (DEV && check_structure) {
let replace_comments = html.replaceAll('<!>', '<!---->');
let remove_attributes_and_text_input = replace_comments
// we remove every attribute since the template automatically adds ="" after boolean attributes
.replace(/<([a-z0-9]+)(\s+[^>]+?)?>/g, '<$1>')
// we remove the text within the elements because the template change & to &amp; (and similar)
.replace(/>([^<>]*)/g, '>');
let remove_attributes_and_text_output = elem.innerHTML
// we remove every attribute since the template automatically adds ="" after boolean attributes
.replace(/<([a-z0-9]+)(\s+[^>]+?)?>/g, '<$1>')
// we remove the text within the elements because the template change & to &amp; (and similar)
.replace(/>([^<>]*)/g, '>');
if (remove_attributes_and_text_input !== remove_attributes_and_text_output) {
e.invalid_html_structure(remove_attributes_and_text_input, remove_attributes_and_text_output);
}
}
return elem.content;
}

@ -198,6 +198,23 @@ export function hydration_failed() {
}
}
/**
* This html structure `%html_input%` would be corrected like this `%html_output%` by the browser making this component impossible to hydrate properly
* @param {string} html_input
* @param {string} html_output
* @returns {never}
*/
export function invalid_html_structure(html_input, html_output) {
if (DEV) {
const error = new Error(`invalid_html_structure\nThis html structure \`${html_input}\` would be corrected like this \`${html_output}\` by the browser making this component impossible to hydrate properly\nhttps://svelte.dev/e/invalid_html_structure`);
error.name = 'Svelte error';
throw error;
} else {
throw new Error(`https://svelte.dev/e/invalid_html_structure`);
}
}
/**
* Could not `{@render}` snippet due to the expression being `null` or `undefined`. Consider using optional chaining `{@render snippet?.()}`
* @returns {never}

@ -0,0 +1,7 @@
import { test } from '../../test';
export default test({
mode: ['client', 'hydrate'],
recover: true,
runtime_error: 'invalid_html_structure'
});
Loading…
Cancel
Save