Skip to content

Commit e1e3bbf

Browse files
committed
fix: fix merge bugs and adapt to latest changes
1 parent 9ddff21 commit e1e3bbf

File tree

9 files changed

+204
-33
lines changed

9 files changed

+204
-33
lines changed

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import * as b from '#compiler/builders';
55
import { get_rune } from '../../../scope.js';
66
import { transform_inspect_rune } from '../../utils.js';
77
import { should_proxy } from '../utils.js';
8+
import { get_onchange } from './shared/state.js';
89

910
/**
1011
* @param {CallExpression} node
@@ -24,6 +25,7 @@ export function CallExpression(node, context) {
2425
case '$state':
2526
case '$state.raw': {
2627
let arg = node.arguments[0];
28+
let onchange = get_onchange(/** @type {Expression} */ (node.arguments[1]), context);
2729

2830
/** @type {Expression | undefined} */
2931
let value = undefined;
@@ -35,11 +37,11 @@ export function CallExpression(node, context) {
3537
rune === '$state' &&
3638
should_proxy(/** @type {Expression} */ (arg), context.state.scope)
3739
) {
38-
value = b.call('$.proxy', value);
40+
return b.call('$.assignable_proxy', value, onchange);
3941
}
4042
}
4143

42-
return b.call('$.state', value);
44+
return b.call('$.state', value, onchange);
4345
}
4446

4547
case '$derived':

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -140,15 +140,17 @@ export function VariableDeclaration(node, context) {
140140
const is_proxy = should_proxy(value, context.state.scope);
141141

142142
if (rune === '$state' && is_proxy) {
143-
value = b.call('$.proxy', value, onchange);
143+
value = b.call(is_state ? '$.assignable_proxy' : '$.proxy', value, onchange);
144144

145145
if (dev && !is_state) {
146146
value = b.call('$.tag_proxy', value, b.literal(id.name));
147147
}
148148
}
149149

150150
if (is_state) {
151-
value = b.call('$.state', value, onchange);
151+
if (!(rune === '$state' && is_proxy)) {
152+
value = b.call('$.state', value, onchange);
153+
}
152154

153155
if (dev) {
154156
value = b.call('$.tag', value, b.literal(id.name));

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
/** @import { Expression, Property } from 'estree' */
2-
/** @import { ComponentContext } from '../../types' */
2+
/** @import { ComponentContext, Context } from '../../types' */
33
import * as b from '../../../../../utils/builders.js';
44

55
/**
66
* Extract the `onchange` callback from the options passed to `$state`
77
* @param {Expression} options
8-
* @param {ComponentContext} context
8+
* @param {ComponentContext | Context} context
99
* @returns {Expression | undefined}
1010
*/
1111
export function get_onchange(options, context) {

packages/svelte/src/internal/client/proxy.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ import {
2222
flush_inspect_effects,
2323
set_inspect_effects_deferred,
2424
batch_onchange,
25-
state
25+
state,
26+
onchange_batch
2627
} from './reactivity/sources.js';
2728
import { PROXY_PATH_SYMBOL, STATE_SYMBOL, PROXY_ONCHANGE_SYMBOL } from '#client/constants';
2829
import { UNINITIALIZED } from '../../constants.js';
@@ -325,7 +326,7 @@ export function proxy(value, onchange) {
325326
// if we are changing the length of the array we batch all the changes
326327
// to the sources and the original value by calling batch_onchange and immediately
327328
// invoking it...otherwise we just invoke an identity function
328-
(is_proxied_array && prop === 'length' ? batch_onchange : identity)(() => {
329+
(is_proxied_array && prop === 'length' && !onchange_batch ? batch_onchange : identity)(() => {
329330
// variable.length = value -> clear all signals with index >= value
330331
if (is_proxied_array && prop === 'length') {
331332
for (var i = value; i < /** @type {Source<number>} */ (s).v; i += 1) {
@@ -361,9 +362,8 @@ export function proxy(value, onchange) {
361362
if (s === undefined) {
362363
if (!has || get_descriptor(target, prop)?.writable) {
363364
s = with_parent(() => source(undefined, onchange, stack));
364-
set(s, proxy(value, onchange));
365-
366365
sources.set(prop, s);
366+
set(s, proxy(value, onchange));
367367

368368
if (DEV) {
369369
tag(s, get_label(path, prop));
@@ -373,13 +373,13 @@ export function proxy(value, onchange) {
373373
has = s.v !== UNINITIALIZED;
374374

375375
var p = with_parent(() => proxy(value, onchange));
376-
set(s, p);
377-
378376
// when we set a property if the source is a proxy we remove the current onchange from
379377
// the proxy `onchanges` so that it doesn't trigger it anymore
380378
if (onchange && typeof s.v === 'object' && s.v !== null && STATE_SYMBOL in s.v) {
381379
s.v[PROXY_ONCHANGE_SYMBOL](onchange, true);
382380
}
381+
382+
set(s, p);
383383
}
384384
})();
385385

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

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ export function set_inspect_effects_deferred() {
5757
}
5858

5959
/** @type {null | Set<() => void>} */
60-
let onchange_batch = null;
60+
export let onchange_batch = null;
6161

6262
/**
6363
* @param {Function} fn
@@ -271,6 +271,15 @@ export function internal_set(source, value) {
271271
if (DEV && inspect_effects.size > 0 && !inspect_effects_deferred) {
272272
flush_inspect_effects();
273273
}
274+
275+
var onchange = source.o;
276+
if (onchange) {
277+
if (onchange_batch) {
278+
onchange_batch.add(onchange);
279+
} else {
280+
onchange();
281+
}
282+
}
274283
}
275284

276285
return value;
@@ -294,14 +303,6 @@ export function flush_inspect_effects() {
294303
}
295304

296305
inspect_effects.clear();
297-
var onchange = source.o;
298-
if (onchange) {
299-
if (onchange_batch) {
300-
onchange_batch.add(onchange);
301-
} else {
302-
onchange();
303-
}
304-
}
305306
}
306307

307308
/**

packages/svelte/tests/runtime-runes/samples/state-onchange-classes/_config.js

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,14 @@ import { test } from '../../test';
33

44
export default test({
55
async test({ assert, target, logs }) {
6-
const [btn, btn2, btn3] = target.querySelectorAll('button');
6+
const [btn, btn2, btn3, btn4, btn5, btn6, btn7] = target.querySelectorAll('button');
77

8-
assert.deepEqual(logs, ['constructor count', 'constructor proxy']);
8+
assert.deepEqual(logs, [
9+
'constructor count',
10+
'constructor proxy',
11+
'assign in constructor',
12+
'assign in constructor proxy'
13+
]);
914

1015
logs.length = 0;
1116

@@ -17,5 +22,43 @@ export default test({
1722

1823
flushSync(() => btn3.click());
1924
assert.deepEqual(logs, ['class count', 'class proxy', 'class proxy']);
25+
26+
flushSync(() => btn4.click());
27+
assert.deepEqual(logs, [
28+
'class count',
29+
'class proxy',
30+
'class proxy',
31+
'declared in constructor'
32+
]);
33+
34+
flushSync(() => btn5.click());
35+
assert.deepEqual(logs, [
36+
'class count',
37+
'class proxy',
38+
'class proxy',
39+
'declared in constructor',
40+
'declared in constructor'
41+
]);
42+
43+
flushSync(() => btn6.click());
44+
assert.deepEqual(logs, [
45+
'class count',
46+
'class proxy',
47+
'class proxy',
48+
'declared in constructor',
49+
'declared in constructor',
50+
'declared in constructor proxy'
51+
]);
52+
53+
flushSync(() => btn7.click());
54+
assert.deepEqual(logs, [
55+
'class count',
56+
'class proxy',
57+
'class proxy',
58+
'declared in constructor',
59+
'declared in constructor',
60+
'declared in constructor proxy',
61+
'declared in constructor proxy'
62+
]);
2063
}
2164
});

packages/svelte/tests/runtime-runes/samples/state-onchange-classes/main.svelte

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,36 @@
2323
}
2424
});
2525
26+
declared_in_constructor;
27+
declared_in_constructor_proxy;
28+
#assign_in_constructor;
29+
#assign_in_constructor_proxy;
30+
2631
constructor(){
2732
this.#in_constructor = 42;
2833
this.#in_constructor_proxy.count++;
34+
this.declared_in_constructor = $state(0, {
35+
onchange(){
36+
console.log("declared in constructor");
37+
}
38+
});
39+
this.declared_in_constructor_proxy = $state({ count: 0 }, {
40+
onchange(){
41+
console.log("declared in constructor proxy");
42+
}
43+
});
44+
this.#assign_in_constructor = $state(0, {
45+
onchange(){
46+
console.log("assign in constructor");
47+
}
48+
});
49+
this.#assign_in_constructor++;
50+
this.#assign_in_constructor_proxy = $state({ count: 0 }, {
51+
onchange(){
52+
console.log("assign in constructor proxy");
53+
}
54+
});
55+
this.#assign_in_constructor_proxy.count++;
2956
}
3057
}
3158
@@ -34,4 +61,8 @@
3461

3562
<button onclick={()=> class_test.count++}>{class_test.count}</button>
3663
<button onclick={()=> class_test.proxy.count++}>{class_test.proxy.count}</button>
37-
<button onclick={()=> class_test.proxy = {count: class_test.proxy.count+1}}>{class_test.proxy.count}</button>
64+
<button onclick={()=> class_test.proxy = {count: class_test.proxy.count+1}}>{class_test.proxy.count}</button>
65+
<button onclick={()=> class_test.declared_in_constructor++}>{class_test.declared_in_constructor}</button>
66+
<button onclick={()=> class_test.declared_in_constructor = class_test.declared_in_constructor + 1 }>{class_test.declared_in_constructor}</button>
67+
<button onclick={()=> class_test.declared_in_constructor_proxy.count++}>{class_test.declared_in_constructor_proxy.count}</button>
68+
<button onclick={()=> class_test.declared_in_constructor_proxy.count = class_test.declared_in_constructor_proxy.count + 1 }>{class_test.declared_in_constructor_proxy.count}</button>

packages/svelte/tests/runtime-runes/samples/state-raw-onchange/_config.js

Lines changed: 69 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,15 @@ import { test } from '../../test';
33

44
export default test({
55
async test({ assert, target, logs }) {
6-
const [btn, btn2, btn3, btn4, btn5, btn6, btn7, btn8, btn9, btn10] =
6+
const [btn, btn2, btn3, btn4, btn5, btn6, btn7, btn8, btn9, btn10, btn11, btn12, btn13] =
77
target.querySelectorAll('button');
88

9-
assert.deepEqual(logs, ['constructor count', 'constructor object']);
9+
assert.deepEqual(logs, [
10+
'constructor count',
11+
'constructor object',
12+
'assign in constructor',
13+
'assign in constructor object'
14+
]);
1015

1116
logs.length = 0;
1217

@@ -29,15 +34,73 @@ export default test({
2934
assert.deepEqual(logs, ['count', 'object', 'class count', 'class object']);
3035

3136
flushSync(() => btn7.click());
32-
assert.deepEqual(logs, ['count', 'object', 'class count', 'class object']);
37+
assert.deepEqual(logs, [
38+
'count',
39+
'object',
40+
'class count',
41+
'class object',
42+
'declared in constructor'
43+
]);
3344

3445
flushSync(() => btn8.click());
35-
assert.deepEqual(logs, ['count', 'object', 'class count', 'class object']);
46+
assert.deepEqual(logs, [
47+
'count',
48+
'object',
49+
'class count',
50+
'class object',
51+
'declared in constructor',
52+
'declared in constructor object'
53+
]);
3654

3755
flushSync(() => btn9.click());
38-
assert.deepEqual(logs, ['count', 'object', 'class count', 'class object']);
56+
assert.deepEqual(logs, [
57+
'count',
58+
'object',
59+
'class count',
60+
'class object',
61+
'declared in constructor',
62+
'declared in constructor object'
63+
]);
3964

4065
flushSync(() => btn10.click());
41-
assert.deepEqual(logs, ['count', 'object', 'class count', 'class object', 'arr']);
66+
assert.deepEqual(logs, [
67+
'count',
68+
'object',
69+
'class count',
70+
'class object',
71+
'declared in constructor',
72+
'declared in constructor object'
73+
]);
74+
75+
flushSync(() => btn11.click());
76+
assert.deepEqual(logs, [
77+
'count',
78+
'object',
79+
'class count',
80+
'class object',
81+
'declared in constructor',
82+
'declared in constructor object'
83+
]);
84+
85+
flushSync(() => btn12.click());
86+
assert.deepEqual(logs, [
87+
'count',
88+
'object',
89+
'class count',
90+
'class object',
91+
'declared in constructor',
92+
'declared in constructor object'
93+
]);
94+
95+
flushSync(() => btn13.click());
96+
assert.deepEqual(logs, [
97+
'count',
98+
'object',
99+
'class count',
100+
'class object',
101+
'declared in constructor',
102+
'declared in constructor object',
103+
'arr'
104+
]);
42105
}
43106
});

0 commit comments

Comments
 (0)