From 1032b313aba1b79e0a942289dceb4eafc62f9096 Mon Sep 17 00:00:00 2001 From: Simon Holthausen Date: Tue, 4 Nov 2025 16:53:10 +0100 Subject: [PATCH] fix WAS_MARKED logic --- .../src/internal/client/reactivity/sources.js | 5 +++- .../svelte/src/internal/client/runtime.js | 29 +++++++++++++++---- 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/packages/svelte/src/internal/client/reactivity/sources.js b/packages/svelte/src/internal/client/reactivity/sources.js index f3568325cb..0b5ad33bfc 100644 --- a/packages/svelte/src/internal/client/reactivity/sources.js +++ b/packages/svelte/src/internal/client/reactivity/sources.js @@ -335,7 +335,10 @@ function mark_reactions(signal, status) { if ((flags & DERIVED) !== 0) { if ((flags & WAS_MARKED) === 0) { - reaction.f |= WAS_MARKED; + // Only connected deriveds can be reliably unmarked right away + if (flags & CONNECTED) { + reaction.f |= WAS_MARKED; + } mark_reactions(/** @type {Derived} */ (reaction), MAYBE_DIRTY); } } else if (not_dirty) { diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index ed15213545..3a31c175f1 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -154,13 +154,13 @@ export function is_dirty(reaction) { return true; } + if (flags & DERIVED) { + reaction.f &= ~WAS_MARKED; + } + if ((flags & MAYBE_DIRTY) !== 0) { var dependencies = reaction.deps; - if (flags & DERIVED) { - reaction.f &= ~WAS_MARKED; - } - if (dependencies !== null) { var i; var dependency; @@ -365,9 +365,10 @@ function remove_reaction(signal, dependency) { ) { set_signal_status(dependency, MAYBE_DIRTY); // If we are working with a derived that is owned by an effect, then mark it as being - // disconnected. + // disconnected and remove the mark flag, as it cannot be reliably removed otherwise if ((dependency.f & CONNECTED) !== 0) { dependency.f ^= CONNECTED; + dependency.f &= ~WAS_MARKED; } // Disconnect any reactions owned by this reaction destroy_derived_effects(/** @type {Derived} **/ (dependency)); @@ -613,6 +614,10 @@ export function get(signal) { var should_reconnect = is_updating_effect && effect_tracking() && (derived.f & CONNECTED) === 0; if (batch_values?.has(derived)) { + // This happens as part of is_dirty normally, but we return early + // here so we need to do it separately + remove_marked_flag(derived); + if (should_reconnect) { reconnect(derived); } @@ -657,6 +662,20 @@ function reconnect(derived) { } } +/** + * Removes the WAS_MARKED flag from the derived and its dependencies + * @param {Derived} derived + */ +function remove_marked_flag(derived) { + if ((derived.f & WAS_MARKED) === 0) return; + derived.f ^= WAS_MARKED; + + // Only deriveds with dependencies can be marked + for (const dep of /** @type {Value[]} */ (derived.deps)) { + remove_marked_flag(/** @type {Derived} */ (dep)); + } +} + /** @param {Derived} derived */ function depends_on_old_values(derived) { if (derived.v === UNINITIALIZED) return true; // we don't know, so assume the worst