Skip to content

Commit 02788f8

Browse files
fix: allow mutation of private derived state (#15228)
Also disallow reassignment of private derived state Fixes #15227
1 parent 7ab18bb commit 02788f8

File tree

13 files changed

+102
-21
lines changed

13 files changed

+102
-21
lines changed

.changeset/rare-carpets-wave.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'svelte': patch
3+
---
4+
5+
fix: allow mutation of private derived state

packages/svelte/src/compiler/phases/2-analyze/index.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -605,7 +605,7 @@ export function analyze_component(root, source, options) {
605605
has_props_rune: false,
606606
component_slots: new Set(),
607607
expression: null,
608-
private_derived_state: [],
608+
derived_state: [],
609609
function_depth: scope.function_depth,
610610
instance_scope: instance.scope,
611611
reactive_statement: null,
@@ -676,7 +676,7 @@ export function analyze_component(root, source, options) {
676676
reactive_statements: analysis.reactive_statements,
677677
component_slots: new Set(),
678678
expression: null,
679-
private_derived_state: [],
679+
derived_state: [],
680680
function_depth: scope.function_depth
681681
};
682682

packages/svelte/src/compiler/phases/2-analyze/types.d.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ export interface AnalysisState {
1919
component_slots: Set<string>;
2020
/** Information about the current expression/directive/block value */
2121
expression: ExpressionMetadata | null;
22-
private_derived_state: string[];
22+
derived_state: string[];
2323
function_depth: number;
2424

2525
// legacy stuff

packages/svelte/src/compiler/phases/2-analyze/visitors/ClassBody.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,20 @@ import { get_rune } from '../../scope.js';
88
*/
99
export function ClassBody(node, context) {
1010
/** @type {string[]} */
11-
const private_derived_state = [];
11+
const derived_state = [];
1212

1313
for (const definition of node.body) {
1414
if (
1515
definition.type === 'PropertyDefinition' &&
16-
definition.key.type === 'PrivateIdentifier' &&
16+
(definition.key.type === 'PrivateIdentifier' || definition.key.type === 'Identifier') &&
1717
definition.value?.type === 'CallExpression'
1818
) {
1919
const rune = get_rune(definition.value, context.state.scope);
2020
if (rune === '$derived' || rune === '$derived.by') {
21-
private_derived_state.push(definition.key.name);
21+
derived_state.push(definition.key.name);
2222
}
2323
}
2424
}
2525

26-
context.next({ ...context.state, private_derived_state });
26+
context.next({ ...context.state, derived_state });
2727
}

packages/svelte/src/compiler/phases/2-analyze/visitors/shared/utils.js

+11-14
Original file line numberDiff line numberDiff line change
@@ -35,20 +35,17 @@ export function validate_assignment(node, argument, state) {
3535
}
3636
}
3737

38-
let object = /** @type {Expression | Super} */ (argument);
39-
40-
/** @type {Expression | PrivateIdentifier | null} */
41-
let property = null;
42-
43-
while (object.type === 'MemberExpression') {
44-
property = object.property;
45-
object = object.object;
46-
}
47-
48-
if (object.type === 'ThisExpression' && property?.type === 'PrivateIdentifier') {
49-
if (state.private_derived_state.includes(property.name)) {
50-
e.constant_assignment(node, 'derived state');
51-
}
38+
if (
39+
argument.type === 'MemberExpression' &&
40+
argument.object.type === 'ThisExpression' &&
41+
(((argument.property.type === 'PrivateIdentifier' || argument.property.type === 'Identifier') &&
42+
state.derived_state.includes(argument.property.name)) ||
43+
(argument.property.type === 'Literal' &&
44+
argument.property.value &&
45+
typeof argument.property.value === 'string' &&
46+
state.derived_state.includes(argument.property.value)))
47+
) {
48+
e.constant_assignment(node, 'derived state');
5249
}
5350
}
5451

Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
[]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<script>
2+
class Test{
3+
#der = $derived({test: 0});
4+
5+
set test(v){
6+
this.#der.test = 45;
7+
}
8+
}
9+
</script>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
[
2+
{
3+
"code": "constant_assignment",
4+
"message": "Cannot assign to derived state",
5+
"start": {
6+
"column": 3,
7+
"line": 6
8+
},
9+
"end": {
10+
"column": 29,
11+
"line": 6
12+
}
13+
}
14+
]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<script>
2+
class Test{
3+
der = $derived({ test: 0 });
4+
5+
set test(v){
6+
this["der"] = { test: 45 };
7+
}
8+
}
9+
</script>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
[
2+
{
3+
"code": "constant_assignment",
4+
"message": "Cannot assign to derived state",
5+
"start": {
6+
"column": 3,
7+
"line": 6
8+
},
9+
"end": {
10+
"column": 27,
11+
"line": 6
12+
}
13+
}
14+
]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<script>
2+
class Test{
3+
#der = $derived({ test: 0 });
4+
5+
set test(v){
6+
this.#der = { test: 45 };
7+
}
8+
}
9+
</script>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
[
2+
{
3+
"code": "constant_assignment",
4+
"message": "Cannot assign to derived state",
5+
"start": {
6+
"column": 3,
7+
"line": 6
8+
},
9+
"end": {
10+
"column": 26,
11+
"line": 6
12+
}
13+
}
14+
]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<script>
2+
class Test{
3+
der = $derived({ test: 0 });
4+
5+
set test(v){
6+
this.der = { test: 45 };
7+
}
8+
}
9+
</script>

0 commit comments

Comments
 (0)