Skip to content

Commit 68071f7

Browse files
fix: disallow mixing event-handling syntaxes (#11295)
Closes #11262 --------- Co-authored-by: Simon Holthausen <[email protected]>
1 parent bda32ed commit 68071f7

File tree

20 files changed

+132
-27
lines changed

20 files changed

+132
-27
lines changed

.changeset/cool-poems-watch.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+
fix: disallow mixing on:click and onclick syntax

packages/svelte/messages/compile-errors/template.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,10 @@
172172

173173
> `let:` directive at invalid position
174174
175+
## mixed_event_handler_syntaxes
176+
177+
> Mixing old (on:%name%) and new syntaxes for event handling is not allowed. Use only the on%name% syntax.
178+
175179
## node_invalid_placement
176180

177181
> %thing% is invalid inside <%parent%>

packages/svelte/src/compiler/errors.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -918,6 +918,16 @@ export function let_directive_invalid_placement(node) {
918918
e(node, "let_directive_invalid_placement", "`let:` directive at invalid position");
919919
}
920920

921+
/**
922+
* Mixing old (on:%name%) and new syntaxes for event handling is not allowed. Use only the on%name% syntax.
923+
* @param {null | number | NodeLike} node
924+
* @param {string} name
925+
* @returns {never}
926+
*/
927+
export function mixed_event_handler_syntaxes(node, name) {
928+
e(node, "mixed_event_handler_syntaxes", `Mixing old (on:${name}) and new syntaxes for event handling is not allowed. Use only the on${name} syntax.`);
929+
}
930+
921931
/**
922932
* %thing% is invalid inside <%parent%>
923933
* @param {null | number | NodeLike} node

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

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,8 @@ export function analyze_component(root, source, options) {
372372
uses_render_tags: false,
373373
needs_context: false,
374374
needs_props: false,
375+
event_directive_node: null,
376+
uses_event_attributes: false,
375377
custom_element: options.customElementOptions ?? options.customElement,
376378
inject_styles: options.css === 'injected' || options.customElement,
377379
accessors: options.customElement
@@ -494,6 +496,13 @@ export function analyze_component(root, source, options) {
494496
analysis.reactive_statements = order_reactive_statements(analysis.reactive_statements);
495497
}
496498

499+
if (analysis.event_directive_node && analysis.uses_event_attributes) {
500+
e.mixed_event_handler_syntaxes(
501+
analysis.event_directive_node,
502+
analysis.event_directive_node.name
503+
);
504+
}
505+
497506
if (analysis.uses_render_tags && (analysis.uses_slots || analysis.slot_names.size > 0)) {
498507
e.slot_snippet_conflict(analysis.slot_names.values().next().value);
499508
}
@@ -1153,6 +1162,11 @@ const common_visitors = {
11531162
});
11541163

11551164
if (is_event_attribute(node)) {
1165+
const parent = context.path.at(-1);
1166+
if (parent?.type === 'RegularElement' || parent?.type === 'SvelteElement') {
1167+
context.state.analysis.uses_event_attributes = true;
1168+
}
1169+
11561170
const expression = node.value[0].expression;
11571171

11581172
const delegated_event = get_delegated_event(node.name.slice(2), expression, context);
@@ -1286,6 +1300,13 @@ const common_visitors = {
12861300

12871301
context.next();
12881302
},
1303+
OnDirective(node, { state, path, next }) {
1304+
const parent = path.at(-1);
1305+
if (parent?.type === 'SvelteElement' || parent?.type === 'RegularElement') {
1306+
state.analysis.event_directive_node ??= node;
1307+
}
1308+
next();
1309+
},
12891310
BindDirective(node, context) {
12901311
let i = context.path.length;
12911312
while (i--) {

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import * as e from '../../errors.js';
88
import {
99
extract_identifiers,
1010
get_parent,
11+
is_event_attribute,
1112
is_expression_attribute,
1213
is_text_attribute,
1314
object,

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type {
22
Binding,
33
Css,
44
Fragment,
5+
OnDirective,
56
RegularElement,
67
SlotElement,
78
SvelteElement,
@@ -59,6 +60,10 @@ export interface ComponentAnalysis extends Analysis {
5960
uses_render_tags: boolean;
6061
needs_context: boolean;
6162
needs_props: boolean;
63+
/** Set to the first event directive (on:x) found on a DOM element in the code */
64+
event_directive_node: OnDirective | null;
65+
/** true if uses event attributes (onclick) on a DOM element */
66+
uses_event_attributes: boolean;
6267
custom_element: boolean | SvelteOptions['customElement'];
6368
/** If `true`, should append styles through JavaScript */
6469
inject_styles: boolean;

packages/svelte/tests/runtime-runes/samples/event-attribute-delegation-2/main.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<div on:click={(e) => { console.log('clicked div') }}>
1+
<div onclick={(e) => { console.log('clicked div') }}>
22
<button onclick={(e) => { console.log('clicked button'); e.stopPropagation() }}>
33
Button
44
</button>
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<script>
2+
const { children, ...props } = $props();
3+
</script>
4+
5+
<div {...props} on:click>
6+
{@render children()}
7+
</div>
Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
<script>
2+
import Component from "./Component.svelte";
23
import Sub from "./sub.svelte";
34
</script>
45

56
<svelte:window onclick="{() => console.log('window main')}" />
67
<svelte:document onclick="{() => console.log('document main')}" />
78

8-
<div on:click={() => console.log('div main 1')} on:click={() => console.log('div main 2')}>
9+
<Component on:click={() => console.log('div main 1')} on:click={() => console.log('div main 2')}>
910
<button onclick={() => console.log('button main')}>main</button>
10-
</div>
11+
</Component>
1112

1213
<Sub />
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<script>
2+
const { children, ...props } = $props();
3+
</script>
4+
5+
<button {...props} on:click>
6+
{@render children()}
7+
</button>

0 commit comments

Comments
 (0)