diff --git a/.changeset/nervous-adults-sell.md b/.changeset/nervous-adults-sell.md new file mode 100644 index 0000000000..a7bad9bda8 --- /dev/null +++ b/.changeset/nervous-adults-sell.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: ensure `$store` reads are properly transformed diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/Program.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/Program.js index dc30255a1a..aa1a94824a 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/Program.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/Program.js @@ -40,6 +40,45 @@ export function Program(_, context) { } } + for (const [name, binding] of context.state.scope.declarations) { + if (binding.kind === 'prop' || binding.kind === 'bindable_prop') { + if (is_prop_source(binding, context.state)) { + context.state.transform[name] = { + read: b.call, + assign: b.call, + mutate: (node, value) => { + if (binding.kind === 'bindable_prop') { + // only necessary for interop with legacy parent bindings + return b.call(node, value, b.true); + } + + return value; + }, + update: (node) => { + return b.call( + node.prefix ? '$.update_pre_prop' : '$.update_prop', + node.argument, + node.operator === '--' && b.literal(-1) + ); + } + }; + } else if (binding.prop_alias) { + const key = b.key(binding.prop_alias); + + context.state.transform[name] = { + read: (_) => b.member(b.id('$$props'), key, key.type === 'Literal') + }; + } else { + context.state.transform[name] = { + read: (node) => b.member(b.id('$$props'), node) + }; + } + } + } + + add_state_transformers(context); + + // store subscriptions need to be last because getting the store value could depend on other transformers for (const [name, binding] of context.state.scope.declarations) { if (binding.kind === 'store_sub') { const store = /** @type {Expression} */ (context.visit(b.id(name.slice(1)))); @@ -91,43 +130,7 @@ export function Program(_, context) { } }; } - - if (binding.kind === 'prop' || binding.kind === 'bindable_prop') { - if (is_prop_source(binding, context.state)) { - context.state.transform[name] = { - read: b.call, - assign: b.call, - mutate: (node, value) => { - if (binding.kind === 'bindable_prop') { - // only necessary for interop with legacy parent bindings - return b.call(node, value, b.true); - } - - return value; - }, - update: (node) => { - return b.call( - node.prefix ? '$.update_pre_prop' : '$.update_prop', - node.argument, - node.operator === '--' && b.literal(-1) - ); - } - }; - } else if (binding.prop_alias) { - const key = b.key(binding.prop_alias); - - context.state.transform[name] = { - read: (_) => b.member(b.id('$$props'), key, key.type === 'Literal') - }; - } else { - context.state.transform[name] = { - read: (node) => b.member(b.id('$$props'), node) - }; - } - } } - add_state_transformers(context); - context.next(); } diff --git a/packages/svelte/tests/runtime-runes/samples/store-from-derived/_config.js b/packages/svelte/tests/runtime-runes/samples/store-from-derived/_config.js new file mode 100644 index 0000000000..8cb3fd9cae --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/store-from-derived/_config.js @@ -0,0 +1,11 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + html: ``, + test({ assert, target }) { + target.querySelector('button')?.click(); + flushSync(); + assert.htmlEqual(target.innerHTML, ``); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/store-from-derived/main.svelte b/packages/svelte/tests/runtime-runes/samples/store-from-derived/main.svelte new file mode 100644 index 0000000000..728544e664 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/store-from-derived/main.svelte @@ -0,0 +1,7 @@ + + +