Skip to content

Commit bdd63c8

Browse files
authored
fix: ensure class constructor values are proxied (#9888)
* fix: ensure class constructor values are proxied * debugger
1 parent 646c0c4 commit bdd63c8

File tree

6 files changed

+95
-3
lines changed

6 files changed

+95
-3
lines changed

.changeset/tall-books-grin.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: ensure class constructor values are proxied

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

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -157,16 +157,45 @@ export function serialize_set_binding(node, context, fallback) {
157157

158158
let left = node.left;
159159

160+
// Handle class private/public state assignment cases
160161
while (left.type === 'MemberExpression') {
161-
if (left.object.type === 'ThisExpression' && left.property.type === 'PrivateIdentifier') {
162-
if (context.state.private_state.has(left.property.name) && !state.in_constructor) {
163-
const value = get_assignment_value(node, context);
162+
if (
163+
left.object.type === 'ThisExpression' &&
164+
left.property.type === 'PrivateIdentifier' &&
165+
context.state.private_state.has(left.property.name)
166+
) {
167+
const value = get_assignment_value(node, context);
168+
if (state.in_constructor) {
169+
// See if we should wrap value in $.proxy
170+
if (context.state.analysis.runes && should_proxy(value)) {
171+
const assignment = fallback();
172+
if (assignment.type === 'AssignmentExpression') {
173+
assignment.right = b.call('$.proxy', value);
174+
return assignment;
175+
}
176+
}
177+
} else {
164178
return b.call(
165179
'$.set',
166180
left,
167181
context.state.analysis.runes && should_proxy(value) ? b.call('$.proxy', value) : value
168182
);
169183
}
184+
} else if (
185+
left.object.type === 'ThisExpression' &&
186+
left.property.type === 'Identifier' &&
187+
context.state.public_state.has(left.property.name) &&
188+
state.in_constructor
189+
) {
190+
const value = get_assignment_value(node, context);
191+
// See if we should wrap value in $.proxy
192+
if (context.state.analysis.runes && should_proxy(value)) {
193+
const assignment = fallback();
194+
if (assignment.type === 'AssignmentExpression') {
195+
assignment.right = b.call('$.proxy', value);
196+
return assignment;
197+
}
198+
}
170199
}
171200
// @ts-expect-error
172201
left = left.object;
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { test } from '../../test';
2+
3+
export default test({
4+
html: `<button>0</button>`,
5+
6+
async test({ assert, target }) {
7+
const btn = target.querySelector('button');
8+
9+
await btn?.click();
10+
assert.htmlEqual(target.innerHTML, `<button>1</button>`);
11+
12+
await btn?.click();
13+
assert.htmlEqual(target.innerHTML, `<button>2</button>`);
14+
}
15+
});
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<script>
2+
class Counter {
3+
#count = $state();
4+
5+
constructor(v) {
6+
this.#count = v;
7+
}
8+
9+
get count() {
10+
return this.#count;
11+
}
12+
}
13+
const counter = new Counter({ count: 0 });
14+
</script>
15+
16+
<button on:click={() => counter.count.count++}>{counter.count.count}</button>
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { test } from '../../test';
2+
3+
export default test({
4+
html: `<button>0</button>`,
5+
6+
async test({ assert, target }) {
7+
const btn = target.querySelector('button');
8+
9+
await btn?.click();
10+
assert.htmlEqual(target.innerHTML, `<button>1</button>`);
11+
12+
await btn?.click();
13+
assert.htmlEqual(target.innerHTML, `<button>2</button>`);
14+
}
15+
});
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<script>
2+
class Counter {
3+
count = $state();
4+
5+
constructor(v) {
6+
this.count = v;
7+
}
8+
}
9+
const counter = new Counter({ count: 0 });
10+
</script>
11+
12+
<button on:click={() => counter.count.count++}>{counter.count.count}</button>

0 commit comments

Comments
 (0)