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 @@
+
+
+