fix: warn against accidental global event referenced

closes #10393
pull/10442/head
Simon Holthausen 3 years ago
parent 456cf843d2
commit 6edff52518

@ -0,0 +1,5 @@
---
"svelte": patch
---
fix: warn against accidental global event referenced

@ -1,10 +1,21 @@
import { error } from '../../errors.js';
import { extract_identifiers, get_parent, is_text_attribute, object } from '../../utils/ast.js';
import {
extract_identifiers,
get_parent,
is_expression_attribute,
is_text_attribute,
object
} from '../../utils/ast.js';
import { warn } from '../../warnings.js';
import fuzzymatch from '../1-parse/utils/fuzzymatch.js';
import { disallowed_parapgraph_contents, interactive_elements } from '../1-parse/utils/html.js';
import { binding_properties } from '../bindings.js';
import { ContentEditableBindings, EventModifiers, SVGElements } from '../constants.js';
import {
ContentEditableBindings,
EventModifiers,
SVGElements,
global_events
} from '../constants.js';
import { is_custom_element_node } from '../nodes.js';
import {
regex_illegal_attribute_character,
@ -66,12 +77,24 @@ function validate_element(node, context) {
}
if (attribute.name.startsWith('on') && attribute.name.length > 2) {
if (
attribute.value === true ||
is_text_attribute(attribute) ||
attribute.value.length > 1
) {
if (!is_expression_attribute(attribute)) {
error(attribute, 'invalid-event-attribute-value');
} else {
const value = attribute.value[0].expression;
if (
value.type === 'Identifier' &&
value.name === attribute.name &&
!context.state.scope.get(value.name) &&
global_events.has(attribute.name)
) {
warn(
context.state.analysis.warnings,
attribute,
context.path,
'global-event-reference',
attribute.name
);
}
}
}

@ -197,3 +197,120 @@ export const JsKeywords = [
'return',
'this'
];
/**
* Event variables that are available globally through the window object,
* but it's likely a user mistake if they're used
*/
export const global_events = new Set([
'onabort',
'onafterprint',
'onanimationcancel',
'onanimationend',
'onanimationiteration',
'onanimationstart',
'onauxclick',
'onbeforeprint',
'onbeforeunload',
'onblur',
'oncanplay',
'oncanplaythrough',
'onchange',
'onclick',
'onclose',
'oncontextmenu',
'oncuechange',
'ondblclick',
'ondevicemotion',
'ondeviceorientation',
'ondrag',
'ondragend',
'ondragenter',
'ondragleave',
'ondragover',
'ondragstart',
'ondrop',
'ondurationchange',
'onemptied',
'onended',
'onerror',
'onfocus',
'onformdata',
'ongamepadconnected',
'ongamepaddisconnected',
'ongotpointercapture',
'onhashchange',
'oninput',
'oninvalid',
'onkeydown',
'onkeypress',
'onkeyup',
'onlanguagechange',
'onload',
'onloadeddata',
'onloadedmetadata',
'onloadstart',
'onlostpointercapture',
'onmessage',
'onmessageerror',
'onmousedown',
'onmouseenter',
'onmouseleave',
'onmousemove',
'onmouseout',
'onmouseover',
'onmouseup',
'onoffline',
'ononline',
'onorientationchange',
'onpagehide',
'onpageshow',
'onpause',
'onplay',
'onplaying',
'onpointercancel',
'onpointerdown',
'onpointerenter',
'onpointerleave',
'onpointermove',
'onpointerout',
'onpointerover',
'onpointerup',
'onpopstate',
'onprogress',
'onratechange',
'onrejectionhandled',
'onreset',
'onresize',
'onscroll',
'onsecuritypolicyviolation',
'onseeked',
'onseeking',
'onselect',
'onselectionchange',
'onselectstart',
'onslotchange',
'onstalled',
'onstorage',
'onsubmit',
'onsuspend',
'ontimeupdate',
'ontoggle',
'ontouchcancel',
'ontouchend',
'ontouchmove',
'ontouchstart',
'ontransitioncancel',
'ontransitionend',
'ontransitionrun',
'ontransitionstart',
'onunhandledrejection',
'onunload',
'onvolumechange',
'onwaiting',
'onwebkitanimationend',
'onwebkitanimationiteration',
'onwebkitanimationstart',
'onwebkittransitionend',
'onwheel'
]);

@ -36,7 +36,7 @@ export function is_text_attribute(attribute) {
* @param {import('#compiler').Attribute} attribute
* @returns {attribute is import('#compiler').Attribute & { value: [import('#compiler').ExpressionTag] }}
*/
function is_expression_attribute(attribute) {
export function is_expression_attribute(attribute) {
return (
attribute.value !== true &&
attribute.value.length === 1 &&

@ -12,7 +12,10 @@ const css = {
/** @satisfies {Warnings} */
const attributes = {
'avoid-is': () => 'The "is" attribute is not supported cross-browser and should be avoided'
'avoid-is': () => 'The "is" attribute is not supported cross-browser and should be avoided',
/** @param {string} name */
'global-event-reference': (name) =>
`You are referencing the global property window.${name}. Did you forget to declare a variable with that name?`
};
/** @satisfies {Warnings} */

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

@ -0,0 +1,12 @@
<script>
let onclick;
</script>
<button {onclick}></button>
<button onclick={onclick}></button>
<button {onTypeScriptWillCatchThis}></button>
<button onTypeScriptWillCatchThis={onTypeScriptWillCatchThis}></button>
<button {onkeydown}></button>
<button onkeydown={onkeydown}></button>

@ -0,0 +1,26 @@
[
{
"code": "global-event-reference",
"message": "You are referencing the global property window.onkeydown. Did you forget to declare a variable with that name?",
"start": {
"column": 8,
"line": 11
},
"end": {
"column": 19,
"line": 11
}
},
{
"code": "global-event-reference",
"message": "You are referencing the global property window.onkeydown. Did you forget to declare a variable with that name?",
"start": {
"column": 8,
"line": 12
},
"end": {
"column": 29,
"line": 12
}
}
]
Loading…
Cancel
Save