fix: raising an error when mixing both old and new event-handling syntaxes

pull/11295/head
Caique Torres 2 years ago
parent f1986da755
commit 3f5616c141

@ -172,6 +172,10 @@
> `let:` directive at invalid position > `let:` directive at invalid position
## mixed_event_handler_syntaxes
> Mixing old (on:%name%) and new syntaxes for event handling is not allowed. Use only the on%name% syntax.
## node_invalid_placement ## node_invalid_placement
> %thing% is invalid inside <%parent%> > %thing% is invalid inside <%parent%>

@ -918,6 +918,16 @@ export function let_directive_invalid_placement(node) {
e(node, "let_directive_invalid_placement", "`let:` directive at invalid position"); e(node, "let_directive_invalid_placement", "`let:` directive at invalid position");
} }
/**
* Mixing old (on:%name%) and new syntaxes for event handling is not allowed. Use only the on%name% syntax.
* @param {null | number | NodeLike} node
* @param {string} name
* @returns {never}
*/
export function mixed_event_handler_syntaxes(node, name) {
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.`);
}
/** /**
* %thing% is invalid inside <%parent%> * %thing% is invalid inside <%parent%>
* @param {null | number | NodeLike} node * @param {null | number | NodeLike} node

@ -372,6 +372,8 @@ export function analyze_component(root, source, options) {
uses_render_tags: false, uses_render_tags: false,
needs_context: false, needs_context: false,
needs_props: false, needs_props: false,
event_directive_node: null,
uses_event_attributes: false,
custom_element: options.customElementOptions ?? options.customElement, custom_element: options.customElementOptions ?? options.customElement,
inject_styles: options.css === 'injected' || options.customElement, inject_styles: options.css === 'injected' || options.customElement,
accessors: options.customElement accessors: options.customElement
@ -1153,6 +1155,8 @@ const common_visitors = {
}); });
if (is_event_attribute(node)) { if (is_event_attribute(node)) {
context.state.analysis.uses_event_attributes = true;
const expression = node.value[0].expression; const expression = node.value[0].expression;
const delegated_event = get_delegated_event(node.name.slice(2), expression, context); const delegated_event = get_delegated_event(node.name.slice(2), expression, context);
@ -1286,6 +1290,13 @@ const common_visitors = {
context.next(); context.next();
}, },
OnDirective(node, { state, path, next }) {
const parent = path.at(-1);
if (parent?.type === 'SvelteElement' || parent?.type === 'RegularElement') {
state.analysis.event_directive_node ??= node;
}
next();
},
BindDirective(node, context) { BindDirective(node, context) {
let i = context.path.length; let i = context.path.length;
while (i--) { while (i--) {

@ -8,6 +8,7 @@ import * as e from '../../errors.js';
import { import {
extract_identifiers, extract_identifiers,
get_parent, get_parent,
is_event_attribute,
is_expression_attribute, is_expression_attribute,
is_text_attribute, is_text_attribute,
object, object,
@ -104,6 +105,18 @@ function validate_element(node, context) {
for (const attribute of node.attributes) { for (const attribute of node.attributes) {
if (attribute.type === 'Attribute') { if (attribute.type === 'Attribute') {
const parent_type = node.type;
// Don't warn on component events; these might not be under the author's control so the warning would be unactionable
if (
(parent_type === 'RegularElement' || parent_type === 'SvelteElement') &&
is_event_attribute(attribute) &&
context.state.analysis.event_directive_node
) {
const { event_directive_node } = context.state.analysis;
e.mixed_event_handler_syntaxes(event_directive_node, event_directive_node.name);
}
const is_expression = is_expression_attribute(attribute); const is_expression = is_expression_attribute(attribute);
if (context.state.analysis.runes && is_expression) { if (context.state.analysis.runes && is_expression) {
@ -1204,10 +1217,13 @@ export const validation_runes = merge(validation, a11y_validators, {
w.slot_element_deprecated(node); w.slot_element_deprecated(node);
} }
}, },
OnDirective(node, { path }) { OnDirective(node, { state, path }) {
const parent_type = path.at(-1)?.type; const parent_type = path.at(-1)?.type;
// Don't warn on component events; these might not be under the author's control so the warning would be unactionable // Don't warn on component events; these might not be under the author's control so the warning would be unactionable
if (parent_type === 'RegularElement' || parent_type === 'SvelteElement') { if (parent_type === 'RegularElement' || parent_type === 'SvelteElement') {
if (state.analysis.uses_event_attributes) {
e.mixed_event_handler_syntaxes(node, node.name);
}
w.event_directive_deprecated(node, node.name); w.event_directive_deprecated(node, node.name);
} }
}, },

@ -2,6 +2,7 @@ import type {
Binding, Binding,
Css, Css,
Fragment, Fragment,
OnDirective,
RegularElement, RegularElement,
SlotElement, SlotElement,
SvelteElement, SvelteElement,
@ -59,6 +60,8 @@ export interface ComponentAnalysis extends Analysis {
uses_render_tags: boolean; uses_render_tags: boolean;
needs_context: boolean; needs_context: boolean;
needs_props: boolean; needs_props: boolean;
event_directive_node: OnDirective | null;
uses_event_attributes: boolean;
custom_element: boolean | SvelteOptions['customElement']; custom_element: boolean | SvelteOptions['customElement'];
/** If `true`, should append styles through JavaScript */ /** If `true`, should append styles through JavaScript */
inject_styles: boolean; inject_styles: boolean;

@ -0,0 +1,14 @@
[
{
"code": "mixed_event_handler_syntaxes",
"message": "Mixing old (on:click) and new syntaxes for event handling is not allowed. Use only the onclick syntax.",
"start": {
"line": 11,
"column": 8
},
"end": {
"line": 11,
"column": 22
}
}
]

@ -0,0 +1,11 @@
<script>
let { foo } = $props();
</script>
<!-- ok -->
<button onclick={foo}>click me</button>
<Button on:click={foo}>click me</Button>
<Button on:click={foo}>click me</Button>
<!-- error -->
<button on:click={foo}>click me</button>
Loading…
Cancel
Save