From 3b7066f5c611a9913ab62a5bf927443fc110d5f2 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Wed, 5 Feb 2025 20:22:58 +0100 Subject: [PATCH] fix: ensure custom element updates don't run in hydration mode (#15217) * fix: ensure custom element updates don't run in hydration mode When a custom element is created before Svelte hydration kicks in, it will scaffold itself, using the properties given via attributes. Now when a custom element property is set during Svelte's hydration, the Svelte custom element component could run logic like updating an each block. Without turning off hydration mode during that time, the update would try to pick up existing element nodes (because it thinks they must be there because of hydration mode), and crash. No test because it would require a setup where we can ensure the element is scaffolded before hydration runs. Fixes #15213 * changeset --------- Co-authored-by: Rich Harris --- .changeset/light-ligers-switch.md | 5 +++++ .../client/dom/elements/attributes.js | 22 ++++++++++++++++++- 2 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 .changeset/light-ligers-switch.md diff --git a/.changeset/light-ligers-switch.md b/.changeset/light-ligers-switch.md new file mode 100644 index 0000000000..51b3271047 --- /dev/null +++ b/.changeset/light-ligers-switch.md @@ -0,0 +1,5 @@ +--- +"svelte": patch +--- + +fix: ensure custom element updates don't run in hydration mode diff --git a/packages/svelte/src/internal/client/dom/elements/attributes.js b/packages/svelte/src/internal/client/dom/elements/attributes.js index 8c86fe7e02..308f23d340 100644 --- a/packages/svelte/src/internal/client/dom/elements/attributes.js +++ b/packages/svelte/src/internal/client/dom/elements/attributes.js @@ -1,5 +1,5 @@ import { DEV } from 'esm-env'; -import { hydrating } from '../hydration.js'; +import { hydrating, set_hydrating } from '../hydration.js'; import { get_descriptors, get_prototype_of } from '../../../shared/utils.js'; import { create_event, delegate } from './events.js'; import { add_form_reset_listener, autofocus } from './misc.js'; @@ -213,6 +213,12 @@ export function set_custom_element_data(node, prop, value) { // or effect var previous_reaction = active_reaction; var previous_effect = active_effect; + // If we're hydrating but the custom element is from Svelte, and it already scaffolded, + // then it might run block logic in hydration mode, which we have to prevent. + let was_hydrating = hydrating; + if (hydrating) { + set_hydrating(false); + } set_active_reaction(null); set_active_effect(null); @@ -239,6 +245,9 @@ export function set_custom_element_data(node, prop, value) { } finally { set_active_reaction(previous_reaction); set_active_effect(previous_effect); + if (was_hydrating) { + set_hydrating(true); + } } } @@ -262,6 +271,13 @@ export function set_attributes( is_custom_element = false, skip_warning = false ) { + // If we're hydrating but the custom element is from Svelte, and it already scaffolded, + // then it might run block logic in hydration mode, which we have to prevent. + let is_hydrating_custom_element = hydrating && is_custom_element; + if (is_hydrating_custom_element) { + set_hydrating(false); + } + var current = prev || {}; var is_option_element = element.tagName === 'OPTION'; @@ -416,6 +432,10 @@ export function set_attributes( } } + if (is_hydrating_custom_element) { + set_hydrating(true); + } + return current; }