Skip to content

Commit 2f0b3e5

Browse files
committed
feat: raising a warning if the code contains both old and new event handling syntaxes
1 parent ba8b896 commit 2f0b3e5

File tree

5 files changed

+47
-2
lines changed

5 files changed

+47
-2
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
@@ -384,6 +384,8 @@ export function analyze_component(root, source, options) {
384384
uses_slots: false,
385385
uses_component_bindings: false,
386386
uses_render_tags: false,
387+
event_directive_node: null,
388+
uses_event_attributes: false,
387389
custom_element: options.customElementOptions ?? options.customElement,
388390
inject_styles: options.css === 'injected' || options.customElement,
389391
accessors: options.customElement
@@ -1070,6 +1072,8 @@ const common_visitors = {
10701072
});
10711073

10721074
if (is_event_attribute(node)) {
1075+
context.state.analysis.uses_event_attributes = true;
1076+
10731077
const expression = node.value[0].expression;
10741078

10751079
const delegated_event = get_delegated_event(node.name.slice(2), expression, context);
@@ -1187,6 +1191,11 @@ const common_visitors = {
11871191

11881192
context.next();
11891193
},
1194+
OnDirective(node, context) {
1195+
context.state.analysis.event_directive_node = node;
1196+
1197+
context.next();
1198+
},
11901199
BindDirective(node, context) {
11911200
let i = context.path.length;
11921201
while (i--) {

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

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { error } 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,
@@ -98,6 +99,22 @@ function validate_element(node, context) {
9899

99100
for (const attribute of node.attributes) {
100101
if (attribute.type === 'Attribute') {
102+
const parent_type = attribute.parent?.type;
103+
104+
// Don't warn on component events; these might not be under the author's control so the warning would be unactionable
105+
if (
106+
(parent_type === 'RegularElement' || parent_type === 'SvelteElement') &&
107+
is_event_attribute(attribute) &&
108+
context.state.analysis.event_directive_node
109+
) {
110+
warn(
111+
context.state.analysis.warnings,
112+
context.state.analysis.event_directive_node,
113+
context.path,
114+
'mixing-events-handling-syntax'
115+
);
116+
}
117+
101118
const is_expression = is_expression_attribute(attribute);
102119

103120
if (context.state.analysis.runes && is_expression) {
@@ -1214,6 +1231,9 @@ export const validation_runes = merge(validation, a11y_validators, {
12141231
const parent_type = path.at(-1)?.type;
12151232
// Don't warn on component events; these might not be under the author's control so the warning would be unactionable
12161233
if (parent_type === 'RegularElement' || parent_type === 'SvelteElement') {
1234+
if (state.analysis.uses_event_attributes) {
1235+
warn(state.analysis.warnings, node, path, 'mixing-events-handling-syntax');
1236+
}
12171237
warn(state.analysis.warnings, node, path, 'deprecated-event-handler', node.name);
12181238
}
12191239
},

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

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

1414
export interface Js {
@@ -63,6 +63,8 @@ export interface ComponentAnalysis extends Analysis {
6363
uses_slots: boolean;
6464
uses_component_bindings: boolean;
6565
uses_render_tags: boolean;
66+
event_directive_node: BaseNode | null;
67+
uses_event_attributes: boolean;
6668
custom_element: boolean | SvelteOptions['customElement'];
6769
/** If `true`, should append styles through JavaScript */
6870
inject_styles: boolean;

packages/svelte/src/compiler/warnings.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,9 @@ const legacy = {
238238
`Using <slot> to render parent content is deprecated. Use {@render ...} tags instead.`,
239239
/** @param {string} name */
240240
'deprecated-event-handler': (name) =>
241-
`Using on:${name} to listen to the ${name} event is is deprecated. Use the event attribute on${name} instead.`
241+
`Using on:${name} to listen to the ${name} event is is deprecated. Use the event attribute on${name} instead.`,
242+
'mixing-events-handling-syntax': () =>
243+
`Do not mix old and new event handling syntaxes in the same component. Use only standard event attributes.`
242244
};
243245

244246
const block = {

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": "Do not mix old and new event handling syntaxes in the same component. Use only standard event attributes.",
33+
"start": {
34+
"column": 8,
35+
"line": 13
36+
}
37+
},
2638
{
2739
"code": "deprecated-event-handler",
2840
"end": {

0 commit comments

Comments
 (0)