fix: ensure `$store` reads are properly transformed (#12952)

The backing store variable of a $store read could be a variable that itself needs a transformer, which is why we need to make accessing the reads lazy

fixes #12859
pull/12957/head
Rich Harris 3 months ago committed by GitHub
parent 732dbf7fa9
commit af7f90020d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: ensure `$store` reads are properly transformed

@ -42,11 +42,17 @@ export function Program(_, context) {
for (const [name, binding] of context.state.scope.declarations) { for (const [name, binding] of context.state.scope.declarations) {
if (binding.kind === 'store_sub') { if (binding.kind === 'store_sub') {
const store = /** @type {Expression} */ (context.visit(b.id(name.slice(1)))); // read lazily, so that transforms added later are still applied
/** @type {Expression} */
let cached;
const get_store = () => {
return (cached ??= /** @type {Expression} */ (context.visit(b.id(name.slice(1)))));
};
context.state.transform[name] = { context.state.transform[name] = {
read: b.call, read: b.call,
assign: (_, value) => b.call('$.store_set', store, value), assign: (_, value) => b.call('$.store_set', get_store(), value),
mutate: (node, mutation) => { mutate: (node, mutation) => {
// We need to untrack the store read, for consistency with Svelte 4 // We need to untrack the store read, for consistency with Svelte 4
const untracked = b.call('$.untrack', node); const untracked = b.call('$.untrack', node);
@ -70,7 +76,7 @@ export function Program(_, context) {
return b.call( return b.call(
'$.store_mutate', '$.store_mutate',
store, get_store(),
b.assignment( b.assignment(
mutation.operator, mutation.operator,
/** @type {MemberExpression} */ ( /** @type {MemberExpression} */ (

@ -0,0 +1,11 @@
import { flushSync } from 'svelte';
import { test } from '../../test';
export default test({
html: `<button>false</button>`,
test({ assert, target }) {
target.querySelector('button')?.click();
flushSync();
assert.htmlEqual(target.innerHTML, `<button>true</button>`);
}
});

@ -0,0 +1,7 @@
<script>
import { writable } from 'svelte/store';
let data = { store: writable(false) };
let { store } = $derived(data);
</script>
<button onclick={() => ($store = true)}>{$store}</button>
Loading…
Cancel
Save