diff --git a/.changeset/angry-weeks-design.md b/.changeset/angry-weeks-design.md new file mode 100644 index 000000000000..897f2e2192e0 --- /dev/null +++ b/.changeset/angry-weeks-design.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: ensure reactive graph is fully traversed in the marking phase for non-runes mode diff --git a/packages/svelte/src/internal/client/reactivity/effects.js b/packages/svelte/src/internal/client/reactivity/effects.js index e0d97beaf244..585800d148de 100644 --- a/packages/svelte/src/internal/client/reactivity/effects.js +++ b/packages/svelte/src/internal/client/reactivity/effects.js @@ -33,7 +33,8 @@ import { UNOWNED, CLEAN, INSPECT_EFFECT, - HEAD_EFFECT + HEAD_EFFECT, + MAYBE_DIRTY } from '../constants.js'; import { set } from './sources.js'; import * as e from '../errors.js'; @@ -281,6 +282,12 @@ export function legacy_pre_effect_reset() { for (var token of context.l.r1) { var effect = token.effect; + // If the effect is CLEAN, then make it MAYBE_DIRTY. This ensures we traverse through + // the effects dependencies and correctly ensure each dependency is up-to-date. + if ((effect.f & CLEAN) !== 0) { + set_signal_status(effect, MAYBE_DIRTY); + } + if (check_dirtiness(effect)) { update_effect(effect); } diff --git a/packages/svelte/tests/runtime-legacy/samples/store-updated-in-reactive-statement/Child.svelte b/packages/svelte/tests/runtime-legacy/samples/store-updated-in-reactive-statement/Child.svelte new file mode 100644 index 000000000000..58ec0f651639 --- /dev/null +++ b/packages/svelte/tests/runtime-legacy/samples/store-updated-in-reactive-statement/Child.svelte @@ -0,0 +1,15 @@ + + +

{$copy}

diff --git a/packages/svelte/tests/runtime-legacy/samples/store-updated-in-reactive-statement/_config.js b/packages/svelte/tests/runtime-legacy/samples/store-updated-in-reactive-statement/_config.js new file mode 100644 index 000000000000..7db95248aaa6 --- /dev/null +++ b/packages/svelte/tests/runtime-legacy/samples/store-updated-in-reactive-statement/_config.js @@ -0,0 +1,18 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; +import { store } from './state.js'; + +export default test({ + html: '

0

', + + before_test() { + store.set({ value: 0 }); + }, + + async test({ assert, target }) { + const button = target.querySelector('button'); + flushSync(() => button?.click()); + + assert.htmlEqual(target.innerHTML, '

1

'); + } +}); diff --git a/packages/svelte/tests/runtime-legacy/samples/store-updated-in-reactive-statement/main.svelte b/packages/svelte/tests/runtime-legacy/samples/store-updated-in-reactive-statement/main.svelte new file mode 100644 index 000000000000..1d7a9d6e8f60 --- /dev/null +++ b/packages/svelte/tests/runtime-legacy/samples/store-updated-in-reactive-statement/main.svelte @@ -0,0 +1,8 @@ + + + + + diff --git a/packages/svelte/tests/runtime-legacy/samples/store-updated-in-reactive-statement/state.js b/packages/svelte/tests/runtime-legacy/samples/store-updated-in-reactive-statement/state.js new file mode 100644 index 000000000000..030378b5e7d0 --- /dev/null +++ b/packages/svelte/tests/runtime-legacy/samples/store-updated-in-reactive-statement/state.js @@ -0,0 +1,3 @@ +import { writable } from 'svelte/store'; + +export const store = writable({ value: 0 });