From 69f0bb549f026285af28f7a82ea988a34dabab26 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 13 Oct 2026 12:07:23 -0400 Subject: [PATCH] restore resetting effects to CLEAN in is_dirty while time travelling Effects left MAYBE_DIRTY are forced to re-run when a paused branch is resumed. --- .../svelte/src/internal/client/runtime.js | 6 ++- .../async-effect-resume-no-rerun/Child.svelte | 9 +++++ .../async-effect-resume-no-rerun/_config.js | 37 +++++++++++++++++++ .../async-effect-resume-no-rerun/main.svelte | 29 +++++++++++++++ 4 files changed, 79 insertions(+), 2 deletions(-) create mode 100644 packages/svelte/tests/runtime-runes/samples/async-effect-resume-no-rerun/Child.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/async-effect-resume-no-rerun/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/async-effect-resume-no-rerun/main.svelte diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index 62d26c0391..07f8773915 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -185,8 +185,10 @@ export function is_dirty(reaction) { if ( (flags & CONNECTED) !== 0 && // During time traveling we don't want to reset the status so that - // traversal of the graph in the other batches still happens - batch_values === null + // traversal of the graph in the other batches still happens. Effects + // can be reset because block/async effects execute right away and others + // are deferred and re-dirtied as needed. + (batch_values === null || (flags & DERIVED) === 0) ) { set_signal_status(reaction, CLEAN); } diff --git a/packages/svelte/tests/runtime-runes/samples/async-effect-resume-no-rerun/Child.svelte b/packages/svelte/tests/runtime-runes/samples/async-effect-resume-no-rerun/Child.svelte new file mode 100644 index 0000000000..6f48eaf129 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/async-effect-resume-no-rerun/Child.svelte @@ -0,0 +1,9 @@ + + +

child

diff --git a/packages/svelte/tests/runtime-runes/samples/async-effect-resume-no-rerun/_config.js b/packages/svelte/tests/runtime-runes/samples/async-effect-resume-no-rerun/_config.js new file mode 100644 index 0000000000..4fd60a98ce --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/async-effect-resume-no-rerun/_config.js @@ -0,0 +1,37 @@ +import { tick } from 'svelte'; +import { test } from '../../test'; + +export default test({ + mode: ['client'], + async test({ assert, target, logs, raf }) { + await tick(); + const [x, s, toggle, shift] = target.querySelectorAll('button'); + assert.deepEqual(logs, ['effect false']); + + // keep an earlier batch pending, so that the next one commits while time travelling + x.click(); + await tick(); + + // `big` stays `false`, so the effect must not re-run + s.click(); + await tick(); + assert.deepEqual(logs, ['effect false']); + + shift.click(); + await tick(); + assert.htmlEqual( + target.innerHTML, + '

1

child

' + ); + + // outro, then reintro before the outro finished: the branch is resumed + toggle.click(); + await tick(); + raf.tick(50); + toggle.click(); + await tick(); + raf.tick(200); + + assert.deepEqual(logs, ['effect false']); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/async-effect-resume-no-rerun/main.svelte b/packages/svelte/tests/runtime-runes/samples/async-effect-resume-no-rerun/main.svelte new file mode 100644 index 0000000000..30d68f614b --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/async-effect-resume-no-rerun/main.svelte @@ -0,0 +1,29 @@ + + + + + + + +

{await push(x)}

+ +{#if show} +
+ +
+{/if}