Skip to content

Commit b20b461

Browse files
authored
chore: rethink props (#9826)
Cleaned up prop_source and renamed it to prop. Updated tests accordingly
1 parent c9c2bde commit b20b461

File tree

67 files changed

+395
-359
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

67 files changed

+395
-359
lines changed

.changeset/empty-crabs-think.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+
chore: refactor props handling

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -255,8 +255,8 @@ export function client_component(source, analysis, options) {
255255
const key = binding.prop_alias ?? name;
256256

257257
properties.push(
258-
b.get(key, [b.return(b.call('$.get', b.id(name)))]),
259-
b.set(key, [b.stmt(b.call('$.set_sync', b.id(name), b.id('$$value')))])
258+
b.get(key, [b.return(b.call(b.id(name)))]),
259+
b.set(key, [b.stmt(b.call(b.id(name), b.id('$$value'))), b.stmt(b.call('$.flushSync'))])
260260
);
261261
}
262262
}

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

Lines changed: 45 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@ import * as b from '../../../utils/builders.js';
22
import { extract_paths, is_simple_expression } from '../../../utils/ast.js';
33
import { error } from '../../../errors.js';
44
import {
5-
PROPS_CALL_DEFAULT_VALUE,
5+
PROPS_IS_LAZY_INITIAL,
66
PROPS_IS_IMMUTABLE,
7-
PROPS_IS_RUNES
7+
PROPS_IS_RUNES,
8+
PROPS_IS_UPDATED
89
} from '../../../../constants.js';
910

1011
/**
@@ -73,12 +74,14 @@ export function serialize_get_binding(node, state) {
7374
}
7475

7576
if (
76-
!state.analysis.accessors &&
77-
!(state.analysis.immutable ? binding.reassigned : binding.mutated) &&
78-
!binding.initial
77+
state.analysis.accessors ||
78+
(state.analysis.immutable ? binding.reassigned : binding.mutated) ||
79+
binding.initial
7980
) {
80-
return b.member(b.id('$$props'), node);
81+
return b.call(node);
8182
}
83+
84+
return b.member(b.id('$$props'), node);
8285
}
8386

8487
if (binding.kind === 'legacy_reactive_import') {
@@ -89,7 +92,6 @@ export function serialize_get_binding(node, state) {
8992
(binding.kind === 'state' &&
9093
(!state.analysis.immutable || state.analysis.accessors || binding.reassigned)) ||
9194
binding.kind === 'derived' ||
92-
binding.kind === 'prop' ||
9395
binding.kind === 'legacy_reactive'
9496
) {
9597
return b.call('$.get', node);
@@ -208,9 +210,12 @@ export function serialize_set_binding(node, context, fallback) {
208210
}
209211

210212
const value = get_assignment_value(node, context);
213+
211214
const serialize = () => {
212215
if (left === node.left) {
213-
if (is_store) {
216+
if (binding.kind === 'prop') {
217+
return b.call(left, value);
218+
} else if (is_store) {
214219
return b.call('$.store_set', serialize_get_binding(b.id(left_name), state), value);
215220
} else {
216221
return b.call(
@@ -232,15 +237,27 @@ export function serialize_set_binding(node, context, fallback) {
232237
b.call('$' + left_name)
233238
);
234239
} else if (!state.analysis.runes) {
235-
return b.call(
236-
'$.mutate',
237-
b.id(left_name),
238-
b.assignment(
239-
node.operator,
240-
/** @type {import('estree').Pattern} */ (visit(node.left)),
241-
value
242-
)
243-
);
240+
if (binding.kind === 'prop') {
241+
return b.call(
242+
left,
243+
b.assignment(
244+
node.operator,
245+
/** @type {import('estree').Pattern} */ (visit(node.left)),
246+
value
247+
),
248+
b.literal(true)
249+
);
250+
} else {
251+
return b.call(
252+
'$.mutate',
253+
b.id(left_name),
254+
b.assignment(
255+
node.operator,
256+
/** @type {import('estree').Pattern} */ (visit(node.left)),
257+
value
258+
)
259+
);
260+
}
244261
} else {
245262
return b.assignment(
246263
node.operator,
@@ -345,12 +362,13 @@ export function serialize_hoistable_params(node, context) {
345362
}
346363

347364
/**
365+
* @param {import('#compiler').Binding} binding
348366
* @param {import('./types').ComponentClientTransformState} state
349367
* @param {string} name
350368
* @param {import('estree').Expression | null} [initial]
351369
* @returns
352370
*/
353-
export function get_prop_source(state, name, initial) {
371+
export function get_prop_source(binding, state, name, initial) {
354372
/** @type {import('estree').Expression[]} */
355373
const args = [b.id('$$props'), b.literal(name)];
356374

@@ -364,6 +382,13 @@ export function get_prop_source(state, name, initial) {
364382
flags |= PROPS_IS_RUNES;
365383
}
366384

385+
if (
386+
state.analysis.accessors ||
387+
(state.analysis.immutable ? binding.reassigned : binding.mutated)
388+
) {
389+
flags |= PROPS_IS_UPDATED;
390+
}
391+
367392
/** @type {import('estree').Expression | undefined} */
368393
let arg;
369394

@@ -382,7 +407,7 @@ export function get_prop_source(state, name, initial) {
382407
arg = b.thunk(initial);
383408
}
384409

385-
flags |= PROPS_CALL_DEFAULT_VALUE;
410+
flags |= PROPS_IS_LAZY_INITIAL;
386411
}
387412
}
388413

@@ -391,7 +416,7 @@ export function get_prop_source(state, name, initial) {
391416
if (arg) args.push(arg);
392417
}
393418

394-
return b.call('$.prop_source', ...args);
419+
return b.call('$.prop', ...args);
395420
}
396421

397422
/**

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

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ export const global_visitors = {
4545
const binding = state.scope.get(argument.name);
4646
const is_store = binding?.kind === 'store_sub';
4747
const name = is_store ? argument.name.slice(1) : argument.name;
48+
4849
// use runtime functions for smaller output
4950
if (
5051
binding?.kind === 'state' ||
@@ -53,18 +54,28 @@ export const global_visitors = {
5354
binding?.kind === 'prop' ||
5455
is_store
5556
) {
56-
let fn = node.operator === '++' ? '$.increment' : '$.decrement';
57+
/** @type {import('estree').Expression[]} */
58+
const args = [];
59+
60+
let fn = '$.update';
5761
if (node.prefix) fn += '_pre';
5862

5963
if (is_store) {
6064
fn += '_store';
61-
return b.call(fn, serialize_get_binding(b.id(name), state), b.call('$' + name));
65+
args.push(serialize_get_binding(b.id(name), state), b.call('$' + name));
6266
} else {
63-
return b.call(fn, b.id(name));
67+
if (binding.kind === 'prop') fn += '_prop';
68+
args.push(b.id(name));
6469
}
65-
} else {
66-
return next();
70+
71+
if (node.operator === '--') {
72+
args.push(b.literal(-1));
73+
}
74+
75+
return b.call(fn, ...args);
6776
}
77+
78+
return next();
6879
} else if (
6980
argument.type === 'MemberExpression' &&
7081
argument.object.type === 'ThisExpression' &&

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

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ export const javascript_visitors_legacy = {
5555
b.declarator(
5656
path.node,
5757
binding.kind === 'prop'
58-
? get_prop_source(state, binding.prop_alias ?? name, value)
58+
? get_prop_source(binding, state, binding.prop_alias ?? name, value)
5959
: value
6060
)
6161
);
@@ -76,6 +76,7 @@ export const javascript_visitors_legacy = {
7676
b.declarator(
7777
declarator.id,
7878
get_prop_source(
79+
binding,
7980
state,
8081
binding.prop_alias ?? declarator.id.name,
8182
declarator.init &&
@@ -107,8 +108,11 @@ export const javascript_visitors_legacy = {
107108
};
108109
},
109110
LabeledStatement(node, context) {
110-
if (context.path.length > 1) return;
111-
if (node.label.name !== '$') return;
111+
if (context.path.length > 1 || node.label.name !== '$') {
112+
context.next();
113+
return;
114+
}
115+
112116
const state = context.state;
113117
// To recreate Svelte 4 behaviour, we track the dependencies
114118
// the compiler can 'see', but we untrack the effect itself

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ export const javascript_visitors_runes = {
185185
const binding = /** @type {import('#compiler').Binding} */ (state.scope.get(id.name));
186186

187187
if (binding.reassigned || state.analysis.accessors || initial) {
188-
declarations.push(b.declarator(id, get_prop_source(state, name, initial)));
188+
declarations.push(b.declarator(id, get_prop_source(binding, state, name, initial)));
189189
}
190190
} else {
191191
// RestElement

packages/svelte/src/constants.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ export const EACH_IS_IMMUTABLE = 1 << 6;
77

88
export const PROPS_IS_IMMUTABLE = 1;
99
export const PROPS_IS_RUNES = 1 << 1;
10-
export const PROPS_CALL_DEFAULT_VALUE = 1 << 2;
10+
export const PROPS_IS_UPDATED = 1 << 2;
11+
export const PROPS_IS_LAZY_INITIAL = 1 << 3;
1112

1213
/** List of Element events that will be delegated */
1314
export const DelegatedEvents = [

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import {
33
effect_active,
44
get,
55
set,
6-
increment,
6+
update,
77
source,
88
updating_derived,
99
UNINITIALIZED,
@@ -143,7 +143,7 @@ const handler = {
143143
const s = metadata.s.get(prop);
144144
if (s !== undefined) set(s, UNINITIALIZED);
145145

146-
if (prop in target) increment(metadata.v);
146+
if (prop in target) update(metadata.v);
147147

148148
return delete target[prop];
149149
},
@@ -224,7 +224,7 @@ const handler = {
224224
}
225225
}
226226
if (not_has) {
227-
increment(metadata.v);
227+
update(metadata.v);
228228
}
229229
// @ts-ignore
230230
target[prop] = value;

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2658,7 +2658,7 @@ export function createRoot(component, options) {
26582658
/** @param {any} value */
26592659
set(value) {
26602660
// @ts-expect-error TS doesn't know key exists on accessor
2661-
accessors[key] = value;
2661+
flushSync(() => (accessors[key] = value));
26622662
},
26632663
enumerable: true
26642664
});

0 commit comments

Comments
 (0)