From 4d611b8e40d41711eff78cee1bc7326752406318 Mon Sep 17 00:00:00 2001 From: raythurnvoid <53383860+raythurnvoid@users.noreply.github.com> Date: Sun, 8 Jun 2025 23:58:02 +0100 Subject: [PATCH 1/3] Fix top-most derived in a chain of deriveds marked as MAYBE_DIRTY when executed from a snippet $.fallback --- .../svelte/src/internal/client/reactivity/deriveds.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/svelte/src/internal/client/reactivity/deriveds.js b/packages/svelte/src/internal/client/reactivity/deriveds.js index e9cea0df3e64..33026e4039ca 100644 --- a/packages/svelte/src/internal/client/reactivity/deriveds.js +++ b/packages/svelte/src/internal/client/reactivity/deriveds.js @@ -183,8 +183,14 @@ export function update_derived(derived) { // cleanup function, or it will cache a stale value if (is_destroying_effect) return; - var status = - (skip_reaction || (derived.f & UNOWNED) !== 0) && derived.deps !== null ? MAYBE_DIRTY : CLEAN; + // only mark unowned deriveds as MAYBE_DIRTY if they have dependencies, otherwise they + // must be clean regardless of the value of the skip_reaction flag value set for the previous_reaction + // because not marking a regular derived as CLEAN will cause incosistent state when chaining + // multiple derivides in which the top-most derived is marked MAYBE_DIRTY and all the ones that depends + // on it are instead marked as CLEAN causing issues with properly updating the UI when the source state + // is updated because the MAYBE_DIRTY derived is skipped and as a consequence also + // the other deriveds (aka its reactions) are skipped as well. + var status = (derived.f & UNOWNED) !== 0 && derived.deps !== null ? MAYBE_DIRTY : CLEAN; set_signal_status(derived, status); } From f80082c58a73ba6f9ea52449355b7773ebc8e4fd Mon Sep 17 00:00:00 2001 From: raythurnvoid <53383860+raythurnvoid@users.noreply.github.com> Date: Mon, 9 Jun 2025 00:10:09 +0100 Subject: [PATCH 2/3] Add test --- .../_config.js | 58 +++++++++++++++++++ .../main.svelte | 33 +++++++++++ 2 files changed, 91 insertions(+) create mode 100644 packages/svelte/tests/runtime-runes/samples/devides-chain-with-snippet-fallback-trigger/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/devides-chain-with-snippet-fallback-trigger/main.svelte diff --git a/packages/svelte/tests/runtime-runes/samples/devides-chain-with-snippet-fallback-trigger/_config.js b/packages/svelte/tests/runtime-runes/samples/devides-chain-with-snippet-fallback-trigger/_config.js new file mode 100644 index 000000000000..20df2425f6f6 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/devides-chain-with-snippet-fallback-trigger/_config.js @@ -0,0 +1,58 @@ +import { test } from '../../test'; +import { flushSync, tick } from 'svelte'; + +export default test({ + async test({ assert, target }) { + assert.htmlEqual( + target.innerHTML, + ` + + + +

0

+ ` + ); + + const step1 = /** @type {HTMLButtonElement | null} */ (target.querySelector('#step1')); + const step2 = /** @type {HTMLButtonElement | null} */ (target.querySelector('#step2')); + const step3 = /** @type {HTMLButtonElement | null} */ (target.querySelector('#step3')); + + // Step 1: hide and reset data + step1?.click(); + await tick(); + assert.htmlEqual( + target.innerHTML, + ` + + + + ` + ); + + // Step 2: show again + step2?.click(); + await tick(); + assert.htmlEqual( + target.innerHTML, + ` + + + +

0

+ ` + ); + + // Step 3: update override - this should show 2, not 0 (the bug) + step3?.click(); + await tick(); + assert.htmlEqual( + target.innerHTML, + ` + + + +

2

+ ` + ); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/devides-chain-with-snippet-fallback-trigger/main.svelte b/packages/svelte/tests/runtime-runes/samples/devides-chain-with-snippet-fallback-trigger/main.svelte new file mode 100644 index 000000000000..3269ad28a050 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/devides-chain-with-snippet-fallback-trigger/main.svelte @@ -0,0 +1,33 @@ + + + + + + +{#snippet dummy(value = 0)}{/snippet} + +{#if show} +

{derived2}

+ {@render dummy(derived2 ? 0 : 0)} +{/if} From 77e211632a00421794d160864e8396b3441caa56 Mon Sep 17 00:00:00 2001 From: raythurnvoid <53383860+raythurnvoid@users.noreply.github.com> Date: Mon, 9 Jun 2025 00:54:08 +0100 Subject: [PATCH 3/3] Add changeset --- .changeset/fresh-walls-wave.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/fresh-walls-wave.md diff --git a/.changeset/fresh-walls-wave.md b/.changeset/fresh-walls-wave.md new file mode 100644 index 000000000000..2e9c949c7c16 --- /dev/null +++ b/.changeset/fresh-walls-wave.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +Fix top-most derived in a chain of deriveds marked as MAYBE_DIRTY when executed from a snippet $.fallback