Skip to content

Commit

Permalink
fix: allow mutation of private derived state (#15228)
Browse files Browse the repository at this point in the history
Also disallow reassignment of private derived state

Fixes #15227
  • Loading branch information
paoloricciuti authored Feb 11, 2025
1 parent 7ab18bb commit 02788f8
Show file tree
Hide file tree
Showing 13 changed files with 102 additions and 21 deletions.
5 changes: 5 additions & 0 deletions .changeset/rare-carpets-wave.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'svelte': patch
---

fix: allow mutation of private derived state
4 changes: 2 additions & 2 deletions packages/svelte/src/compiler/phases/2-analyze/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -605,7 +605,7 @@ export function analyze_component(root, source, options) {
has_props_rune: false,
component_slots: new Set(),
expression: null,
private_derived_state: [],
derived_state: [],
function_depth: scope.function_depth,
instance_scope: instance.scope,
reactive_statement: null,
Expand Down Expand Up @@ -676,7 +676,7 @@ export function analyze_component(root, source, options) {
reactive_statements: analysis.reactive_statements,
component_slots: new Set(),
expression: null,
private_derived_state: [],
derived_state: [],
function_depth: scope.function_depth
};

Expand Down
2 changes: 1 addition & 1 deletion packages/svelte/src/compiler/phases/2-analyze/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export interface AnalysisState {
component_slots: Set<string>;
/** Information about the current expression/directive/block value */
expression: ExpressionMetadata | null;
private_derived_state: string[];
derived_state: string[];
function_depth: number;

// legacy stuff
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,20 @@ import { get_rune } from '../../scope.js';
*/
export function ClassBody(node, context) {
/** @type {string[]} */
const private_derived_state = [];
const derived_state = [];

for (const definition of node.body) {
if (
definition.type === 'PropertyDefinition' &&
definition.key.type === 'PrivateIdentifier' &&
(definition.key.type === 'PrivateIdentifier' || definition.key.type === 'Identifier') &&
definition.value?.type === 'CallExpression'
) {
const rune = get_rune(definition.value, context.state.scope);
if (rune === '$derived' || rune === '$derived.by') {
private_derived_state.push(definition.key.name);
derived_state.push(definition.key.name);
}
}
}

context.next({ ...context.state, private_derived_state });
context.next({ ...context.state, derived_state });
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,20 +35,17 @@ export function validate_assignment(node, argument, state) {
}
}

let object = /** @type {Expression | Super} */ (argument);

/** @type {Expression | PrivateIdentifier | null} */
let property = null;

while (object.type === 'MemberExpression') {
property = object.property;
object = object.object;
}

if (object.type === 'ThisExpression' && property?.type === 'PrivateIdentifier') {
if (state.private_derived_state.includes(property.name)) {
e.constant_assignment(node, 'derived state');
}
if (
argument.type === 'MemberExpression' &&
argument.object.type === 'ThisExpression' &&
(((argument.property.type === 'PrivateIdentifier' || argument.property.type === 'Identifier') &&
state.derived_state.includes(argument.property.name)) ||
(argument.property.type === 'Literal' &&
argument.property.value &&
typeof argument.property.value === 'string' &&
state.derived_state.includes(argument.property.value)))
) {
e.constant_assignment(node, 'derived state');
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[]
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<script>
class Test{
#der = $derived({test: 0});
set test(v){
this.#der.test = 45;
}
}
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[
{
"code": "constant_assignment",
"message": "Cannot assign to derived state",
"start": {
"column": 3,
"line": 6
},
"end": {
"column": 29,
"line": 6
}
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<script>
class Test{
der = $derived({ test: 0 });
set test(v){
this["der"] = { test: 45 };
}
}
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[
{
"code": "constant_assignment",
"message": "Cannot assign to derived state",
"start": {
"column": 3,
"line": 6
},
"end": {
"column": 27,
"line": 6
}
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<script>
class Test{
#der = $derived({ test: 0 });
set test(v){
this.#der = { test: 45 };
}
}
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[
{
"code": "constant_assignment",
"message": "Cannot assign to derived state",
"start": {
"column": 3,
"line": 6
},
"end": {
"column": 26,
"line": 6
}
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<script>
class Test{
der = $derived({ test: 0 });
set test(v){
this.der = { test: 45 };
}
}
</script>

0 comments on commit 02788f8

Please sign in to comment.