From 90a70cb012733be22858dc4db9a18868159bd9fa Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Thu, 30 Apr 2026 00:18:09 +0200 Subject: [PATCH] fix: flush eager effects in production (#18107) While working on #18106 I noticed that we're not adding eager effects inside `mark_reactions` when `DEV` is `false`. As a result production build could have `$state.eager` or `$state.pending` not working correctly. ~No test because I can't get Vitest to not run with `DEV` being `true`.~ added a test --- .changeset/flat-shrimps-worry.md | 5 ++++ .../src/internal/client/reactivity/sources.js | 21 ++++++----------- .../samples/async-eager-derived/_config.js | 23 +++++++++++++++++++ .../samples/async-eager-derived/main.svelte | 22 ++++++++++++++++++ 4 files changed, 57 insertions(+), 14 deletions(-) create mode 100644 .changeset/flat-shrimps-worry.md create mode 100644 packages/svelte/tests/runtime-production/samples/async-eager-derived/_config.js create mode 100644 packages/svelte/tests/runtime-production/samples/async-eager-derived/main.svelte diff --git a/.changeset/flat-shrimps-worry.md b/.changeset/flat-shrimps-worry.md new file mode 100644 index 0000000000..a5f76a0f9d --- /dev/null +++ b/.changeset/flat-shrimps-worry.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: flush eager effects in production diff --git a/packages/svelte/src/internal/client/reactivity/sources.js b/packages/svelte/src/internal/client/reactivity/sources.js index 1831183e6f..a5dd94badf 100644 --- a/packages/svelte/src/internal/client/reactivity/sources.js +++ b/packages/svelte/src/internal/client/reactivity/sources.js @@ -47,7 +47,7 @@ import { proxy } from '../proxy.js'; import { execute_derived } from './deriveds.js'; import { set_signal_status, update_derived_status } from './status.js'; -/** @type {Set} */ +/** @type {Set} */ export let eager_effects = new Set(); /** @type {Map} */ @@ -266,12 +266,6 @@ export function flush_eager_effects() { eager_effects_deferred = false; for (const effect of eager_effects) { - // Mark clean inspect-effects as maybe dirty and then check their dirtiness - // instead of just updating the effects - this way we avoid overfiring. - if ((effect.f & CLEAN) !== 0) { - set_signal_status(effect, MAYBE_DIRTY); - } - if (is_dirty(effect)) { update_effect(effect); } @@ -338,12 +332,6 @@ function mark_reactions(signal, status, updated_during_traversal) { // In legacy mode, skip the current effect to prevent infinite loops if (!runes && reaction === active_effect) continue; - // Inspect effects need to run immediately, so that the stack trace makes sense - if (DEV && (flags & EAGER_EFFECT) !== 0) { - eager_effects.add(reaction); - continue; - } - var not_dirty = (flags & DIRTY) === 0; // don't set a DIRTY reaction to MAYBE_DIRTY @@ -351,7 +339,12 @@ function mark_reactions(signal, status, updated_during_traversal) { set_signal_status(reaction, status); } - if ((flags & DERIVED) !== 0) { + if ((flags & EAGER_EFFECT) !== 0) { + // Eager effects need to run immediately: + // - for $inspect so that the stack trace makes sense + // - for $state.eager because they might be without an effect parent + eager_effects.add(/** @type {Effect} */ (reaction)); + } else if ((flags & DERIVED) !== 0) { var derived = /** @type {Derived} */ (reaction); batch_values?.delete(derived); diff --git a/packages/svelte/tests/runtime-production/samples/async-eager-derived/_config.js b/packages/svelte/tests/runtime-production/samples/async-eager-derived/_config.js new file mode 100644 index 0000000000..043f1610fb --- /dev/null +++ b/packages/svelte/tests/runtime-production/samples/async-eager-derived/_config.js @@ -0,0 +1,23 @@ +import { tick } from 'svelte'; +import { test } from '../../test'; + +export default test({ + async test({ assert, target }) { + await tick(); + const [increment, shift] = target.querySelectorAll('button'); + + increment.click(); + await tick(); + assert.htmlEqual( + target.innerHTML, + `

true - true

` + ); + + shift.click(); + await tick(); + assert.htmlEqual( + target.innerHTML, + `

false - false

` + ); + } +}); diff --git a/packages/svelte/tests/runtime-production/samples/async-eager-derived/main.svelte b/packages/svelte/tests/runtime-production/samples/async-eager-derived/main.svelte new file mode 100644 index 0000000000..d1d979126d --- /dev/null +++ b/packages/svelte/tests/runtime-production/samples/async-eager-derived/main.svelte @@ -0,0 +1,22 @@ + + + + + +

{$state.eager(count) !== count} - {$state.eager(derivedCount) !== derivedCount}