From 4f95abff87e0c424f9eb7d29254e2cb86a2e20cf Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 4 Mar 2025 21:07:50 -0500 Subject: [PATCH 1/5] process effect roots in tree order --- .../src/internal/client/reactivity/effects.js | 13 +++++++++---- packages/svelte/src/internal/client/runtime.js | 14 +++++--------- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/packages/svelte/src/internal/client/reactivity/effects.js b/packages/svelte/src/internal/client/reactivity/effects.js index 28589ce94df1..93ef9741a12b 100644 --- a/packages/svelte/src/internal/client/reactivity/effects.js +++ b/packages/svelte/src/internal/client/reactivity/effects.js @@ -82,7 +82,6 @@ function push_effect(effect, parent_effect) { * @returns {Effect} */ function create_effect(type, fn, sync, push = true) { - var is_root = (type & ROOT_EFFECT) !== 0; var parent_effect = active_effect; if (DEV) { @@ -103,7 +102,7 @@ function create_effect(type, fn, sync, push = true) { fn, last: null, next: null, - parent: is_root ? null : parent_effect, + parent: parent_effect, prev: null, teardown: null, transitions: null, @@ -136,7 +135,7 @@ function create_effect(type, fn, sync, push = true) { effect.teardown === null && (effect.f & (EFFECT_HAS_DERIVED | BOUNDARY_EFFECT)) === 0; - if (!inert && !is_root && push) { + if (!inert && push) { if (parent_effect !== null) { push_effect(effect, parent_effect); } @@ -391,7 +390,13 @@ export function destroy_effect_children(signal, remove_dom = false) { while (effect !== null) { var next = effect.next; - destroy_effect(effect, remove_dom); + + if ((effect.f & ROOT_EFFECT) !== 0) { + effect.parent = null; + } else { + destroy_effect(effect, remove_dom); + } + effect = next; } } diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index 9f721f9ec4d6..bbe4dc3d9b8f 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -661,13 +661,7 @@ function flush_queued_root_effects() { queued_root_effects = []; for (var i = 0; i < length; i++) { - var root = root_effects[i]; - - if ((root.f & CLEAN) === 0) { - root.f ^= CLEAN; - } - - var collected_effects = process_effects(root); + var collected_effects = process_effects(root_effects[i]); flush_queued_effects(collected_effects); } } @@ -759,11 +753,12 @@ function process_effects(root) { /** @type {Effect[]} */ var effects = []; - var effect = root.first; + /** @type {Effect | null} */ + var effect = root; while (effect !== null) { var flags = effect.f; - var is_branch = (flags & BRANCH_EFFECT) !== 0; + var is_branch = (flags & (BRANCH_EFFECT | ROOT_EFFECT)) !== 0; var is_skippable_branch = is_branch && (flags & CLEAN) !== 0; if (!is_skippable_branch && (flags & INERT) === 0) { @@ -788,6 +783,7 @@ function process_effects(root) { } } + /** @type {Effect | null} */ var child = effect.first; if (child !== null) { From 635319f2224361b4b6da469706350001f2091044 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 4 Mar 2025 21:09:46 -0500 Subject: [PATCH 2/5] bring test over --- .../src/internal/client/reactivity/effects.js | 1 + .../samples/toStore-teardown/_config.js | 13 +++++++++++++ .../samples/toStore-teardown/child.svelte | 11 +++++++++++ .../samples/toStore-teardown/main.svelte | 15 +++++++++++++++ 4 files changed, 40 insertions(+) create mode 100644 packages/svelte/tests/runtime-runes/samples/toStore-teardown/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/toStore-teardown/child.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/toStore-teardown/main.svelte diff --git a/packages/svelte/src/internal/client/reactivity/effects.js b/packages/svelte/src/internal/client/reactivity/effects.js index 93ef9741a12b..95e10a6a70d2 100644 --- a/packages/svelte/src/internal/client/reactivity/effects.js +++ b/packages/svelte/src/internal/client/reactivity/effects.js @@ -392,6 +392,7 @@ export function destroy_effect_children(signal, remove_dom = false) { var next = effect.next; if ((effect.f & ROOT_EFFECT) !== 0) { + // this is now an independent root effect.parent = null; } else { destroy_effect(effect, remove_dom); diff --git a/packages/svelte/tests/runtime-runes/samples/toStore-teardown/_config.js b/packages/svelte/tests/runtime-runes/samples/toStore-teardown/_config.js new file mode 100644 index 000000000000..95904f011fc0 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/toStore-teardown/_config.js @@ -0,0 +1,13 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + async test({ assert, target }) { + let [, btn2] = target.querySelectorAll('button'); + + btn2.click(); + flushSync(); + + assert.htmlEqual(target.innerHTML, ``); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/toStore-teardown/child.svelte b/packages/svelte/tests/runtime-runes/samples/toStore-teardown/child.svelte new file mode 100644 index 000000000000..f1b1b7b49769 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/toStore-teardown/child.svelte @@ -0,0 +1,11 @@ + + +

+ Current value: + {$currentValue} +

diff --git a/packages/svelte/tests/runtime-runes/samples/toStore-teardown/main.svelte b/packages/svelte/tests/runtime-runes/samples/toStore-teardown/main.svelte new file mode 100644 index 000000000000..7d36dd95cbfd --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/toStore-teardown/main.svelte @@ -0,0 +1,15 @@ + + + + + +{#if data} + +{/if} From a8108d450417d3b3c4bac067edb92688a58bbf94 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 4 Mar 2025 21:12:31 -0500 Subject: [PATCH 3/5] add test --- .../samples/effect-root-5/_config.js | 17 ++++++++++++++ .../samples/effect-root-5/main.svelte | 23 +++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 packages/svelte/tests/runtime-runes/samples/effect-root-5/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/effect-root-5/main.svelte diff --git a/packages/svelte/tests/runtime-runes/samples/effect-root-5/_config.js b/packages/svelte/tests/runtime-runes/samples/effect-root-5/_config.js new file mode 100644 index 000000000000..260c757e3d8e --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/effect-root-5/_config.js @@ -0,0 +1,17 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + async test({ assert, target, logs }) { + const [b1, b2] = target.querySelectorAll('button'); + + flushSync(() => b1.click()); + assert.deepEqual(logs, [0, 1]); + + flushSync(() => b1.click()); + assert.deepEqual(logs, [0, 1, 2]); + + flushSync(() => b2.click()); + assert.deepEqual(logs, [0, 1, 2]); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/effect-root-5/main.svelte b/packages/svelte/tests/runtime-runes/samples/effect-root-5/main.svelte new file mode 100644 index 000000000000..06655a53623c --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/effect-root-5/main.svelte @@ -0,0 +1,23 @@ + + + + From a74ee233188522ee0dd011768df32e24ba4439cf Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 4 Mar 2025 21:12:52 -0500 Subject: [PATCH 4/5] changeset --- .changeset/shy-falcons-occur.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/shy-falcons-occur.md diff --git a/.changeset/shy-falcons-occur.md b/.changeset/shy-falcons-occur.md new file mode 100644 index 000000000000..b16e28549eb4 --- /dev/null +++ b/.changeset/shy-falcons-occur.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: run effect roots in tree order From dd0ec003512181d45946657d35f6501f2b3113f7 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 4 Mar 2025 21:17:29 -0500 Subject: [PATCH 5/5] tidy --- .../svelte/src/internal/client/reactivity/effects.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/svelte/src/internal/client/reactivity/effects.js b/packages/svelte/src/internal/client/reactivity/effects.js index 95e10a6a70d2..468bb94ab428 100644 --- a/packages/svelte/src/internal/client/reactivity/effects.js +++ b/packages/svelte/src/internal/client/reactivity/effects.js @@ -82,12 +82,12 @@ function push_effect(effect, parent_effect) { * @returns {Effect} */ function create_effect(type, fn, sync, push = true) { - var parent_effect = active_effect; + var parent = active_effect; if (DEV) { // Ensure the parent is never an inspect effect - while (parent_effect !== null && (parent_effect.f & INSPECT_EFFECT) !== 0) { - parent_effect = parent_effect.parent; + while (parent !== null && (parent.f & INSPECT_EFFECT) !== 0) { + parent = parent.parent; } } @@ -102,7 +102,7 @@ function create_effect(type, fn, sync, push = true) { fn, last: null, next: null, - parent: parent_effect, + parent, prev: null, teardown: null, transitions: null, @@ -136,8 +136,8 @@ function create_effect(type, fn, sync, push = true) { (effect.f & (EFFECT_HAS_DERIVED | BOUNDARY_EFFECT)) === 0; if (!inert && push) { - if (parent_effect !== null) { - push_effect(effect, parent_effect); + if (parent !== null) { + push_effect(effect, parent); } // if we're in a derived, add the effect there too