fix: warn on undeclared shorthand event handlers on svelte:window/document/body (#18480)

Same as we already did on regular elements.
Fixes #18275

---------

Co-authored-by: Simon Holthausen <simon.holthausen@vercel.com>
pull/18680/merge
Alexander Kireyev 4 days ago committed by GitHub
parent 44bd73f3e5
commit 09a67efcfc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: warn on undeclared shorthand event handlers on `<svelte:window>`, `<svelte:document>` and `<svelte:body>`

@ -3,6 +3,7 @@
import * as e from '../../../errors.js';
import { is_event_attribute } from '../../../utils/ast.js';
import { disallow_children } from './shared/special-element.js';
import { check_global_event_reference } from './shared/utils.js';
/**
* @param {AST.SvelteBody} node
@ -11,10 +12,9 @@ import { disallow_children } from './shared/special-element.js';
export function SvelteBody(node, context) {
disallow_children(node);
for (const attribute of node.attributes) {
if (
attribute.type === 'SpreadAttribute' ||
(attribute.type === 'Attribute' && !is_event_attribute(attribute))
) {
if (attribute.type === 'Attribute' && is_event_attribute(attribute)) {
check_global_event_reference(attribute, context);
} else if (attribute.type === 'SpreadAttribute' || attribute.type === 'Attribute') {
e.svelte_body_illegal_attribute(attribute);
}
}

@ -3,6 +3,7 @@
import { disallow_children } from './shared/special-element.js';
import * as e from '../../../errors.js';
import { is_event_attribute } from '../../../utils/ast.js';
import { check_global_event_reference } from './shared/utils.js';
/**
* @param {AST.SvelteDocument} node
@ -12,10 +13,9 @@ export function SvelteDocument(node, context) {
disallow_children(node);
for (const attribute of node.attributes) {
if (
attribute.type === 'SpreadAttribute' ||
(attribute.type === 'Attribute' && !is_event_attribute(attribute))
) {
if (attribute.type === 'Attribute' && is_event_attribute(attribute)) {
check_global_event_reference(attribute, context);
} else if (attribute.type === 'SpreadAttribute' || attribute.type === 'Attribute') {
e.illegal_element_attribute(attribute, 'svelte:document');
}
}

@ -3,6 +3,7 @@
import { disallow_children } from './shared/special-element.js';
import * as e from '../../../errors.js';
import { is_event_attribute } from '../../../utils/ast.js';
import { check_global_event_reference } from './shared/utils.js';
/**
* @param {AST.SvelteWindow} node
@ -12,10 +13,9 @@ export function SvelteWindow(node, context) {
disallow_children(node);
for (const attribute of node.attributes) {
if (
attribute.type === 'SpreadAttribute' ||
(attribute.type === 'Attribute' && !is_event_attribute(attribute))
) {
if (attribute.type === 'Attribute' && is_event_attribute(attribute)) {
check_global_event_reference(attribute, context);
} else if (attribute.type === 'SpreadAttribute' || attribute.type === 'Attribute') {
e.illegal_element_attribute(attribute, 'svelte:window');
}
}

@ -9,6 +9,7 @@ import {
validate_attribute_name,
validate_slot_attribute
} from './attribute.js';
import { check_global_event_reference } from './utils.js';
const EVENT_MODIFIERS = [
'preventDefault',
@ -64,14 +65,7 @@ export function validate_element(node, context) {
e.attribute_invalid_event_handler(attribute);
}
const value = get_attribute_expression(attribute);
if (
value.type === 'Identifier' &&
value.name === attribute.name &&
!context.state.scope.get(value.name)
) {
w.attribute_global_event_reference(attribute, attribute.name);
}
check_global_event_reference(attribute, context);
}
if (attribute.name === 'slot') {

@ -4,7 +4,11 @@
/** @import { Scope } from '../../../scope' */
/** @import { NodeLike } from '../../../../errors.js' */
import * as e from '../../../../errors.js';
import { extract_identifiers, get_parent } from '../../../../utils/ast.js';
import {
extract_identifiers,
get_attribute_expression,
get_parent
} from '../../../../utils/ast.js';
import * as w from '../../../../warnings.js';
import * as b from '#compiler/builders';
import { get_rune } from '../../../scope.js';
@ -298,3 +302,21 @@ export function validate_export(node, scope, name) {
e.state_invalid_export(node);
}
}
/**
* Warns when an event attribute uses the shorthand form (`{onclick}`) but the
* referenced name isn't declared, so it silently resolves to the global handler.
* @param {AST.Attribute & { value: [AST.ExpressionTag] | AST.ExpressionTag }} attribute
* @param {Context} context
*/
export function check_global_event_reference(attribute, context) {
const value = get_attribute_expression(attribute);
if (
value.type === 'Identifier' &&
value.name === attribute.name &&
!context.state.scope.get(value.name)
) {
w.attribute_global_event_reference(attribute, attribute.name);
}
}

@ -0,0 +1,3 @@
import { test } from '../../test';
export default test({});

@ -0,0 +1,7 @@
<script>
let onkeydown;
</script>
<svelte:window {onkeydown} {onresize} />
<svelte:document {onvisibilitychange} />
<svelte:body {onfocus} />

@ -0,0 +1,38 @@
[
{
"code": "attribute_global_event_reference",
"message": "You are referencing `globalThis.onresize`. Did you forget to declare a variable with that name?",
"start": {
"column": 27,
"line": 5
},
"end": {
"column": 37,
"line": 5
}
},
{
"code": "attribute_global_event_reference",
"message": "You are referencing `globalThis.onvisibilitychange`. Did you forget to declare a variable with that name?",
"start": {
"column": 17,
"line": 6
},
"end": {
"column": 37,
"line": 6
}
},
{
"code": "attribute_global_event_reference",
"message": "You are referencing `globalThis.onfocus`. Did you forget to declare a variable with that name?",
"start": {
"column": 13,
"line": 7
},
"end": {
"column": 22,
"line": 7
}
}
]
Loading…
Cancel
Save