Skip to content

Commit 239c50b

Browse files
committed
fix: make $state component exports settable
fixes #11983
1 parent 67bf7a8 commit 239c50b

File tree

4 files changed

+52
-9
lines changed

4 files changed

+52
-9
lines changed

.changeset/tidy-lizards-happen.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'svelte': patch
3+
---
4+
5+
fix: make `$state` component exports settable

packages/svelte/src/compiler/phases/3-transform/client/transform-client.js

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -198,14 +198,38 @@ export function client_component(source, analysis, options) {
198198
}
199199

200200
/** @type {Array<ESTree.Property | ESTree.SpreadElement>} */
201-
const component_returned_object = analysis.exports.map(({ name, alias }) => {
201+
const component_returned_object = analysis.exports.flatMap(({ name, alias }) => {
202+
const binding = instance_state.scope.get(name);
202203
const expression = serialize_get_binding(b.id(name), instance_state);
204+
const getter = b.get(alias ?? name, [b.return(expression)]);
205+
206+
if (expression.type === 'Identifier') {
207+
if (binding?.declaration_kind === 'let' || binding?.declaration_kind === 'var') {
208+
return [
209+
getter,
210+
b.set(alias ?? name, [b.stmt(b.assignment('=', expression, b.id('$$value')))])
211+
];
212+
} else if (!options.dev) {
213+
return b.init(alias ?? name, expression);
214+
}
215+
}
203216

204-
if (expression.type === 'Identifier' && !options.dev) {
205-
return b.init(alias ?? name, expression);
217+
if (binding?.kind === 'state' || binding?.kind === 'frozen_state') {
218+
return [
219+
getter,
220+
b.set(alias ?? name, [
221+
b.stmt(
222+
b.call(
223+
'$.set',
224+
b.id(name),
225+
b.call(binding.kind === 'state' ? '$.proxy' : '$.freeze', b.id('$$value'))
226+
)
227+
)
228+
])
229+
];
206230
}
207231

208-
return b.get(alias ?? name, [b.return(expression)]);
232+
return getter;
209233
});
210234

211235
const properties = [...analysis.instance.scope.declarations].filter(

packages/svelte/tests/runtime-runes/samples/exports-3/_config.js

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,24 @@ import { test } from '../../test';
33

44
export default test({
55
test({ assert, target }) {
6-
assert.htmlEqual(target.innerHTML, `0 0 <button>0 / 0</button>`);
7-
const [btn] = target.querySelectorAll('button');
6+
assert.htmlEqual(
7+
target.innerHTML,
8+
`0 0 <button>0 / 0</button> <button>assign directly</button>`
9+
);
10+
const [btn1, btn2] = target.querySelectorAll('button');
811

9-
btn?.click();
12+
btn1.click();
1013
flushSync();
11-
assert.htmlEqual(target.innerHTML, '1 2 <button>1 / 2</button>');
14+
assert.htmlEqual(
15+
target.innerHTML,
16+
'1 2 <button>1 / 2</button> <button>assign directly</button>'
17+
);
18+
19+
btn2.click();
20+
flushSync();
21+
assert.htmlEqual(
22+
target.innerHTML,
23+
'2 4 <button>2 / 4</button> <button>assign directly</button>'
24+
);
1225
}
1326
});
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
<script>
2-
import Sub from './sub.svelte'
2+
import Sub from './sub.svelte';
33
let sub = $state();
44
</script>
55

66
<Sub bind:this={sub} />
77
<button on:click={() => sub.increment()}>{sub?.count} / {sub?.doubled}</button>
8+
<button on:click={() => sub.count++}>assign directly</button>

0 commit comments

Comments
 (0)