From 17b4243589760a5acf0ffce7e0a6992a563de352 Mon Sep 17 00:00:00 2001 From: Ed Sanders Date: Wed, 11 May 2022 16:05:22 +0100 Subject: [PATCH] New rule: no-nodelist-unsupported-methods This rule disallows NodeList prototype methods unsupported by various browsers. --- docs/rules/no-nodelist-unsupported-methods.md | 32 ++++++++++++ src/index.js | 1 + src/rules/no-nodelist-unsupported-methods.js | 49 +++++++++++++++++++ .../rules/no-nodelist-unsupported-methods.js | 42 ++++++++++++++++ 4 files changed, 124 insertions(+) create mode 100644 docs/rules/no-nodelist-unsupported-methods.md create mode 100644 src/rules/no-nodelist-unsupported-methods.js create mode 100644 tests/rules/no-nodelist-unsupported-methods.js diff --git a/docs/rules/no-nodelist-unsupported-methods.md b/docs/rules/no-nodelist-unsupported-methods.md new file mode 100644 index 0000000..5cff56a --- /dev/null +++ b/docs/rules/no-nodelist-unsupported-methods.md @@ -0,0 +1,32 @@ +[//]: # (This file is generated by eslint-docgen. Do not edit it directly.) + +# no-nodelist-unsupported-methods + +Prohibits [NodeList methods](https://developer.mozilla.org/en-US/docs/Web/API/NodeList#browser_compatibility) not supported by Chrome<51, Firefox<50, Safari<10, IE & others + +## Rule details + +❌ Examples of **incorrect** code: +```js +element.childNodes.forEach(); +element.childNodes.entries(); +element.childNodes.keys(); +element.childNodes.values(); +document.querySelectorAll( '.foo' ).forEach(); +``` + +✔️ Examples of **correct** code: +```js +Array.prototype.forEach.call( element.childNodes, function ( element ) {} ); +[].forEach(); +[].property.forEach(); +[].method().forEach(); +element.childNodes.item( 1 ); +document.querySelectorAll( '.foo' ).item( 1 ); +forEach(); +``` + +## Resources + +* [Rule source](/src/rules/no-nodelist-unsupported-methods.js) +* [Test source](/tests/rules/no-nodelist-unsupported-methods.js) diff --git a/src/index.js b/src/index.js index 007292f..87a2431 100644 --- a/src/index.js +++ b/src/index.js @@ -5,6 +5,7 @@ module.exports = { 'class-doc': require( './rules/class-doc.js' ), 'msg-doc': require( './rules/msg-doc.js' ), 'no-extended-unicode-identifiers': require( './rules/no-extended-unicode-identifiers' ), + 'no-nodelist-unsupported-methods': require( './rules/no-nodelist-unsupported-methods' ), 'no-vue-dynamic-i18n': require( './rules/no-vue-dynamic-i18n.js' ), 'valid-package-file-require': require( './rules/valid-package-file-require.js' ), 'vue-exports-component-directive': require( './rules/vue-exports-component-directive.js' ) diff --git a/src/rules/no-nodelist-unsupported-methods.js b/src/rules/no-nodelist-unsupported-methods.js new file mode 100644 index 0000000..d3e9296 --- /dev/null +++ b/src/rules/no-nodelist-unsupported-methods.js @@ -0,0 +1,49 @@ +'use strict'; + +const methods = [ 'forEach', 'entries', 'keys', 'values' ]; + +module.exports = { + meta: { + type: 'problem', + docs: { + // Full browser support table: https://developer.mozilla.org/en-US/docs/Web/API/NodeList#browser_compatibility + description: 'Prohibits [NodeList methods](https://developer.mozilla.org/en-US/docs/Web/API/NodeList#browser_compatibility) not supported by Chrome<51, Firefox<50, Safari<10, IE & others' + }, + schema: [], + messages: { + nodelistMethod: 'NodeList.{{method}} not supported by Chrome<51, Firefox<50, Safari<10, IE & others. Use Array.prototype.{{method}}.call instead.' + } + }, + + create( context ) { + return { + CallExpression( node ) { + if ( node.callee.type !== 'MemberExpression' ) { + return; + } + const name = node.callee.property.name; + if ( !methods.includes( name ) ) { + return; + } + + if ( + // element.childNodes.forEach + ( node.callee.object.property && node.callee.object.property.name === 'childNodes' ) || + // element.querySelectorAll( '' ).forEach + ( + node.callee.object.callee && node.callee.object.callee.property && + node.callee.object.callee.property.name === 'querySelectorAll' + ) + ) { + context.report( { + node: node, + messageId: 'nodelistMethod', + data: { + method: name + } + } ); + } + } + }; + } +}; diff --git a/tests/rules/no-nodelist-unsupported-methods.js b/tests/rules/no-nodelist-unsupported-methods.js new file mode 100644 index 0000000..96694be --- /dev/null +++ b/tests/rules/no-nodelist-unsupported-methods.js @@ -0,0 +1,42 @@ +'use strict'; + +const rule = require( '../../src/rules/no-nodelist-unsupported-methods' ); +const RuleTester = require( 'eslint-docgen' ).RuleTester; +const errorMessage = function ( method ) { + return `NodeList.${method} not supported by Chrome<51, Firefox<50, Safari<10, IE & others. Use Array.prototype.${method}.call instead.`; +}; + +const ruleTester = new RuleTester(); +ruleTester.run( 'no-nodelist-unsupported-methods', rule, { + valid: [ + 'Array.prototype.forEach.call( element.childNodes, function ( element ) {} )', + '[].forEach()', + '[].property.forEach()', + '[].method().forEach()', + 'element.childNodes.item(1)', + 'document.querySelectorAll(".foo").item(1)', + 'forEach()' + ], + invalid: [ + { + code: 'element.childNodes.forEach()', + errors: [ errorMessage( 'forEach' ) ] + }, + { + code: 'element.childNodes.entries()', + errors: [ errorMessage( 'entries' ) ] + }, + { + code: 'element.childNodes.keys()', + errors: [ errorMessage( 'keys' ) ] + }, + { + code: 'element.childNodes.values()', + errors: [ errorMessage( 'values' ) ] + }, + { + code: 'document.querySelectorAll(".foo").forEach()', + errors: [ errorMessage( 'forEach' ) ] + } + ] +} );