Skip to content

Commit c436b6c

Browse files
authored
fix: simplify set calls for proxyable values (#15548)
* chore: simplify set calls for proxyable values * changeset
1 parent 99ca7a4 commit c436b6c

File tree

11 files changed

+40
-60
lines changed

11 files changed

+40
-60
lines changed

.changeset/nine-laws-rush.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: simplify set calls for proxyable values

packages/svelte/src/compiler/phases/3-transform/client/types.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ export interface ClientTransformState extends TransformState {
3030
/** turn `foo` into e.g. `$.get(foo)` */
3131
read: (id: Identifier) => Expression;
3232
/** turn `foo = bar` into e.g. `$.set(foo, bar)` */
33-
assign?: (node: Identifier, value: Expression) => Expression;
33+
assign?: (node: Identifier, value: Expression, proxy?: boolean) => Expression;
3434
/** turn `foo.bar = baz` into e.g. `$.mutate(foo, $.get(foo).bar = baz);` */
3535
mutate?: (node: Identifier, mutation: AssignmentExpression | UpdateExpression) => Expression;
3636
/** turn `foo++` into e.g. `$.update(foo)` */

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

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -45,14 +45,6 @@ export function build_getter(node, state) {
4545
return node;
4646
}
4747

48-
/**
49-
* @param {Expression} value
50-
* @param {Expression} previous
51-
*/
52-
export function build_proxy_reassignment(value, previous) {
53-
return dev ? b.call('$.proxy', value, b.null, previous) : b.call('$.proxy', value);
54-
}
55-
5648
/**
5749
* @param {FunctionDeclaration | FunctionExpression | ArrowFunctionExpression} node
5850
* @param {ComponentContext} context

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

Lines changed: 17 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
/** @import { Location } from 'locate-character' */
2-
/** @import { AssignmentExpression, AssignmentOperator, Expression, Identifier, Literal, MemberExpression, Pattern } from 'estree' */
1+
/** @import { AssignmentExpression, AssignmentOperator, Expression, Identifier, Pattern } from 'estree' */
32
/** @import { AST } from '#compiler' */
43
/** @import { Context } from '../types.js' */
54
import * as b from '../../../../utils/builders.js';
@@ -8,8 +7,8 @@ import {
87
get_attribute_expression,
98
is_event_attribute
109
} from '../../../../utils/ast.js';
11-
import { dev, filename, is_ignored, locate_node, locator } from '../../../../state.js';
12-
import { build_proxy_reassignment, should_proxy } from '../utils.js';
10+
import { dev, is_ignored, locate_node } from '../../../../state.js';
11+
import { should_proxy } from '../utils.js';
1312
import { visit_assignment_expression } from '../../shared/assignments.js';
1413

1514
/**
@@ -65,21 +64,12 @@ function build_assignment(operator, left, right, context) {
6564
context.visit(build_assignment_value(operator, left, right))
6665
);
6766

68-
if (
67+
const needs_proxy =
6968
private_state.kind === 'state' &&
7069
is_non_coercive_operator(operator) &&
71-
should_proxy(value, context.state.scope)
72-
) {
73-
value = build_proxy_reassignment(value, b.member(b.this, private_state.id));
74-
}
75-
76-
if (context.state.in_constructor) {
77-
// inside the constructor, we can assign to `this.#foo.v` rather than using `$.set`,
78-
// since nothing is tracking the signal at this point
79-
return b.assignment(operator, /** @type {Pattern} */ (context.visit(left)), value);
80-
}
70+
should_proxy(value, context.state.scope);
8171

82-
return b.call('$.set', left, value);
72+
return b.call('$.set', left, value, needs_proxy && b.true);
8373
}
8474
}
8575

@@ -113,20 +103,18 @@ function build_assignment(operator, left, right, context) {
113103
context.visit(build_assignment_value(operator, left, right))
114104
);
115105

116-
if (
106+
return transform.assign(
107+
object,
108+
value,
117109
!is_primitive &&
118-
binding.kind !== 'prop' &&
119-
binding.kind !== 'bindable_prop' &&
120-
binding.kind !== 'raw_state' &&
121-
binding.kind !== 'store_sub' &&
122-
context.state.analysis.runes &&
123-
should_proxy(right, context.state.scope) &&
124-
is_non_coercive_operator(operator)
125-
) {
126-
value = build_proxy_reassignment(value, object);
127-
}
128-
129-
return transform.assign(object, value);
110+
binding.kind !== 'prop' &&
111+
binding.kind !== 'bindable_prop' &&
112+
binding.kind !== 'raw_state' &&
113+
binding.kind !== 'store_sub' &&
114+
context.state.analysis.runes &&
115+
should_proxy(right, context.state.scope) &&
116+
is_non_coercive_operator(operator)
117+
);
130118
}
131119

132120
// mutation

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

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { dev, is_ignored } from '../../../../state.js';
55
import * as b from '../../../../utils/builders.js';
66
import { regex_invalid_identifier_chars } from '../../../patterns.js';
77
import { get_rune } from '../../../scope.js';
8-
import { build_proxy_reassignment, should_proxy } from '../utils.js';
8+
import { should_proxy } from '../utils.js';
99

1010
/**
1111
* @param {ClassBody} node
@@ -142,29 +142,20 @@ export function ClassBody(node, context) {
142142
// get foo() { return this.#foo; }
143143
body.push(b.method('get', definition.key, [], [b.return(b.call('$.get', member))]));
144144

145-
if (field.kind === 'state') {
145+
if (field.kind === 'state' || field.kind === 'raw_state') {
146146
// set foo(value) { this.#foo = value; }
147147
const value = b.id('value');
148-
const prev = b.member(b.this, field.id);
149148

150149
body.push(
151150
b.method(
152151
'set',
153152
definition.key,
154153
[value],
155-
[b.stmt(b.call('$.set', member, build_proxy_reassignment(value, prev)))]
154+
[b.stmt(b.call('$.set', member, value, field.kind === 'state' && b.true))]
156155
)
157156
);
158157
}
159158

160-
if (field.kind === 'raw_state') {
161-
// set foo(value) { this.#foo = value; }
162-
const value = b.id('value');
163-
body.push(
164-
b.method('set', definition.key, [value], [b.stmt(b.call('$.set', member, value))])
165-
);
166-
}
167-
168159
if (dev && (field.kind === 'derived' || field.kind === 'derived_by')) {
169160
body.push(
170161
b.method(

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ export function add_state_transformers(context) {
2424
) {
2525
context.state.transform[name] = {
2626
read: binding.declaration_kind === 'var' ? (node) => b.call('$.safe_get', node) : get_value,
27-
assign: (node, value) => {
28-
let call = b.call('$.set', node, value);
27+
assign: (node, value, proxy = false) => {
28+
let call = b.call('$.set', node, value, proxy && b.true);
2929

3030
if (context.state.scope.get(`$${node.name}`)?.kind === 'store_sub') {
3131
call = b.call('$.store_unsub', call, b.literal(`$${node.name}`), b.id('$$stores'));

packages/svelte/src/internal/client/reactivity/sources.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import * as e from '../errors.js';
3333
import { legacy_mode_flag, tracing_mode_flag } from '../../flags/index.js';
3434
import { get_stack } from '../dev/tracing.js';
3535
import { component_context, is_runes } from '../context.js';
36+
import { proxy } from '../proxy.js';
3637

3738
export let inspect_effects = new Set();
3839
export const old_values = new Map();
@@ -143,9 +144,10 @@ export function mutate(source, value) {
143144
* @template V
144145
* @param {Source<V>} source
145146
* @param {V} value
147+
* @param {boolean} [should_proxy]
146148
* @returns {V}
147149
*/
148-
export function set(source, value) {
150+
export function set(source, value, should_proxy = false) {
149151
if (
150152
active_reaction !== null &&
151153
!untracking &&
@@ -158,7 +160,9 @@ export function set(source, value) {
158160
e.state_unsafe_mutation();
159161
}
160162

161-
return internal_set(source, value);
163+
let new_value = should_proxy ? proxy(value, null, source) : value;
164+
165+
return internal_set(source, new_value);
162166
}
163167

164168
/**

packages/svelte/tests/snapshot/samples/bind-component-snippet/_expected/client/index.svelte.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ export default function Bind_component_snippet($$anchor) {
2323
return $.get(value);
2424
},
2525
set value($$value) {
26-
$.set(value, $.proxy($$value));
26+
$.set(value, $$value, true);
2727
}
2828
});
2929

packages/svelte/tests/snapshot/samples/class-state-field-constructor-assignment/_expected/client/index.svelte.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,14 @@ export default function Class_state_field_constructor_assignment($$anchor, $$pro
1212
}
1313

1414
set a(value) {
15-
$.set(this.#a, $.proxy(value));
15+
$.set(this.#a, value, true);
1616
}
1717

1818
#b = $.state();
1919

2020
constructor() {
2121
this.a = 1;
22-
this.#b.v = 2;
22+
$.set(this.#b, 2);
2323
}
2424
}
2525

packages/svelte/tests/snapshot/samples/destructured-assignments/_expected/client/index.svelte.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ let d = 4;
88

99
export function update(array) {
1010
(
11-
$.set(a, $.proxy(array[0])),
12-
$.set(b, $.proxy(array[1]))
11+
$.set(a, array[0], true),
12+
$.set(b, array[1], true)
1313
);
1414

1515
[c, d] = array;

packages/svelte/tests/snapshot/samples/function-prop-no-getter/_expected/client/index.svelte.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export default function Function_prop_no_getter($$anchor) {
1313
Button($$anchor, {
1414
onmousedown: () => $.set(count, $.get(count) + 1),
1515
onmouseup,
16-
onmouseenter: () => $.set(count, $.proxy(plusOne($.get(count)))),
16+
onmouseenter: () => $.set(count, plusOne($.get(count)), true),
1717
children: ($$anchor, $$slotProps) => {
1818
$.next();
1919

0 commit comments

Comments
 (0)