From 4dfad737861c66aedf27fd3a8d3b74935dcb6e63 Mon Sep 17 00:00:00 2001 From: 7nik Date: Thu, 26 Jun 2025 19:51:57 +0300 Subject: [PATCH] fix: remount at any hydration error --- .changeset/hip-eagles-yawn.md | 5 ++++ packages/svelte/src/internal/client/render.js | 23 ++++++++++--------- .../whitespace-at-block-start/Nested.svelte | 1 + .../whitespace-at-block-start/_config.js | 21 +++++++++++++++++ .../whitespace-at-block-start/_expected.html | 1 + .../whitespace-at-block-start/_override.html | 2 ++ .../whitespace-at-block-start/main.svelte | 7 ++++++ .../samples/keyed-each-dev-unique/_config.js | 1 + .../inspect-state-unsafe-mutation/_config.js | 1 + .../samples/props-bound-fallback/_config.js | 1 + 10 files changed, 52 insertions(+), 11 deletions(-) create mode 100644 .changeset/hip-eagles-yawn.md create mode 100644 packages/svelte/tests/hydration/samples/whitespace-at-block-start/Nested.svelte create mode 100644 packages/svelte/tests/hydration/samples/whitespace-at-block-start/_config.js create mode 100644 packages/svelte/tests/hydration/samples/whitespace-at-block-start/_expected.html create mode 100644 packages/svelte/tests/hydration/samples/whitespace-at-block-start/_override.html create mode 100644 packages/svelte/tests/hydration/samples/whitespace-at-block-start/main.svelte diff --git a/.changeset/hip-eagles-yawn.md b/.changeset/hip-eagles-yawn.md new file mode 100644 index 0000000000..c987e7c7f2 --- /dev/null +++ b/.changeset/hip-eagles-yawn.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: remount at any hydration error diff --git a/packages/svelte/src/internal/client/render.js b/packages/svelte/src/internal/client/render.js index ff6844453d..dfb40f1796 100644 --- a/packages/svelte/src/internal/client/render.js +++ b/packages/svelte/src/internal/client/render.js @@ -136,20 +136,21 @@ export function hydrate(component, options) { return /** @type {Exports} */ (instance); } catch (error) { - if (error === HYDRATION_ERROR) { - if (options.recover === false) { - e.hydration_failed(); - } - - // If an error occured above, the operations might not yet have been initialised. - init_operations(); - clear_text_content(target); + if (error !== HYDRATION_ERROR) { + // eslint-disable-next-line no-console + console.error('Failed to hydrate: ', error); + } - set_hydrating(false); - return mount(component, options); + if (options.recover === false) { + e.hydration_failed(); } - throw error; + // If an error occured above, the operations might not yet have been initialised. + init_operations(); + clear_text_content(target); + + set_hydrating(false); + return mount(component, options); } finally { set_hydrating(was_hydrating); set_hydrate_node(previous_hydrate_node); diff --git a/packages/svelte/tests/hydration/samples/whitespace-at-block-start/Nested.svelte b/packages/svelte/tests/hydration/samples/whitespace-at-block-start/Nested.svelte new file mode 100644 index 0000000000..70bf63ad9d --- /dev/null +++ b/packages/svelte/tests/hydration/samples/whitespace-at-block-start/Nested.svelte @@ -0,0 +1 @@ +

nested

\ No newline at end of file diff --git a/packages/svelte/tests/hydration/samples/whitespace-at-block-start/_config.js b/packages/svelte/tests/hydration/samples/whitespace-at-block-start/_config.js new file mode 100644 index 0000000000..e01861a823 --- /dev/null +++ b/packages/svelte/tests/hydration/samples/whitespace-at-block-start/_config.js @@ -0,0 +1,21 @@ +import { test } from '../../test'; + +/** @type {string[]} */ +let logs = []; +/** @type {typeof console['error']} */ +let console_error; + +export default test({ + before_test() { + console_error = console.error; + console.error = (...args) => logs.push(args.join('')); + }, + after_test() { + console.error = console_error; + }, + test({ deepEqual }) { + deepEqual(logs, [ + "Failed to hydrate: HierarchyRequestError: Node can't be inserted in a #text parent." + ]); + } +}); diff --git a/packages/svelte/tests/hydration/samples/whitespace-at-block-start/_expected.html b/packages/svelte/tests/hydration/samples/whitespace-at-block-start/_expected.html new file mode 100644 index 0000000000..46f8e8a7ac --- /dev/null +++ b/packages/svelte/tests/hydration/samples/whitespace-at-block-start/_expected.html @@ -0,0 +1 @@ +

nested

\ No newline at end of file diff --git a/packages/svelte/tests/hydration/samples/whitespace-at-block-start/_override.html b/packages/svelte/tests/hydration/samples/whitespace-at-block-start/_override.html new file mode 100644 index 0000000000..90ca4ef4b8 --- /dev/null +++ b/packages/svelte/tests/hydration/samples/whitespace-at-block-start/_override.html @@ -0,0 +1,2 @@ + +

nested

\ No newline at end of file diff --git a/packages/svelte/tests/hydration/samples/whitespace-at-block-start/main.svelte b/packages/svelte/tests/hydration/samples/whitespace-at-block-start/main.svelte new file mode 100644 index 0000000000..f1f962b958 --- /dev/null +++ b/packages/svelte/tests/hydration/samples/whitespace-at-block-start/main.svelte @@ -0,0 +1,7 @@ + + +
+ +
\ No newline at end of file diff --git a/packages/svelte/tests/runtime-legacy/samples/keyed-each-dev-unique/_config.js b/packages/svelte/tests/runtime-legacy/samples/keyed-each-dev-unique/_config.js index 6e64245d86..97ccaebac2 100644 --- a/packages/svelte/tests/runtime-legacy/samples/keyed-each-dev-unique/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/keyed-each-dev-unique/_config.js @@ -1,6 +1,7 @@ import { test } from '../../test'; export default test({ + mode: ['server', 'client'], compileOptions: { dev: true }, diff --git a/packages/svelte/tests/runtime-runes/samples/inspect-state-unsafe-mutation/_config.js b/packages/svelte/tests/runtime-runes/samples/inspect-state-unsafe-mutation/_config.js index dcf3a8bc3d..27b9cc7fec 100644 --- a/packages/svelte/tests/runtime-runes/samples/inspect-state-unsafe-mutation/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/inspect-state-unsafe-mutation/_config.js @@ -2,6 +2,7 @@ import { flushSync } from 'svelte'; import { test } from '../../test'; export default test({ + mode: ['client'], compileOptions: { dev: true }, diff --git a/packages/svelte/tests/runtime-runes/samples/props-bound-fallback/_config.js b/packages/svelte/tests/runtime-runes/samples/props-bound-fallback/_config.js index d04d8884ca..26cb426127 100644 --- a/packages/svelte/tests/runtime-runes/samples/props-bound-fallback/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/props-bound-fallback/_config.js @@ -5,6 +5,7 @@ import { test } from '../../test'; // uses a prop it does not write to but has a fallback value export default test({ accessors: false, // so that prop actually becomes $.prop and not $.prop_source + mode: ['server', 'client'], html: `0`, test({ assert, target }) {