Skip to content

Commit fc9776b

Browse files
chore: use proxies instead of signals for spread/rest props (#9801)
* use proxies instead of signals for spread/rest * fix some spread attribute stuff * remove is_signal calls * simplify some more * more * remove some unnecessary unwrapping * another * simplify * simplify * simplify * remove another MaybeSignal * more * remove more unwraps * code-golf, docs --------- Co-authored-by: Rich Harris <[email protected]> Co-authored-by: Simon Holthausen <[email protected]>
1 parent 2ae13df commit fc9776b

File tree

11 files changed

+186
-165
lines changed

11 files changed

+186
-165
lines changed

packages/svelte/src/compiler/phases/1-parse/state/element.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -456,6 +456,7 @@ function read_attribute(parser) {
456456
expression,
457457
parent: null,
458458
metadata: {
459+
contains_call_expression: false,
459460
dynamic: false
460461
}
461462
};

packages/svelte/src/compiler/phases/2-analyze/index.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -906,7 +906,10 @@ const common_visitors = {
906906
}
907907
},
908908
CallExpression(node, context) {
909-
if (context.state.expression?.type === 'ExpressionTag' && !is_known_safe_call(node, context)) {
909+
if (
910+
context.state.expression?.type === 'ExpressionTag' ||
911+
(context.state.expression?.type === 'SpreadAttribute' && !is_known_safe_call(node, context))
912+
) {
910913
context.state.expression.metadata.contains_call_expression = true;
911914
}
912915

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ export function serialize_get_binding(node, state) {
6767

6868
if (binding.kind === 'prop' && binding.node.name === '$$props') {
6969
// Special case for $$props which only exists in the old world
70-
return b.call('$.unwrap', node);
70+
return node;
7171
}
7272

7373
if (
@@ -88,7 +88,6 @@ export function serialize_get_binding(node, state) {
8888
(!state.analysis.immutable || state.analysis.accessors || binding.reassigned)) ||
8989
binding.kind === 'derived' ||
9090
binding.kind === 'prop' ||
91-
binding.kind === 'rest_prop' ||
9291
binding.kind === 'legacy_reactive'
9392
) {
9493
return b.call('$.get', node);

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ export const global_visitors = {
77
Identifier(node, { path, state }) {
88
if (is_reference(node, /** @type {import('estree').Node} */ (path.at(-1)))) {
99
if (node.name === '$$props') {
10-
return b.call('$.get', b.id('$$sanitized_props'));
10+
return b.id('$$sanitized_props');
1111
}
1212
return serialize_get_binding(node, state);
1313
}

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

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -756,7 +756,20 @@ function serialize_inline_component(node, component_name, context) {
756756
}
757757
events[attribute.name].push(handler);
758758
} else if (attribute.type === 'SpreadAttribute') {
759-
props_and_spreads.push(/** @type {import('estree').Expression} */ (context.visit(attribute)));
759+
const expression = /** @type {import('estree').Expression} */ (context.visit(attribute));
760+
if (attribute.metadata.dynamic) {
761+
let value = expression;
762+
763+
if (attribute.metadata.contains_call_expression) {
764+
const id = b.id(context.state.scope.generate('spread_element'));
765+
context.state.init.push(b.var(id, b.call('$.derived', b.thunk(value))));
766+
value = b.call('$.get', id);
767+
}
768+
769+
props_and_spreads.push(b.thunk(value));
770+
} else {
771+
props_and_spreads.push(expression);
772+
}
760773
} else if (attribute.type === 'Attribute') {
761774
if (attribute.name.startsWith('--')) {
762775
custom_css_props.push(
@@ -895,7 +908,7 @@ function serialize_inline_component(node, component_name, context) {
895908
? b.object(/** @type {import('estree').Property[]} */ (props_and_spreads[0]) || [])
896909
: b.call(
897910
'$.spread_props',
898-
b.thunk(b.array(props_and_spreads.map((p) => (Array.isArray(p) ? b.object(p) : p))))
911+
...props_and_spreads.map((p) => (Array.isArray(p) ? b.object(p) : p))
899912
);
900913
/** @param {import('estree').Identifier} node_id */
901914
let fn = (node_id) =>
@@ -2764,8 +2777,8 @@ export const template_visitors = {
27642777
}
27652778
},
27662779
LetDirective(node, { state }) {
2767-
// let:x --> const x = $.derived(() => $.unwrap($$slotProps).x);
2768-
// let:x={{y, z}} --> const derived_x = $.derived(() => { const { y, z } = $.unwrap($$slotProps).x; return { y, z }));
2780+
// let:x --> const x = $.derived(() => $$slotProps.x);
2781+
// let:x={{y, z}} --> const derived_x = $.derived(() => { const { y, z } = $$slotProps.x; return { y, z }));
27692782
if (node.expression && node.expression.type !== 'Identifier') {
27702783
const name = state.scope.generate(node.name);
27712784
const bindings = state.scope.get_bindings(node);
@@ -2787,7 +2800,7 @@ export const template_visitors = {
27872800
b.object_pattern(node.expression.properties)
27882801
: // @ts-expect-error types don't match, but it can't contain spread elements and the structure is otherwise fine
27892802
b.array_pattern(node.expression.elements),
2790-
b.member(b.call('$.unwrap', b.id('$$slotProps')), b.id(node.name))
2803+
b.member(b.id('$$slotProps'), b.id(node.name))
27912804
),
27922805
b.return(b.object(bindings.map((binding) => b.init(binding.node.name, binding.node))))
27932806
])
@@ -2798,10 +2811,7 @@ export const template_visitors = {
27982811
const name = node.expression === null ? node.name : node.expression.name;
27992812
return b.const(
28002813
name,
2801-
b.call(
2802-
'$.derived',
2803-
b.thunk(b.member(b.call('$.unwrap', b.id('$$slotProps')), b.id(node.name)))
2804-
)
2814+
b.call('$.derived', b.thunk(b.member(b.id('$$slotProps'), b.id(node.name))))
28052815
);
28062816
}
28072817
},
@@ -2854,7 +2864,9 @@ export const template_visitors = {
28542864

28552865
for (const attribute of node.attributes) {
28562866
if (attribute.type === 'SpreadAttribute') {
2857-
spreads.push(/** @type {import('estree').Expression} */ (context.visit(attribute)));
2867+
spreads.push(
2868+
b.thunk(/** @type {import('estree').Expression} */ (context.visit(attribute)))
2869+
);
28582870
} else if (attribute.type === 'Attribute') {
28592871
const [, value] = serialize_attribute_value(attribute.value, context);
28602872
if (attribute.name === 'name') {
@@ -2873,7 +2885,7 @@ export const template_visitors = {
28732885
const props_expression =
28742886
spreads.length === 0
28752887
? b.object(props)
2876-
: b.call('$.spread_props', b.thunk(b.array([b.object(props), ...spreads])));
2888+
: b.call('$.spread_props', b.object(props), ...spreads);
28772889
const fallback =
28782890
node.fragment.nodes.length === 0
28792891
? b.literal(null)
@@ -2883,8 +2895,8 @@ export const template_visitors = {
28832895
);
28842896

28852897
const expression = is_default
2886-
? b.member(b.call('$.unwrap', b.id('$$props')), b.id('children'))
2887-
: b.member(b.member(b.call('$.unwrap', b.id('$$props')), b.id('$$slots')), name, true, true);
2898+
? b.member(b.id('$$props'), b.id('children'))
2899+
: b.member(b.member(b.id('$$props'), b.id('$$slots')), name, true, true);
28882900

28892901
const slot = b.call('$.slot', context.state.node, expression, props_expression, fallback);
28902902
context.state.init.push(b.stmt(slot));

packages/svelte/src/compiler/types/template.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -434,6 +434,7 @@ export interface SpreadAttribute extends BaseNode {
434434
type: 'SpreadAttribute';
435435
expression: Expression;
436436
metadata: {
437+
contains_call_expression: boolean;
437438
dynamic: boolean;
438439
};
439440
}

0 commit comments

Comments
 (0)