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 b680634aa0..a1f231398b 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 @@ -714,7 +714,7 @@ function serialize_inline_component(node, component_name, context, anchor = cont lets.push(/** @type {ExpressionStatement} */ (context.visit(attribute))); } else if (attribute.type === 'OnDirective') { events[attribute.name] ||= []; - let handler = serialize_event_handler(attribute, context); + let handler = serialize_event_handler(attribute, null, context); if (attribute.modifiers.includes('once')) { handler = b.call('$.once', handler); } @@ -1122,9 +1122,10 @@ function serialize_render_stmt(update) { /** * Serializes the event handler function of the `on:` directive * @param {Pick} node + * @param {null | { contains_call_expression: boolean; dynamic: boolean; } | null} metadata * @param {import('../types.js').ComponentContext} context */ -function serialize_event_handler(node, { state, visit }) { +function serialize_event_handler(node, metadata, { state, visit }) { /** @type {Expression} */ let handler; @@ -1147,28 +1148,6 @@ function serialize_event_handler(node, { state, visit }) { ]) ); - const derived_dynamic_handler = () => { - const id = b.id(state.scope.generate('event_handler')); - - state.init.push( - b.var(id, b.call('$.derived', b.thunk(/** @type {Expression} */ (visit(handler))))) - ); - - return b.function( - null, - [b.rest(b.id('$$args'))], - b.block([ - b.return( - b.call( - b.member(b.call('$.get', id), b.id('apply'), false, true), - b.this, - b.id('$$args') - ) - ) - ]) - ); - }; - if (handler.type === 'Identifier' || handler.type === 'MemberExpression') { const id = object(handler); const binding = id === null ? null : state.scope.get(id.name); @@ -1187,8 +1166,33 @@ function serialize_event_handler(node, { state, visit }) { } else { handler = /** @type {Expression} */ (visit(handler)); } - } else if (handler.type === 'CallExpression') { - handler = derived_dynamic_handler(); + } else if ( + metadata?.contains_call_expression && + !( + (handler.type === 'ArrowFunctionExpression' || handler.type === 'FunctionExpression') && + handler.metadata.hoistable + ) + ) { + // Create a derived dynamic event handler + const id = b.id(state.scope.generate('event_handler')); + + state.init.push( + b.var(id, b.call('$.derived', b.thunk(/** @type {Expression} */ (visit(handler))))) + ); + + handler = b.function( + null, + [b.rest(b.id('$$args'))], + b.block([ + b.return( + b.call( + b.member(b.call('$.get', id), b.id('apply'), false, true), + b.this, + b.id('$$args') + ) + ) + ]) + ); } else if (handler.type === 'ConditionalExpression' || handler.type === 'LogicalExpression') { handler = dynamic_handler(); } else { @@ -1226,17 +1230,18 @@ function serialize_event_handler(node, { state, visit }) { /** * Serializes an event handler function of the `on:` directive or an attribute starting with `on` - * @param {{name: string; modifiers: string[]; expression: Expression | null; delegated?: import('#compiler').DelegatedEvent | null; }} node + * @param {{name: string;modifiers: string[];expression: Expression | null;delegated?: import('#compiler').DelegatedEvent | null;}} node + * @param {null | { contains_call_expression: boolean; dynamic: boolean; }} metadata * @param {import('../types.js').ComponentContext} context */ -function serialize_event(node, context) { +function serialize_event(node, metadata, context) { const state = context.state; /** @type {Statement} */ let statement; if (node.expression) { - let handler = serialize_event_handler(node, context); + let handler = serialize_event_handler(node, metadata, context); const event_name = node.name; const delegated = node.delegated; @@ -1305,7 +1310,12 @@ function serialize_event(node, context) { statement = b.stmt(b.call('$.event', ...args)); } else { statement = b.stmt( - b.call('$.event', b.literal(node.name), state.node, serialize_event_handler(node, context)) + b.call( + '$.event', + b.literal(node.name), + state.node, + serialize_event_handler(node, metadata, context) + ) ); } @@ -1343,6 +1353,7 @@ function serialize_event_attribute(node, context) { modifiers, delegated: node.metadata.delegated }, + !Array.isArray(node.value) && node.value?.type === 'ExpressionTag' ? node.value.metadata : null, context ); } @@ -2817,7 +2828,7 @@ export const template_visitors = { context.next({ ...context.state, in_constructor: false }); }, OnDirective(node, context) { - serialize_event(node, context); + serialize_event(node, null, context); }, UseDirective(node, { state, next, visit }) { const params = [b.id('$$node')];