From 686d5c57b15172cb09577a73f2463fb12abb02e8 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 5 Dec 2023 21:11:49 -0500 Subject: [PATCH] fix some spread attribute stuff --- .../compiler/phases/1-parse/state/element.js | 1 + .../src/compiler/phases/2-analyze/index.js | 5 ++++- .../3-transform/client/visitors/template.js | 17 +++++++++++++++-- .../svelte/src/compiler/types/template.d.ts | 1 + packages/svelte/src/internal/client/render.js | 2 +- 5 files changed, 22 insertions(+), 4 deletions(-) diff --git a/packages/svelte/src/compiler/phases/1-parse/state/element.js b/packages/svelte/src/compiler/phases/1-parse/state/element.js index 019e29c944..6b38da217e 100644 --- a/packages/svelte/src/compiler/phases/1-parse/state/element.js +++ b/packages/svelte/src/compiler/phases/1-parse/state/element.js @@ -456,6 +456,7 @@ function read_attribute(parser) { expression, parent: null, metadata: { + contains_call_expression: false, dynamic: false } }; diff --git a/packages/svelte/src/compiler/phases/2-analyze/index.js b/packages/svelte/src/compiler/phases/2-analyze/index.js index 1be80cc169..be66f41d27 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/index.js +++ b/packages/svelte/src/compiler/phases/2-analyze/index.js @@ -906,7 +906,10 @@ const common_visitors = { } }, CallExpression(node, context) { - if (context.state.expression?.type === 'ExpressionTag' && !is_known_safe_call(node, context)) { + if ( + context.state.expression?.type === 'ExpressionTag' || + (context.state.expression?.type === 'SpreadAttribute' && !is_known_safe_call(node, context)) + ) { context.state.expression.metadata.contains_call_expression = true; } diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js index d621d63492..de95db2d27 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js @@ -756,7 +756,20 @@ function serialize_inline_component(node, component_name, context) { } events[attribute.name].push(handler); } else if (attribute.type === 'SpreadAttribute') { - props_and_spreads.push(/** @type {import('estree').Expression} */ (context.visit(attribute))); + const expression = /** @type {import('estree').Expression} */ (context.visit(attribute)); + if (attribute.metadata.dynamic) { + let value = expression; + + if (attribute.metadata.contains_call_expression) { + const id = b.id(context.state.scope.generate('spread_element')); + context.state.init.push(b.var(id, b.call('$.derived', b.thunk(value)))); + value = b.call('$.get', id); + } + + props_and_spreads.push(b.thunk(value)); + } else { + props_and_spreads.push(expression); + } } else if (attribute.type === 'Attribute') { if (attribute.name.startsWith('--')) { custom_css_props.push( @@ -895,7 +908,7 @@ function serialize_inline_component(node, component_name, context) { ? b.object(/** @type {import('estree').Property[]} */ (props_and_spreads[0]) || []) : b.call( '$.spread_props', - ...props_and_spreads.map((p) => (Array.isArray(p) ? b.object(p) : b.thunk(p))) + ...props_and_spreads.map((p) => (Array.isArray(p) ? b.object(p) : p)) ); /** @param {import('estree').Identifier} node_id */ let fn = (node_id) => diff --git a/packages/svelte/src/compiler/types/template.d.ts b/packages/svelte/src/compiler/types/template.d.ts index 6343b7ae32..4646dc6d32 100644 --- a/packages/svelte/src/compiler/types/template.d.ts +++ b/packages/svelte/src/compiler/types/template.d.ts @@ -434,6 +434,7 @@ export interface SpreadAttribute extends BaseNode { type: 'SpreadAttribute'; expression: Expression; metadata: { + contains_call_expression: boolean; dynamic: boolean; }; } diff --git a/packages/svelte/src/internal/client/render.js b/packages/svelte/src/internal/client/render.js index b83c430449..b8f8069f0a 100644 --- a/packages/svelte/src/internal/client/render.js +++ b/packages/svelte/src/internal/client/render.js @@ -2580,7 +2580,7 @@ const spread_props_handler = { while (i--) { let p = target.props[i]; if (typeof p === 'function') p = p(); - if (key in p) return p[key]; + if (typeof p === 'object' && p !== null && key in p) return p[key]; } }, getOwnPropertyDescriptor() {