From 3e4f16883d1d54b250cd5d81ee3aa08e394e5703 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristj=C3=A1n=20Oddsson?= Date: Tue, 30 Mar 2021 13:26:56 +0100 Subject: [PATCH] Add `no-method-prefixed-with-on` rule Co-authored-by: Keith Cirkel --- README.md | 1 + docs/rules/no-method-prefixed-with-on.md | 37 ++++++++++++++ lib/rules.js | 1 + lib/rules/no-method-prefixed-with-on.js | 16 ++++++ test/no-method-prefixed-with-on.js | 62 ++++++++++++++++++++++++ 5 files changed, 117 insertions(+) create mode 100644 docs/rules/no-method-prefixed-with-on.md create mode 100644 lib/rules/no-method-prefixed-with-on.js create mode 100644 test/no-method-prefixed-with-on.js diff --git a/README.md b/README.md index f954f70..ae9d6dd 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,7 @@ JSON ESLint config example: - [No Customized Built in Elements](./docs/rules/no-customized-built-in-elements.md) - [No DOM Traversal in Connectedcallback](./docs/rules/no-dom-traversal-in-connectedcallback.md) - [No Exports with Element](./docs/rules/no-exports-with-element.md) +- [No Method Prefixed with on](./docs/rules/no-method-prefixed-with-on.md) - [One Element Per File](./docs/rules/one-element-per-file.md) - [Tag Name Matches Class](./docs/rules/tag-name-matches-class.md) - [Valid Tag Name](./docs/rules/valid-tag-name.md) diff --git a/docs/rules/no-method-prefixed-with-on.md b/docs/rules/no-method-prefixed-with-on.md new file mode 100644 index 0000000..34b4185 --- /dev/null +++ b/docs/rules/no-method-prefixed-with-on.md @@ -0,0 +1,37 @@ +# No Method Prefixed with on + +[Elements have a implicit contract with regards to `on` prefixed methods](https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Event_handlers). Any method prefixed with `on` is expected to be an assignable property and to fire at the same time that its similarly named event is fired. Consider [`onclick`](https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/onclick) vs. [the `click` event](https://developer.mozilla.org/en-US/docs/Web/API/Element/click_event). All built-in elements follow this contract for example the `HTMLDetailsElement.ontoggle` property or the `HTMLVideoElement.onwaiting` property. + +The [`GlobalEventHandlers`](https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers) mixin adds a list of `on` prefixed methods on `HTMLElement`, `Document` and `Window`. Prefixing methods with `on` risks colliding with these methods. The `GlobalEventHandlers` list is not fixed and has potential to grow as new elements or events are added to the HTML spec. + +## Rule Details + +This rule disallows any method names that start with `on` in a Custom Element class definition. + +👎 Examples of **incorrect** code for this rule: + +```js +class FooBar extends HTMLElement { + onclick() { + // ... + } +} +``` + +👍 Examples of **correct** code for this rule: + +```js +class FooBar extends HTMLElement { + handleClick() { + // ... + } +} +``` + +## When Not To Use It + +If you are comfortable with the possibility of clashing with `GlobalEventHandlers` or want to intentionally overwrite those methods. + +## Version + +This rule was introduced in v0.0.1 diff --git a/lib/rules.js b/lib/rules.js index 1bba0ab..076fa37 100644 --- a/lib/rules.js +++ b/lib/rules.js @@ -7,6 +7,7 @@ module.exports = { 'no-customized-built-in-elements': require('./rules/no-customized-built-in-elements'), 'no-dom-traversal-in-connectedcallback': require('./rules/no-dom-traversal-in-connectedcallback'), 'no-exports-with-element': require('./rules/no-exports-with-element'), + 'no-method-prefixed-with-on': require('./rules/no-method-prefixed-with-on'), 'one-element-per-file': require('./rules/one-element-per-file'), 'tag-name-matches-class': require('./rules/tag-name-matches-class'), 'valid-tag-name': require('./rules/valid-tag-name') diff --git a/lib/rules/no-method-prefixed-with-on.js b/lib/rules/no-method-prefixed-with-on.js new file mode 100644 index 0000000..53999fc --- /dev/null +++ b/lib/rules/no-method-prefixed-with-on.js @@ -0,0 +1,16 @@ +const s = require('../custom-selectors') + +module.exports = { + meta: { + type: 'suggestion', + docs: {description: '', url: require('../url')(module)} + }, + schema: [], + create(context) { + return { + [`${s.HTMLElementClass} MethodDefinition[key.name=/^on.*$/i]`](node) { + context.report(node, 'Avoid method names prefixed with `on`') + } + } + } +} diff --git a/test/no-method-prefixed-with-on.js b/test/no-method-prefixed-with-on.js new file mode 100644 index 0000000..b5b4c14 --- /dev/null +++ b/test/no-method-prefixed-with-on.js @@ -0,0 +1,62 @@ +const rule = require('../lib/rules/no-method-prefixed-with-on') +const RuleTester = require('eslint').RuleTester + +const ruleTester = new RuleTester({env: {es2020: true}}) +ruleTester.run('no-method-prefixed-with-on', rule, { + valid: [ + {code: 'class FooBar extends HTMLElement { handleClick() { } }'}, + {code: 'class FooBar extends HTMLElement { offClick() { } }'}, + {code: 'class FooBar extends HTMLElement { click() { } }'}, + {code: 'class FooBar extends HTMLElement { fooOnClick() { } }'}, + {code: 'class FooBar extends HTMLElement { fooonclick() { } }'}, + {code: 'class FooBar extends HTMLElement { handleOnClick() { } }'}, + {code: 'class FooBar extends HTMLElement { handleonclick() { } }'} + ], + invalid: [ + { + code: 'class FooBar extends HTMLElement { onclick() { } }', + errors: [ + { + message: 'Avoid method names prefixed with `on`', + type: 'MethodDefinition' + } + ] + }, + { + code: 'class FooBar extends HTMLElement { ontoggle() { } }', + errors: [ + { + message: 'Avoid method names prefixed with `on`', + type: 'MethodDefinition' + } + ] + }, + { + code: 'class FooBar extends HTMLElement { onload() { } }', + errors: [ + { + message: 'Avoid method names prefixed with `on`', + type: 'MethodDefinition' + } + ] + }, + { + code: 'class FooBar extends HTMLElement { onClick() { } }', + errors: [ + { + message: 'Avoid method names prefixed with `on`', + type: 'MethodDefinition' + } + ] + }, + { + code: 'class FooBar extends HTMLElement { oncease() { } }', + errors: [ + { + message: 'Avoid method names prefixed with `on`', + type: 'MethodDefinition' + } + ] + } + ] +})