diff --git a/.changeset/honest-phones-flash.md b/.changeset/honest-phones-flash.md new file mode 100644 index 0000000000..f4e55d33ec --- /dev/null +++ b/.changeset/honest-phones-flash.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: apply dynamic event fixes to OnDirective 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 161b5fbc61..478b5fc2ad 100644 --- a/packages/svelte/src/compiler/phases/1-parse/state/element.js +++ b/packages/svelte/src/compiler/phases/1-parse/state/element.js @@ -612,7 +612,11 @@ function read_attribute(parser) { type, name: directive_name, modifiers, - expression + expression, + metadata: { + dynamic: false, + contains_call_expression: false + } }; if (directive.type === 'ClassDirective') { diff --git a/packages/svelte/src/compiler/phases/2-analyze/index.js b/packages/svelte/src/compiler/phases/2-analyze/index.js index a064a457d0..fe1027473c 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/index.js +++ b/packages/svelte/src/compiler/phases/2-analyze/index.js @@ -1299,7 +1299,9 @@ const common_visitors = { CallExpression(node, context) { const { expression, render_tag } = context.state; if ( - (expression?.type === 'ExpressionTag' || expression?.type === 'SpreadAttribute') && + (expression?.type === 'ExpressionTag' || + expression?.type === 'SpreadAttribute' || + expression?.type === 'OnDirective') && !is_known_safe_call(node, context) ) { expression.metadata.contains_call_expression = true; @@ -1367,7 +1369,7 @@ const common_visitors = { if (parent?.type === 'SvelteElement' || parent?.type === 'RegularElement') { state.analysis.event_directive_node ??= node; } - next(); + next({ ...state, expression: node }); }, BindDirective(node, context) { let i = context.path.length; diff --git a/packages/svelte/src/compiler/phases/2-analyze/types.d.ts b/packages/svelte/src/compiler/phases/2-analyze/types.d.ts index 461be5df39..feacbdedcc 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/types.d.ts +++ b/packages/svelte/src/compiler/phases/2-analyze/types.d.ts @@ -3,6 +3,7 @@ import type { ComponentAnalysis, ReactiveStatement } from '../types.js'; import type { ClassDirective, ExpressionTag, + OnDirective, RenderTag, SpreadAttribute, SvelteNode, @@ -20,7 +21,7 @@ export interface AnalysisState { /** Which slots the current parent component has */ component_slots: Set; /** The current {expression}, if any */ - expression: ExpressionTag | ClassDirective | SpreadAttribute | null; + expression: ExpressionTag | ClassDirective | OnDirective | SpreadAttribute | null; /** The current {@render ...} tag, if any */ render_tag: null | RenderTag; private_derived_state: string[]; 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 0a4d6f7bc7..7953431e10 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 @@ -2838,7 +2838,7 @@ export const template_visitors = { context.next({ ...context.state, in_constructor: false }); }, OnDirective(node, context) { - serialize_event(node, null, context); + serialize_event(node, node.metadata, context); }, UseDirective(node, { state, next, visit }) { const params = [b.id('$$node')]; diff --git a/packages/svelte/src/compiler/types/template.d.ts b/packages/svelte/src/compiler/types/template.d.ts index 8812f2fd9c..ec35a08227 100644 --- a/packages/svelte/src/compiler/types/template.d.ts +++ b/packages/svelte/src/compiler/types/template.d.ts @@ -211,6 +211,10 @@ export interface OnDirective extends BaseNode { /** The 'y' in `on:x={y}` */ expression: null | Expression; modifiers: string[]; // TODO specify + metadata: { + contains_call_expression: boolean; + dynamic: boolean; + }; } export type DelegatedEvent = diff --git a/packages/svelte/tests/runtime-runes/samples/dynamic-element-event-handler-2/_config.js b/packages/svelte/tests/runtime-runes/samples/dynamic-element-event-handler-2/_config.js new file mode 100644 index 0000000000..e3cc5ecde3 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/dynamic-element-event-handler-2/_config.js @@ -0,0 +1,26 @@ +import { test } from '../../test'; + +export default test({ + html: '
', + + test({ assert, logs, target }) { + const [b1, b2, b3] = target.querySelectorAll('button'); + + b1?.click(); + b2?.click(); + b3?.click(); + b1?.click(); + b2?.click(); + + assert.deepEqual(logs, [ + 'creating "Hello" handler for Tama', + 'Hello Tama', + 'creating "Hello" handler for Pochi', + 'Hello Pochi', + 'creating "Bye" handler for Tama', + 'Bye Tama', + 'creating "Bye" handler for Pochi', + 'Bye Pochi' + ]); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/dynamic-element-event-handler-2/main.svelte b/packages/svelte/tests/runtime-runes/samples/dynamic-element-event-handler-2/main.svelte new file mode 100644 index 0000000000..d9ed2d2d7a --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/dynamic-element-event-handler-2/main.svelte @@ -0,0 +1,19 @@ + + + + + +
+ diff --git a/packages/svelte/types/index.d.ts b/packages/svelte/types/index.d.ts index 62705db8c6..c43ca874f1 100644 --- a/packages/svelte/types/index.d.ts +++ b/packages/svelte/types/index.d.ts @@ -1636,6 +1636,10 @@ declare module 'svelte/compiler' { /** The 'y' in `on:x={y}` */ expression: null | Expression; modifiers: string[]; // TODO specify + metadata: { + contains_call_expression: boolean; + dynamic: boolean; + }; } type DelegatedEvent =