8000 fix: move the error to a warning · sveltejs/svelte@62a6a15 · GitHub
[go: up one dir, main page]

Skip to content

Commit 62a6a15

Browse files
committed
fix: move the error to a warning
1 parent a9b26c3 commit 62a6a15

File tree

12 files changed

+76
-37
lines changed

12 files changed

+76
-37
lines changed

.changeset/fluffy-eggs-do.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22
'svelte': patch
33
---
44

5-
fix: throw runtime error when template returns different html
5+
fix: throw runtime warning when template returns different html

documentation/docs/98-reference/.generated/client-errors.md

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -80,12 +80,6 @@ Maximum update depth exceeded. This can happen when a reactive block or effect r
8080
Failed to hydrate the application
8181
```
8282

83-
### invalid_html_structure
84-
85-
```
86-
This html structure `%html_input%` would be corrected like this `%html_output%` by the browser making this component impossible to hydrate properly
87-
```
88-
8983
### invalid_snippet
9084

9185
```

documentation/docs/98-reference/.generated/client-warnings.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,12 @@ This warning is thrown when Svelte encounters an error while hydrating the HTML
140140
141141
During development, this error is often preceeded by a `console.error` detailing the offending HTML, which needs fixing.
142142
143+
### invalid_html_structure
144+
145+
```
146+
This html structure `%html_input%` would be corrected like this `%html_output%` by the browser making this component impossible to hydrate properly
147+
```
148+
143149
### invalid_raw_snippet_render
144150
145151
```

packages/svelte/messages/client-errors/errors.md

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,6 @@ See the [migration guide](/docs/svelte/v5-migration-guide#Components-are-no-long
5252

5353
> Failed to hydrate the application
5454
55-
## invalid_html_structure
56-
57-
> This html structure `%html_input%` would be corrected like this `%html_output%` by the browser making this component impossible to hydrate properly
58-
5955
## invalid_snippet
6056

6157
> Could not `{@render}` snippet due to the expression being `null` or `undefined`. Consider using optional chaining `{@render snippet?.()}`

packages/svelte/messages/client-warnings/warnings.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,10 @@ This warning is thrown when Svelte encounters an error while hydrating the HTML
118118
119119
During development, this error is often preceeded by a `console.error` detailing the offending HTML, which needs fixing.
120120
121+
## invalid_html_structure
122+
123+
> This html structure `%html_input%` would be corrected like this `%html_output%` by the browser making this component impossible to hydrate properly
124+
121125
## invalid_raw_snippet_render
122126
123127
> The `render` function passed to `createRawSnippet` should return HTML for a single element

packages/svelte/src/internal/client/dom/blocks/html.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ export function html(node, get_value, svg, mathml, skip_warning) {
9999
// Don't use create_fragment_with_script_from_html here because that would mean script tags are executed.
100100
// @html is basically `.innerHTML = ...` and that doesn't execute scripts either due to security reasons.
101101
/** @type {DocumentFragment | Element} */
102-
var node = create_fragment_from_html(html, false);
102+
var node = create_fragment_from_html(html);
103103

104104
if (svg || mathml) {
105105
node = /** @type {Element} */ (get_first_child(node));

packages/svelte/src/internal/client/dom/reconciler.js

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
import { DEV } from 'esm-env';
2-
import * as e from '../errors.js';
2+
import * as w from '../warnings.js';
33

44
/**
55
* @param {string} html
6-
* @param {boolean} [check_structure]
76
*/
8-
export function create_fragment_from_html(html, check_structure = true) {
7+
export function create_fragment_from_html(html) {
98
var elem = document.createElement('template');
109
elem.innerHTML = html;
11-
if (DEV && check_structure) {
10+
if (DEV) {
1211
let replace_comments = html.replaceAll('<!>', '<!---->');
1312
let remove_attributes_and_text_input = replace_comments
1413
// we remove every attribute since the template automatically adds ="" after boolean attributes
@@ -21,7 +20,7 @@ export function create_fragment_from_html(html, check_structure = true) {
2120
// we remove the text within the elements because the template change & to &amp; (and similar)
2221
.replace(/>([^<>]*)/g, '>');
2322
if (remove_attributes_and_text_input !== remove_attributes_and_text_output) {
24-
e.invalid_html_structure(remove_attributes_and_text_input, remove_attributes_and_text_output);
23+
w.invalid_html_structure(remove_attributes_and_text_input, remove_attributes_and_text_output);
2524
}
2625
}
2726

packages/svelte/src/internal/client/dom/template.js

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { create_text, get_first_child, is_firefox } from './operations.js';
44
import { create_fragment_from_html } from './reconciler.js';
55
import { active_effect } from '../runtime.js';
66
import { TEMPLATE_FRAGMENT, TEMPLATE_USE_IMPORT_NODE } from '../../../constants.js';
7+
import { DEV } from 'esm-env';
78

89
/**
910
* @param {TemplateNode} start
@@ -36,15 +37,30 @@ export function template(content, flags) {
3637
*/
3738
var has_start = !content.startsWith('<!>');
3839

40+
function create_node() {
41+
node = create_fragment_from_html(has_start ? content : '<!>' + content);
42+
}
43+
44+
let eagerly_created = false;
45+
46+
if (DEV) {
47+
eagerly_created = true;
48+
// in dev we eagerly create the node to provide warnings in case of mismatches
49+
create_node();
50+
}
51+
3952
return () => {
4053
if (hydrating) {
4154
assign_nodes(hydrate_node, null);
4255
return hydrate_node;
4356
}
4457

4558
if (node === undefined) {
46-
node = create_fragment_from_html(has_start ? content : '<!>' + content);
59+
create_node();
4760
if (!is_fragment) node = /** @type {Node} */ (get_first_child(node));
61+
} else if (eagerly_created && !is_fragment) {
62+
eagerly_created = false;
63+
node = /** @type {Node} */ (get_first_child(node));
4864
}
4965

5066
var clone = /** @type {TemplateNode} */ (

packages/svelte/src/internal/client/errors.js

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -198,23 +198,6 @@ export function hydration_failed() {
198198
}
199199
}
200200

201-
/**
202-
* This html structure `%html_input%` would be corrected like this `%html_output%` by the browser making this component impossible to hydrate properly
203-
* @param {string} html_input
204-
* @param {string} html_output
205-
* @returns {never}
206-
*/
207-
export function invalid_html_structure(html_input, html_output) {
208-
if (DEV) {
209-
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`);
210-
211-
error.name = 'Svelte error';
212-
throw error;
213-
} else {
214-
throw new Error(`https://svelte.dev/e/invalid_html_structure`);
215-
}
216-
}
217-
218201
/**
219202
* Could not `{@render}` snippet due to the expression being `null` or `undefined`. Consider using optional chaining `{@render snippet?.()}`
220203
* @returns {never}

packages/svelte/src/internal/client/warnings.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,19 @@ export function hydration_mismatch(location) {
9494
}
9595
}
9696

97+
/**
98+
* This html structure `%html_input%` would be corrected like this `%html_output%` by the browser making this component impossible to hydrate properly
99+
* @param {string} html_input
100+
* @param {string} html_output
101+
*/
102+
export function invalid_html_structure(html_input, html_output) {
103+
if (DEV) {
104+
console.warn(`%c[svelte] invalid_html_structure\n%cThis 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`, bold, normal);
105+
} else {
106+
console.warn(`https://svelte.dev/e/invalid_html_structure`);
107+
}
108+
}
109+
97110
/**
98111
* The `render` function passed to `createRawSnippet` should return HTML for a single element
99112
*/

packages/svelte/tests/runtime-legacy/shared.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ export interface RuntimeTest<Props extends Record<string, any> = Record<string,
3737
props?: Props;
3838
server_props?: Props;
3939
id_prefix?: string;
40+
needs_import_logs?: boolean;
4041
before_test?: () => void;
4142
after_test?: () => void;
4243
test?: (args: {
@@ -174,6 +175,8 @@ async function common_setup(cwd: string, runes: boolean | undefined, config: Run
174175
return compileOptions;
175176
}
176177

178+
let import_logs = new Map();
179+
177180
async function run_test_variant(
178181
cwd: string,
179182
config: RuntimeTest,
@@ -276,6 +279,13 @@ async function run_test_variant(
276279

277280
let mod = await import(`${cwd}/_output/client/main.svelte.js`);
278281

282+
if (config.needs_import_logs && !import_logs.has(`${cwd}/_output/client/main.svelte.js`)) {
283+
import_logs.set(`${cwd}/_output/client/main.svelte.js`, {
284+
logs: [...logs],
285+
warnings: [...warnings]
286+
});
287+
}
288+
279289
const target = window.document.querySelector('main') as HTMLElement;
280290

281291
let snapshot = undefined;
@@ -336,6 +346,13 @@ async function run_test_variant(
336346
}
337347
} else {
338348
logs.length = warnings.length = 0;
349+
if (config.needs_import_logs) {
350+
const { logs: import_logs_logs, warnings: import_logs_warnings } = import_logs.get(
351+
`${cwd}/_output/client/main.svelte.js`
352+
);
353+
logs.push(...import_logs_logs);
354+
warnings.push(...import_logs_warnings);
355+
}
339356

340357
config.before_test?.();
341358

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,18 @@
11
import { test } from '../../test';
22

33
export default test({
4-
mode: ['client', 'hydrate'],
4+
mode: ['hydrate', 'client'],
55
recover: true,
6-
runtime_error: 'invalid_html_structure'
6+
needs_import_logs: true,
7+
test({ warnings, assert, variant }) {
8+
const expected_warnings = [
9+
'This html structure `<p></p><tr></tr>` would be corrected like this `<p></p>` by the browser making this component impossible to hydrate properly'
10+
];
11+
if (variant === 'hydrate') {
12+
expected_warnings.push(
13+
'Hydration failed because the initial UI does not match what was rendered on the server'
14+
);
15+
}
16+
assert.deepEqual(warnings, expected_warnings);
17+
}
718
});

0 commit comments

Comments
 (0)
0