Skip to content

Commit 520055c

Browse files
fix: always use set for private identifiers (#14378)
* fix: always use set for private identifiers * we can simplify this further - no need to check the value was transformed, since the outcome of not returning immediately is the same but with extra steps * add explanatory note --------- Co-authored-by: Rich Harris <[email protected]>
1 parent 85ec6fa commit 520055c

File tree

4 files changed

+66
-23
lines changed

4 files changed

+66
-23
lines changed

.changeset/tiny-trainers-sleep.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: always use set for private identifiers

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

Lines changed: 18 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -32,31 +32,26 @@ function build_assignment(operator, left, right, context) {
3232
if (
3333
context.state.analysis.runes &&
3434
left.type === 'MemberExpression' &&
35-
left.object.type === 'ThisExpression'
35+
left.property.type === 'PrivateIdentifier'
3636
) {
37-
if (left.property.type === 'PrivateIdentifier') {
38-
const private_state = context.state.private_state.get(left.property.name);
39-
40-
if (private_state !== undefined) {
41-
let transformed = false;
42-
let value = /** @type {Expression} */ (
43-
context.visit(build_assignment_value(operator, left, right))
44-
);
45-
46-
if (should_proxy(value, context.state.scope)) {
47-
transformed = true;
48-
value =
49-
private_state.kind === 'raw_state'
50-
? value
51-
: build_proxy_reassignment(value, b.member(b.this, private_state.id));
52-
}
53-
54-
if (!context.state.in_constructor) {
55-
return b.call('$.set', left, value);
56-
} else if (transformed) {
57-
return b.assignment(operator, /** @type {Pattern} */ (context.visit(left)), value);
58-
}
37+
const private_state = context.state.private_state.get(left.property.name);
38+
39+
if (private_state !== undefined) {
40+
let value = /** @type {Expression} */ (
41+
context.visit(build_assignment_value(operator, left, right))
42+
);
43+
44+
if (private_state.kind !== 'raw_state' && should_proxy(value, context.state.scope)) {
45+
value = build_proxy_reassignment(value, b.member(b.this, private_state.id));
5946
}
47+
48+
if (context.state.in_constructor) {
49+
// inside the constructor, we can assign to `this.#foo.v` rather than using `$.set`,
50+
// since nothing is tracking the signal at this point
51+
return b.assignment(operator, /** @type {Pattern} */ (context.visit(left)), value);
52+
}
53+
54+
return b.call('$.set', left, value);
6055
}
6156
}
6257

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { flushSync } from 'svelte';
2+
import { test } from '../../test';
3+
4+
export default test({
5+
html: `<p>42</p><p>1337</p><button></button>`,
6+
async test({ assert, target, instance }) {
7+
const [a, b] = target.querySelectorAll('p');
8+
const btn = target.querySelector('button');
9+
10+
flushSync(() => {
11+
btn?.click();
12+
});
13+
14+
assert.equal(a.textContent, '1337');
15+
assert.equal(b.textContent, '42');
16+
}
17+
});
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<script>
2+
class Box {
3+
#value = $state(0);
4+
5+
get value(){
6+
return this.#value;
7+
}
8+
9+
constructor(num){
10+
this.#value = num;
11+
}
12+
13+
swap(other) {
14+
const value = this.#value;
15+
this.#value = other.value;
16+
other.#value = value;
17+
}
18+
}
19+
20+
const a = new Box(42);
21+
const b = new Box(1337);
22+
</script>
23+
24+
<p>{a.value}</p>
25+
<p>{b.value}</p>
26+
<button onclick={()=>{a.swap(b)}}></button>

0 commit comments

Comments
 (0)