Skip to content

Commit ef1ffbe

Browse files
committed
extract onchange callbacks
1 parent 96e5edc commit ef1ffbe

File tree

7 files changed

+75
-58
lines changed

7 files changed

+75
-58
lines changed

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

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import * as b from '../../../../utils/builders.js';
66
import { regex_invalid_identifier_chars } from '../../../patterns.js';
77
import { get_rune } from '../../../scope.js';
88
import { should_proxy } from '../utils.js';
9+
import { get_onchange } from './shared/state.js';
910

1011
/**
1112
* @param {ClassBody} node
@@ -117,13 +118,16 @@ export function ClassBody(node, context) {
117118
);
118119

119120
if (field.kind === 'state' || field.kind === 'raw_state') {
120-
let arg = definition.value.arguments[1];
121-
let options = arg && /** @type {Expression} **/ (context.visit(arg, child_state));
121+
let onchange = get_onchange(
122+
/** @type {Expression} */ (definition.value.arguments[1]),
123+
// @ts-ignore mismatch between Context and ComponentContext. TODO look into
124+
context
125+
);
122126

123127
value =
124128
field.kind === 'state' && should_proxy(init, context.state.scope)
125-
? b.call('$.assignable_proxy', init, options)
126-
: b.call('$.state', init, options);
129+
? b.call('$.assignable_proxy', init, onchange)
130+
: b.call('$.state', init, onchange);
127131
} else {
128132
value = b.call('$.derived', field.kind === 'derived_by' ? init : b.thunk(init));
129133
}

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

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import * as assert from '../../../../utils/assert.js';
88
import { get_rune } from '../../../scope.js';
99
import { get_prop_source, is_prop_source, is_state_source, should_proxy } from '../utils.js';
1010
import { is_hoisted_function } from '../../utils.js';
11+
import { get_onchange } from './shared/state.js';
1112

1213
/**
1314
* @param {VariableDeclaration} node
@@ -117,34 +118,34 @@ export function VariableDeclaration(node, context) {
117118

118119
const args = /** @type {CallExpression} */ (init).arguments;
119120
const value = args.length > 0 ? /** @type {Expression} */ (context.visit(args[0])) : b.void0;
120-
let options =
121-
args.length === 2 ? /** @type {Expression} */ (context.visit(args[1])) : undefined;
121+
122+
const onchange = get_onchange(/** @type {Expression} */ (args[1]), context);
122123

123124
if (rune === '$state' || rune === '$state.raw') {
124125
/**
125126
* @param {Identifier} id
126127
* @param {Expression} value
127-
* @param {Expression} [options]
128+
* @param {Expression} [onchange]
128129
*/
129-
const create_state_declarator = (id, value, options) => {
130+
const create_state_declarator = (id, value, onchange) => {
130131
const binding = /** @type {Binding} */ (context.state.scope.get(id.name));
131132
const proxied = rune === '$state' && should_proxy(value, context.state.scope);
132133
const is_state = is_state_source(binding, context.state.analysis);
133134

134135
if (proxied) {
135-
return b.call(is_state ? '$.assignable_proxy' : '$.proxy', value, options);
136+
return b.call(is_state ? '$.assignable_proxy' : '$.proxy', value, onchange);
136137
}
137138

138139
if (is_state) {
139-
return b.call('$.state', value, options);
140+
return b.call('$.state', value, onchange);
140141
}
141142

142143
return value;
143144
};
144145

145146
if (declarator.id.type === 'Identifier') {
146147
declarations.push(
147-
b.declarator(declarator.id, create_state_declarator(declarator.id, value, options))
148+
b.declarator(declarator.id, create_state_declarator(declarator.id, value, onchange))
148149
);
149150
} else {
150151
const tmp = context.state.scope.generate('tmp');
@@ -157,7 +158,7 @@ export function VariableDeclaration(node, context) {
157158
return b.declarator(
158159
path.node,
159160
binding?.kind === 'state' || binding?.kind === 'raw_state'
160-
? create_state_declarator(binding.node, value, options)
161+
? create_state_declarator(binding.node, value, onchange)
161162
: value
162163
);
163164
})
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/** @import { Expression, Property } from 'estree' */
2+
/** @import { ComponentContext } from '../../types' */
3+
import * as b from '../../../../../utils/builders.js';
4+
5+
/**
6+
*
7+
* @param {Expression} options
8+
* @param {ComponentContext} context
9+
* @returns {Expression | undefined}
10+
*/
11+
export function get_onchange(options, context) {
12+
if (!options) {
13+
return undefined;
14+
}
15+
16+
if (options.type === 'ObjectExpression') {
17+
const onchange = /** @type {Property | undefined} */ (
18+
options.properties.find(
19+
(property) =>
20+
property.type === 'Property' &&
21+
!property.computed &&
22+
property.key.type === 'Identifier' &&
23+
property.key.name === 'onchange'
24+
)
25+
);
26+
27+
if (!onchange) {
28+
return undefined;
29+
}
30+
31+
return /** @type {Expression} */ (context.visit(onchange.value));
32+
}
33+
34+
return b.member(/** @type {Expression} */ (context.visit(options)), 'onchange');
35+
}

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

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -113,15 +113,7 @@ export {
113113
user_effect,
114114
user_pre_effect
115115
} from './reactivity/effects.js';
116-
export {
117-
mutable_source,
118-
mutate,
119-
set,
120-
state,
121-
update,
122-
update_pre,
123-
get_options
124-
} from './reactivity/sources.js';
116+
export { mutable_source, mutate, set, state, update, update_pre } from './reactivity/sources.js';
125117
export {
126118
prop,
127119
rest_props,

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

Lines changed: 15 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -34,18 +34,16 @@ var parent_metadata = null;
3434
/**
3535
* @template T
3636
* @param {T} value
37-
* @param {ValueOptions} [options]
37+
* @param {() => void} [onchange]
3838
* @param {Source<T>} [prev] dev mode only
3939
* @returns {T}
4040
*/
41-
export function proxy(value, options, prev) {
41+
export function proxy(value, onchange, prev) {
4242
// if non-proxyable, or is already a proxy, return `value`
4343
if (typeof value !== 'object' || value === null) {
4444
return value;
4545
}
4646

47-
var onchange = options?.onchange;
48-
4947
if (STATE_SYMBOL in value) {
5048
// @ts-ignore
5149
value[PROXY_ONCHANGE_SYMBOL](onchange);
@@ -104,10 +102,7 @@ export function proxy(value, options, prev) {
104102
if (is_proxied_array) {
105103
// We need to create the length source eagerly to ensure that
106104
// mutations to the array are properly synced with our proxy
107-
sources.set(
108-
'length',
109-
source(/** @type {any[]} */ (value).length, onchange && { onchange }, stack)
110-
);
105+
sources.set('length', source(/** @type {any[]} */ (value).length, onchange, stack));
111106
}
112107

113108
/** @type {ProxyMetadata} */
@@ -153,12 +148,12 @@ export function proxy(value, options, prev) {
153148
var s = sources.get(prop);
154149

155150
if (s === undefined) {
156-
s = with_parent(() => source(descriptor.value, onchange && { onchange }, stack));
151+
s = with_parent(() => source(descriptor.value, onchange, stack));
157152
sources.set(prop, s);
158153
} else {
159154
set(
160155
s,
161-
with_parent(() => proxy(descriptor.value, options))
156+
with_parent(() => proxy(descriptor.value, onchange))
162157
);
163158
}
164159

@@ -172,7 +167,7 @@ export function proxy(value, options, prev) {
172167
if (prop in target) {
173168
sources.set(
174169
prop,
175-
with_parent(() => source(UNINITIALIZED, onchange && { onchange }, stack))
170+
with_parent(() => source(UNINITIALIZED, onchange, stack))
176171
);
177172
}
178173
} else {
@@ -222,9 +217,7 @@ export function proxy(value, options, prev) {
222217
} else {
223218
onchange = value;
224219
for (let [, s] of sources) {
225-
if (s.o) {
226-
s.o.onchange = value;
227-
}
220+
s.o = value;
228221
}
229222
}
230223
};
@@ -235,7 +228,7 @@ export function proxy(value, options, prev) {
235228

236229
// create a source, but only if it's an own property and not a prototype property
237230
if (s === undefined && (!exists || get_descriptor(target, prop)?.writable)) {
238-
let opt = onchange && { onchange };
231+
let opt = onchange;
239232
s = with_parent(() =>
240233
source(proxy(exists ? target[prop] : UNINITIALIZED, opt), opt, stack)
241234
);
@@ -316,7 +309,7 @@ export function proxy(value, options, prev) {
316309
(active_effect !== null && (!has || get_descriptor(target, prop)?.writable))
317310
) {
318311
if (s === undefined) {
319-
let opt = onchange && { onchange };
312+
let opt = onchange;
320313
s = with_parent(() => source(has ? proxy(target[prop], opt) : UNINITIALIZED, opt, stack));
321314
sources.set(prop, s);
322315
}
@@ -355,7 +348,7 @@ export function proxy(value, options, prev) {
355348
// If the item exists in the original, we need to create a uninitialized source,
356349
// else a later read of the property would result in a source being created with
357350
// the value of the original item at that index.
358-
other_s = with_parent(() => source(UNINITIALIZED, onchange && { onchange }, stack));
351+
other_s = with_parent(() => source(UNINITIALIZED, onchange, stack));
359352
sources.set(i + '', other_s);
360353
}
361354
}
@@ -367,7 +360,7 @@ export function proxy(value, options, prev) {
367360
// object property before writing to that property.
368361
if (s === undefined) {
369362
if (!has || get_descriptor(target, prop)?.writable) {
370-
const opt = onchange && { onchange };
363+
const opt = onchange;
371364
s = with_parent(() => source(undefined, opt, stack));
372365
set(
373366
s,
@@ -384,7 +377,7 @@ export function proxy(value, options, prev) {
384377
}
385378
set(
386379
s,
387-
with_parent(() => proxy(value, onchange && { onchange }))
380+
with_parent(() => proxy(value, onchange))
388381
);
389382
}
390383
})();
@@ -450,11 +443,11 @@ export function proxy(value, options, prev) {
450443
/**
451444
* @template T
452445
* @param {T} value
453-
* @param {ValueOptions} [options]
446+
* @param {() => void} [onchange]
454447
* @returns {Source<T>}
455448
*/
456-
export function assignable_proxy(value, options) {
457-
return state(proxy(value, options), options);
449+
export function assignable_proxy(value, onchange) {
450+
return state(proxy(value, onchange), onchange);
458451
}
459452

460453
/**

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

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ export function batch_onchange(fn) {
7777
/**
7878
* @template V
7979
* @param {V} v
80-
* @param {ValueOptions} [o]
80+
* @param {() => void} [o]
8181
* @param {Error | null} [stack]
8282
* @returns {Source<V>}
8383
*/
@@ -102,18 +102,10 @@ export function source(v, o, stack) {
102102
return signal;
103103
}
104104

105-
/**
106-
* @param {Source} source
107-
* @returns {ValueOptions | undefined}
108-
*/
109-
export function get_options(source) {
110-
return source.o;
111-
}
112-
113105
/**
114106
* @template V
115107
* @param {V} v
116-
* @param {ValueOptions} [o]
108+
* @param {() => void} [o]
117109
* @param {Error | null} [stack]
118110
*/
119111
export function state(v, o, stack) {
@@ -196,11 +188,11 @@ export function internal_set(source, value) {
196188
if (!source.equals(value)) {
197189
var old_value = source.v;
198190

199-
if (typeof old_value === 'object' && old_value != null && source.o?.onchange) {
191+
if (typeof old_value === 'object' && old_value != null && source.o) {
200192
// @ts-ignore
201193
const remove = old_value[PROXY_ONCHANGE_SYMBOL];
202194
if (remove && typeof remove === 'function') {
203-
remove(source.o?.onchange, true);
195+
remove(source.o, true);
204196
}
205197
}
206198

@@ -257,7 +249,7 @@ export function internal_set(source, value) {
257249
inspect_effects.clear();
258250
}
259251

260-
var onchange = source.o?.onchange;
252+
var onchange = source.o;
261253
if (onchange) {
262254
if (onchange_batch) {
263255
onchange_batch.add(onchange);

packages/svelte/src/internal/client/reactivity/types.d.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ export interface Value<V = unknown> extends Signal {
2020
rv: number;
2121
/** The latest value for this signal */
2222
v: V;
23-
/** Options for the source */
24-
o?: ValueOptions;
23+
/** onchange callback */
24+
o?: () => void;
2525
/** Dev only */
2626
created?: Error | null;
2727
updated?: Error | null;

0 commit comments

Comments
 (0)