From 68071f7c0658e72c2dc0b8cd0bb81a4bd39127f0 Mon Sep 17 00:00:00 2001 From: Caique Torres <56696506+caiquetorres@users.noreply.github.com> Date: Sun, 28 Apr 2024 17:43:07 -0300 Subject: [PATCH] fix: disallow mixing event-handling syntaxes (#11295) Closes #11262 --------- Co-authored-by: Simon Holthausen --- .changeset/cool-poems-watch.md | 5 +++++ .../messages/compile-errors/template.md | 4 ++++ packages/svelte/src/compiler/errors.js | 10 +++++++++ .../src/compiler/phases/2-analyze/index.js | 21 +++++++++++++++++++ .../compiler/phases/2-analyze/validation.js | 1 + .../svelte/src/compiler/phases/types.d.ts | 5 +++++ .../event-attribute-delegation-2/main.svelte | 2 +- .../Component.svelte | 7 +++++++ .../event-attribute-delegation-4/main.svelte | 5 +++-- .../Button.svelte | 7 +++++++ .../Component.svelte | 7 +++++++ .../event-attribute-delegation-5/main.svelte | 15 ++++++++----- .../Button.svelte | 7 +++++++ .../main.svelte | 17 ++++++++------- .../input.svelte | 4 ++-- .../warnings.json | 2 +- .../errors.json | 14 +++++++++++++ .../input.svelte | 11 ++++++++++ .../runes-legacy-syntax-warnings/input.svelte | 3 +-- .../warnings.json | 12 +++++------ 20 files changed, 132 insertions(+), 27 deletions(-) create mode 100644 .changeset/cool-poems-watch.md create mode 100644 packages/svelte/tests/runtime-runes/samples/event-attribute-delegation-4/Component.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/event-attribute-delegation-5/Button.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/event-attribute-delegation-5/Component.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/event-attribute-spread-collision/Button.svelte create mode 100644 packages/svelte/tests/validator/samples/runes-legacy-syntax-warnings-2/errors.json create mode 100644 packages/svelte/tests/validator/samples/runes-legacy-syntax-warnings-2/input.svelte diff --git a/.changeset/cool-poems-watch.md b/.changeset/cool-poems-watch.md new file mode 100644 index 000000000..34527ad16 --- /dev/null +++ b/.changeset/cool-poems-watch.md @@ -0,0 +1,5 @@ +--- +"svelte": patch +--- + +fix: disallow mixing on:click and onclick syntax diff --git a/packages/svelte/messages/compile-errors/template.md b/packages/svelte/messages/compile-errors/template.md index c1b8c5f3c..293d4845c 100644 --- a/packages/svelte/messages/compile-errors/template.md +++ b/packages/svelte/messages/compile-errors/template.md @@ -172,6 +172,10 @@ > `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 > %thing% is invalid inside <%parent%> diff --git a/packages/svelte/src/compiler/errors.js b/packages/svelte/src/compiler/errors.js index a00a2cd59..d2834d03d 100644 --- a/packages/svelte/src/compiler/errors.js +++ b/packages/svelte/src/compiler/errors.js @@ -918,6 +918,16 @@ export function let_directive_invalid_placement(node) { 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%> * @param {null | number | NodeLike} node diff --git a/packages/svelte/src/compiler/phases/2-analyze/index.js b/packages/svelte/src/compiler/phases/2-analyze/index.js index 2ed2288a8..8d3088806 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/index.js +++ b/packages/svelte/src/compiler/phases/2-analyze/index.js @@ -372,6 +372,8 @@ export function analyze_component(root, source, options) { uses_render_tags: false, needs_context: false, needs_props: false, + event_directive_node: null, + uses_event_attributes: false, custom_element: options.customElementOptions ?? options.customElement, inject_styles: options.css === 'injected' || options.customElement, accessors: options.customElement @@ -494,6 +496,13 @@ export function analyze_component(root, source, options) { analysis.reactive_statements = order_reactive_statements(analysis.reactive_statements); } + if (analysis.event_directive_node && analysis.uses_event_attributes) { + e.mixed_event_handler_syntaxes( + analysis.event_directive_node, + analysis.event_directive_node.name + ); + } + if (analysis.uses_render_tags && (analysis.uses_slots || analysis.slot_names.size > 0)) { e.slot_snippet_conflict(analysis.slot_names.values().next().value); } @@ -1153,6 +1162,11 @@ const common_visitors = { }); if (is_event_attribute(node)) { + const parent = context.path.at(-1); + if (parent?.type === 'RegularElement' || parent?.type === 'SvelteElement') { + context.state.analysis.uses_event_attributes = true; + } + const expression = node.value[0].expression; const delegated_event = get_delegated_event(node.name.slice(2), expression, context); @@ -1286,6 +1300,13 @@ const common_visitors = { 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) { let i = context.path.length; while (i--) { diff --git a/packages/svelte/src/compiler/phases/2-analyze/validation.js b/packages/svelte/src/compiler/phases/2-analyze/validation.js index 4f68b5f9e..ab793bf1a 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/validation.js +++ b/packages/svelte/src/compiler/phases/2-analyze/validation.js @@ -8,6 +8,7 @@ import * as e from '../../errors.js'; import { extract_identifiers, get_parent, + is_event_attribute, is_expression_attribute, is_text_attribute, object, diff --git a/packages/svelte/src/compiler/phases/types.d.ts b/packages/svelte/src/compiler/phases/types.d.ts index 3e892fefe..ae346068c 100644 --- a/packages/svelte/src/compiler/phases/types.d.ts +++ b/packages/svelte/src/compiler/phases/types.d.ts @@ -2,6 +2,7 @@ import type { Binding, Css, Fragment, + OnDirective, RegularElement, SlotElement, SvelteElement, @@ -59,6 +60,10 @@ export interface ComponentAnalysis extends Analysis { uses_render_tags: boolean; needs_context: boolean; needs_props: boolean; + /** Set to the first event directive (on:x) found on a DOM element in the code */ + event_directive_node: OnDirective | null; + /** true if uses event attributes (onclick) on a DOM element */ + uses_event_attributes: boolean; custom_element: boolean | SvelteOptions['customElement']; /** If `true`, should append styles through JavaScript */ inject_styles: boolean; diff --git a/packages/svelte/tests/runtime-runes/samples/event-attribute-delegation-2/main.svelte b/packages/svelte/tests/runtime-runes/samples/event-attribute-delegation-2/main.svelte index 2a1800553..3e803a628 100644 --- a/packages/svelte/tests/runtime-runes/samples/event-attribute-delegation-2/main.svelte +++ b/packages/svelte/tests/runtime-runes/samples/event-attribute-delegation-2/main.svelte @@ -1,4 +1,4 @@ -
{ console.log('clicked div') }}> +
{ console.log('clicked div') }}> diff --git a/packages/svelte/tests/runtime-runes/samples/event-attribute-delegation-4/Component.svelte b/packages/svelte/tests/runtime-runes/samples/event-attribute-delegation-4/Component.svelte new file mode 100644 index 000000000..d096bacaa --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/event-attribute-delegation-4/Component.svelte @@ -0,0 +1,7 @@ + + +
+ {@render children()} +
diff --git a/packages/svelte/tests/runtime-runes/samples/event-attribute-delegation-4/main.svelte b/packages/svelte/tests/runtime-runes/samples/event-attribute-delegation-4/main.svelte index 9697540e1..b7b330016 100644 --- a/packages/svelte/tests/runtime-runes/samples/event-attribute-delegation-4/main.svelte +++ b/packages/svelte/tests/runtime-runes/samples/event-attribute-delegation-4/main.svelte @@ -1,12 +1,13 @@ -
console.log('div main 1')} on:click={() => console.log('div main 2')}> + console.log('div main 1')} on:click={() => console.log('div main 2')}> -
+ diff --git a/packages/svelte/tests/runtime-runes/samples/event-attribute-delegation-5/Button.svelte b/packages/svelte/tests/runtime-runes/samples/event-attribute-delegation-5/Button.svelte new file mode 100644 index 000000000..3de20765c --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/event-attribute-delegation-5/Button.svelte @@ -0,0 +1,7 @@ + + + diff --git a/packages/svelte/tests/runtime-runes/samples/event-attribute-delegation-5/Component.svelte b/packages/svelte/tests/runtime-runes/samples/event-attribute-delegation-5/Component.svelte new file mode 100644 index 000000000..d096bacaa --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/event-attribute-delegation-5/Component.svelte @@ -0,0 +1,7 @@ + + +
+ {@render children()} +
diff --git a/packages/svelte/tests/runtime-runes/samples/event-attribute-delegation-5/main.svelte b/packages/svelte/tests/runtime-runes/samples/event-attribute-delegation-5/main.svelte index 6df18bff4..d7d70c0b0 100644 --- a/packages/svelte/tests/runtime-runes/samples/event-attribute-delegation-5/main.svelte +++ b/packages/svelte/tests/runtime-runes/samples/event-attribute-delegation-5/main.svelte @@ -1,5 +1,10 @@ -
console.log('outer div onclick')}> -
console.log('inner div on:click')}> - -
-
+ + + console.log('outer div onclick')}> + console.log('inner div on:click')}> + + + diff --git a/packages/svelte/tests/runtime-runes/samples/event-attribute-spread-collision/Button.svelte b/packages/svelte/tests/runtime-runes/samples/event-attribute-spread-collision/Button.svelte new file mode 100644 index 000000000..3de20765c --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/event-attribute-spread-collision/Button.svelte @@ -0,0 +1,7 @@ + + + diff --git a/packages/svelte/tests/runtime-runes/samples/event-attribute-spread-collision/main.svelte b/packages/svelte/tests/runtime-runes/samples/event-attribute-spread-collision/main.svelte index de589e3e4..b80782b28 100644 --- a/packages/svelte/tests/runtime-runes/samples/event-attribute-spread-collision/main.svelte +++ b/packages/svelte/tests/runtime-runes/samples/event-attribute-spread-collision/main.svelte @@ -1,21 +1,22 @@ - + - + - + - + diff --git a/packages/svelte/tests/validator/samples/a11y-click-events-have-key-events/input.svelte b/packages/svelte/tests/validator/samples/a11y-click-events-have-key-events/input.svelte index 4f23811a0..37df59812 100644 --- a/packages/svelte/tests/validator/samples/a11y-click-events-have-key-events/input.svelte +++ b/packages/svelte/tests/validator/samples/a11y-click-events-have-key-events/input.svelte @@ -25,7 +25,7 @@
-
+
@@ -68,7 +68,7 @@
-
+
diff --git a/packages/svelte/tests/validator/samples/a11y-click-events-have-key-events/warnings.json b/packages/svelte/tests/validator/samples/a11y-click-events-have-key-events/warnings.json index 00adecd2e..ec348e03e 100644 --- a/packages/svelte/tests/validator/samples/a11y-click-events-have-key-events/warnings.json +++ b/packages/svelte/tests/validator/samples/a11y-click-events-have-key-events/warnings.json @@ -92,7 +92,7 @@ }, "end": { "line": 28, - "column": 32 + "column": 33 } } ] diff --git a/packages/svelte/tests/validator/samples/runes-legacy-syntax-warnings-2/errors.json b/packages/svelte/tests/validator/samples/runes-legacy-syntax-warnings-2/errors.json new file mode 100644 index 000000000..6a60c0900 --- /dev/null +++ b/packages/svelte/tests/validator/samples/runes-legacy-syntax-warnings-2/errors.json @@ -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 + } + } +] diff --git a/packages/svelte/tests/validator/samples/runes-legacy-syntax-warnings-2/input.svelte b/packages/svelte/tests/validator/samples/runes-legacy-syntax-warnings-2/input.svelte new file mode 100644 index 000000000..c6df23913 --- /dev/null +++ b/packages/svelte/tests/validator/samples/runes-legacy-syntax-warnings-2/input.svelte @@ -0,0 +1,11 @@ + + + + + + + + + diff --git a/packages/svelte/tests/validator/samples/runes-legacy-syntax-warnings/input.svelte b/packages/svelte/tests/validator/samples/runes-legacy-syntax-warnings/input.svelte index e1d0afed4..557632850 100644 --- a/packages/svelte/tests/validator/samples/runes-legacy-syntax-warnings/input.svelte +++ b/packages/svelte/tests/validator/samples/runes-legacy-syntax-warnings/input.svelte @@ -3,8 +3,7 @@ - - + diff --git a/packages/svelte/tests/validator/samples/runes-legacy-syntax-warnings/warnings.json b/packages/svelte/tests/validator/samples/runes-legacy-syntax-warnings/warnings.json index b667f2e2d..90fc8fe6f 100644 --- a/packages/svelte/tests/validator/samples/runes-legacy-syntax-warnings/warnings.json +++ b/packages/svelte/tests/validator/samples/runes-legacy-syntax-warnings/warnings.json @@ -3,36 +3,36 @@ "code": "slot_element_deprecated", "end": { "column": 13, - "line": 11 + "line": 10 }, "message": "Using `` to render parent content is deprecated. Use `{@render ...}` tags instead.", "start": { "column": 0, - "line": 11 + "line": 10 } }, { "code": "slot_element_deprecated", "end": { "column": 24, - "line": 12 + "line": 11 }, "message": "Using `` to render parent content is deprecated. Use `{@render ...}` tags instead.", "start": { "column": 0, - "line": 12 + "line": 11 } }, { "code": "event_directive_deprecated", "end": { "column": 22, - "line": 13 + "line": 12 }, "message": "Using `on:click` to listen to the click event is deprecated. Use the event attribute `onclick` instead.", "start": { "column": 8, - "line": 13 + "line": 12 } } ]