Skip to content

Commit 2235a6a

Browse files
committed
feat: raising a warning if the code contains both old and new event handling syntaxes
1 parent 37d7f91 commit 2235a6a

File tree

5 files changed

+50
-3
lines changed

5 files changed

+50
-3
lines changed

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,8 @@ export function analyze_component(root, source, options) {
374374
uses_slots: false,
375375
uses_component_bindings: false,
376376
uses_render_tags: false,
377+
event_directive_node: null,
378+
uses_event_attributes: false,
377379
custom_element: options.customElementOptions ?? options.customElement,
378380
inject_styles: options.css === 'injected' || options.customElement,
379381
accessors: options.customElement
@@ -1126,6 +1128,8 @@ const common_visitors = {
11261128
});
11271129

11281130
if (is_event_attribute(node)) {
1131+
context.state.analysis.uses_event_attributes = true;
1132+
11291133
const expression = node.value[0].expression;
11301134

11311135
const delegated_event = get_delegated_event(node.name.slice(2), expression, context);
@@ -1243,6 +1247,11 @@ const common_visitors = {
12431247

12441248
context.next();
12451249
},
1250+
OnDirective(node, context) {
1251+
context.state.analysis.event_directive_node = node;
1252+
1253+
context.next();
1254+
},
12461255
BindDirective(node, context) {
12471256
let i = context.path.length;
12481257
while (i--) {

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

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import * as e from '../../errors.js';
77
import {
88
extract_identifiers,
99
get_parent,
10+
is_event_attribute,
1011
is_expression_attribute,
1112
is_text_attribute,
1213
object,
@@ -102,6 +103,17 @@ function validate_element(node, context) {
102103

103104
for (const attribute of node.attributes) {
104105
if (attribute.type === 'Attribute') {
106+
const parent_type = attribute.parent?.type;
107+
108+
// Don't warn on component events; these might not be under the author's control so the warning would be unactionable
109+
if (
110+
(parent_type === 'RegularElement' || parent_type === 'SvelteElement') &&
111+
is_event_attribute(attribute) &&
112+
context.state.analysis.event_directive_node
113+
) {
114+
w.mixing_old_and_new_event_handler_syntaxes(node, node.name);
115+
}
116+
105117
const is_expression = is_expression_attribute(attribute);
106118

107119
if (context.state.analysis.runes && is_expression) {
@@ -1181,10 +1193,13 @@ export const validation_runes = merge(validation, a11y_validators, {
11811193
w.deprecated_slot_element(node);
11821194
}
11831195
},
1184-
OnDirective(node, { path }) {
1196+
OnDirective(node, { state, path }) {
11851197
const parent_type = path.at(-1)?.type;
11861198
// Don't warn on component events; these might not be under the author's control so the warning would be unactionable
11871199
if (parent_type === 'RegularElement' || parent_type === 'SvelteElement') {
1200+
if (state.analysis.uses_event_attributes) {
1201+
w.mixing_old_and_new_event_handler_syntaxes(node, node.name);
1202+
}
11881203
w.deprecated_event_handler(node, node.name);
11891204
}
11901205
},

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import type {
99
SvelteOptions,
1010
Warning
1111
} from '#compiler';
12-
import type { Identifier, LabeledStatement, Program } from 'estree';
12+
import type { BaseNode, Identifier, LabeledStatement, Program } from 'estree';
1313
import type { Scope, ScopeRoot } from './scope.js';
1414

1515
export interface Js {
@@ -57,6 +57,8 @@ export interface ComponentAnalysis extends Analysis {
5757
uses_slots: boolean;
5858
uses_component_bindings: boolean;
5959
uses_render_tags: boolean;
60+
event_directive_node: BaseNode | null;
61+
uses_event_attributes: boolean;
6062
custom_element: boolean | SvelteOptions['customElement'];
6163
/** If `true`, should append styles through JavaScript */
6264
inject_styles: boolean;

packages/svelte/src/compiler/warnings.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -535,6 +535,15 @@ export function deprecated_event_handler(node, name) {
535535
w(node, "deprecated_event_handler", `Using on:${name} to listen to the ${name} event is is deprecated. Use the event attribute on${name} instead.`);
536536
}
537537

538+
/**
539+
* Mixing old (on:%name%) and new syntaxes for event handling is not recommended. Use only the on%name% syntax.
540+
* @param {null | NodeLike} node
541+
* @param {string} name
542+
*/
543+
export function mixing_old_and_new_event_handler_syntaxes(node, name) {
544+
w(node, "mixing-events-handling-syntax", `Mixing old (on:${name}) and new syntaxes for event handling is not recommended. Use only the on${name} syntax.`);
545+
}
546+
538547
/**
539548
* Self-closing HTML tags for non-void elements are ambiguous — use <%name% ...></%name%> rather than <%name% ... />
540549
* @param {null | NodeLike} node
@@ -665,4 +674,4 @@ export function static_state_reference(node) {
665674
*/
666675
export function invalid_rest_eachblock_binding(node, name) {
667676
w(node, "invalid_rest_eachblock_binding", `The rest operator (...) will create a new object and binding '${name}' with the original object will not work`);
668-
}
677+
}

packages/svelte/tests/validator/samples/runes-legacy-syntax-warnings/warnings.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,18 @@
2323
"line": 12
2424
}
2525
},
26+
{
27+
"code": "mixing-events-handling-syntax",
28+
"end": {
29+
"column": 22,
30+
"line": 13
31+
},
32+
"message": "Mixing old (on:click) and new syntaxes for event handling is not recommended. Use only the onclick syntax.",
33+
"start": {
34+
"column": 8,
35+
"line": 13
36+
}
37+
},
2638
{
2739
"code": "deprecated_event_handler",
2840
"end": {

0 commit comments

Comments
 (0)