From 635601d540e11a070892d8c4447b38d3f9dddfcb Mon Sep 17 00:00:00 2001 From: fisker Cheung Date: Tue, 24 Nov 2020 19:50:33 +0800 Subject: [PATCH] Improve `prefer-query-selector` (#908) --- rules/prefer-query-selector.js | 36 +- test/prefer-query-selector.js | 155 +++----- test/snapshots/prefer-query-selector.js.md | 372 ++++++++++++++++++- test/snapshots/prefer-query-selector.js.snap | Bin 209 -> 1295 bytes 4 files changed, 432 insertions(+), 131 deletions(-) diff --git a/rules/prefer-query-selector.js b/rules/prefer-query-selector.js index 6a51a2da87..61cfb047bd 100644 --- a/rules/prefer-query-selector.js +++ b/rules/prefer-query-selector.js @@ -1,11 +1,21 @@ 'use strict'; const getDocumentationUrl = require('./utils/get-documentation-url'); +const methodSelector = require('./utils/method-selector'); +const {notDomNodeSelector} = require('./utils/not-dom-node'); const MESSAGE_ID = 'prefer-query-selector'; const messages = { [MESSAGE_ID]: 'Prefer `.{{replacement}}()` over `.{{method}}()`.' }; +const selector = [ + methodSelector({ + names: ['getElementById', 'getElementsByClassName', 'getElementsByTagName'], + length: 1 + }), + notDomNodeSelector('callee.object') +].join(''); + const forbiddenIdentifierNames = new Map([ ['getElementById', 'querySelector'], ['getElementsByClassName', 'querySelectorAll'], @@ -93,37 +103,27 @@ const fix = (node, identifierName, preferedSelector) => { const create = context => { return { - CallExpression(node) { - const {callee: {property, type}} = node; - if (!property || type !== 'MemberExpression') { - return; - } - - const identifierName = property.name; - const preferedSelector = forbiddenIdentifierNames.get(identifierName); + [selector](node) { + const method = node.callee.property.name; + const preferedSelector = forbiddenIdentifierNames.get(method); if (!preferedSelector) { return; } - const [firstArgument] = node.arguments; - if (node.arguments.length !== 1 || firstArgument.type === 'SpreadElement') { - return; - } - - const report = { - node, + const problem = { + node: node.callee.property, messageId: MESSAGE_ID, data: { replacement: preferedSelector, - method: identifierName + method } }; if (canBeFixed(node.arguments[0])) { - report.fix = fix(node, identifierName, preferedSelector); + problem.fix = fix(node, method, preferedSelector); } - context.report(report); + context.report(problem); } }; }; diff --git a/test/prefer-query-selector.js b/test/prefer-query-selector.js index a00955dcb3..ed3ccfb2b1 100644 --- a/test/prefer-query-selector.js +++ b/test/prefer-query-selector.js @@ -1,17 +1,27 @@ import {test} from './utils/test'; - -const createError = (method, replacement) => ({ - messageId: 'prefer-query-selector', - data: {method, replacement} -}); +import notDomNodeTypes from './utils/not-dom-node-types'; +import {outdent} from 'outdent'; test({ valid: [ - // More or less arguments + // Not `CallExpression` + 'new document.getElementById(foo);', + // Not `MemberExpression` + 'getElementById(foo);', + // `callee.property` is not a `Identifier` + 'document[\'getElementById\'](bar);', + // Computed + 'document[getElementById](bar);', + // Not listed method + 'document.foo(bar);', + // More or less argument(s) 'document.getElementById();', 'document.getElementsByClassName("foo", "bar");', 'document.getElementById(...["id"]);', + // `callee.object` is not a DOM Node, + ...notDomNodeTypes.map(data => `(${data}).getElementById(foo)`), + 'document.querySelector("#foo");', 'document.querySelector(".bar");', 'document.querySelector("main #foo .bar");', @@ -19,112 +29,35 @@ test({ 'document.querySelectorAll("li a");', 'document.querySelector("li").querySelectorAll("a");' ], - invalid: [ - { - code: 'document.getElementById("foo");', - errors: [createError('getElementById', 'querySelector')], - output: 'document.querySelector("#foo");' - }, - { - code: 'document.getElementsByClassName("foo");', - errors: [createError('getElementsByClassName', 'querySelectorAll')], - output: 'document.querySelectorAll(".foo");' - }, - { - code: 'document.getElementsByClassName("foo bar");', - errors: [createError('getElementsByClassName', 'querySelectorAll')], - output: 'document.querySelectorAll(".foo.bar");' - }, - { - code: 'document.getElementsByTagName("foo");', - errors: [createError('getElementsByTagName', 'querySelectorAll')], - output: 'document.querySelectorAll("foo");' - }, - { - code: 'document.getElementById("");', - errors: [createError('getElementById', 'querySelector')] - }, - { - code: 'document.getElementById(\'foo\');', - errors: [createError('getElementById', 'querySelector')], - output: 'document.querySelector(\'#foo\');' - }, - { - code: 'document.getElementsByClassName(\'foo\');', - errors: [createError('getElementsByClassName', 'querySelectorAll')], - output: 'document.querySelectorAll(\'.foo\');' - }, - { - code: 'document.getElementsByClassName(\'foo bar\');', - errors: [createError('getElementsByClassName', 'querySelectorAll')], - output: 'document.querySelectorAll(\'.foo.bar\');' - }, - { - code: 'document.getElementsByTagName(\'foo\');', - errors: [createError('getElementsByTagName', 'querySelectorAll')], - output: 'document.querySelectorAll(\'foo\');' - }, - { - code: 'document.getElementsByClassName(\'\');', - errors: [createError('getElementsByClassName', 'querySelectorAll')] - }, - { - code: 'document.getElementById(`foo`);', - errors: [createError('getElementById', 'querySelector')], - output: 'document.querySelector(`#foo`);' - }, - { - code: 'document.getElementsByClassName(`foo`);', - errors: [createError('getElementsByClassName', 'querySelectorAll')], - output: 'document.querySelectorAll(`.foo`);' - }, - { - code: 'document.getElementsByClassName(`foo bar`);', - errors: [createError('getElementsByClassName', 'querySelectorAll')], - output: 'document.querySelectorAll(`.foo.bar`);' - }, - { - code: 'document.getElementsByTagName(`foo`);', - errors: [createError('getElementsByTagName', 'querySelectorAll')], - output: 'document.querySelectorAll(`foo`);' - }, - { - code: 'document.getElementsByTagName(``);', - errors: [createError('getElementsByTagName', 'querySelectorAll')] - }, - { - code: 'document.getElementsByClassName(`${fn()}`);', // eslint-disable-line no-template-curly-in-string - errors: [createError('getElementsByClassName', 'querySelectorAll')] - }, - { - code: 'document.getElementsByClassName(`foo ${undefined}`);', // eslint-disable-line no-template-curly-in-string - errors: [createError('getElementsByClassName', 'querySelectorAll')] - }, - { - code: 'document.getElementsByClassName(null);', - errors: [createError('getElementsByClassName', 'querySelectorAll')], - output: 'document.querySelectorAll(null);' - }, - { - code: 'document.getElementsByTagName(null);', - errors: [createError('getElementsByTagName', 'querySelectorAll')], - output: 'document.querySelectorAll(null);' - }, - { - code: 'document.getElementsByClassName(fn());', - errors: [createError('getElementsByClassName', 'querySelectorAll')] - }, - { - code: 'document.getElementsByClassName("foo" + fn());', - errors: [createError('getElementsByClassName', 'querySelectorAll')] - }, - { - code: 'document.getElementsByClassName(foo + "bar");', - errors: [createError('getElementsByClassName', 'querySelectorAll')] - } - ] + invalid: [] }); test.visualize([ - 'document.getElementById("foo");' + 'document.getElementById("foo");', + 'document.getElementsByClassName("foo");', + 'document.getElementsByClassName("foo bar");', + 'document.getElementsByTagName("foo");', + 'document.getElementById("");', + 'document.getElementById(\'foo\');', + 'document.getElementsByClassName(\'foo\');', + 'document.getElementsByClassName(\'foo bar\');', + 'document.getElementsByTagName(\'foo\');', + 'document.getElementsByClassName(\'\');', + 'document.getElementById(`foo`);', + 'document.getElementsByClassName(`foo`);', + 'document.getElementsByClassName(`foo bar`);', + 'document.getElementsByTagName(`foo`);', + 'document.getElementsByTagName(``);', + 'document.getElementsByClassName(`${fn()}`);', // eslint-disable-line no-template-curly-in-string + 'document.getElementsByClassName(`foo ${undefined}`);', // eslint-disable-line no-template-curly-in-string + 'document.getElementsByClassName(null);', + 'document.getElementsByTagName(null);', + 'document.getElementsByClassName(fn());', + 'document.getElementsByClassName("foo" + fn());', + 'document.getElementsByClassName(foo + "bar");', + outdent` + for (const div of document.body.getElementById("id").getElementsByClassName("class")) { + console.log(div.getElementsByTagName("div")); + } + ` ]); diff --git a/test/snapshots/prefer-query-selector.js.md b/test/snapshots/prefer-query-selector.js.md index 2f23262e87..fe8c359650 100644 --- a/test/snapshots/prefer-query-selector.js.md +++ b/test/snapshots/prefer-query-selector.js.md @@ -4,7 +4,7 @@ The actual snapshot is saved in `prefer-query-selector.js.snap`. Generated by [AVA](https://avajs.dev). -## prefer-query-selector - #1 +## prefer-query-selector - #01 > Snapshot 1 @@ -17,5 +17,373 @@ Generated by [AVA](https://avajs.dev). ␊ Error 1/1:␊ > 1 | document.getElementById("foo");␊ - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Prefer `.querySelector()` over `.getElementById()`.␊ + | ^^^^^^^^^^^^^^ Prefer `.querySelector()` over `.getElementById()`.␊ + ` + +## prefer-query-selector - #02 + +> Snapshot 1 + + `␊ + Input:␊ + 1 | document.getElementsByClassName("foo");␊ + ␊ + Output:␊ + 1 | document.querySelectorAll(".foo");␊ + ␊ + Error 1/1:␊ + > 1 | document.getElementsByClassName("foo");␊ + | ^^^^^^^^^^^^^^^^^^^^^^ Prefer `.querySelectorAll()` over `.getElementsByClassName()`.␊ + ` + +## prefer-query-selector - #03 + +> Snapshot 1 + + `␊ + Input:␊ + 1 | document.getElementsByClassName("foo bar");␊ + ␊ + Output:␊ + 1 | document.querySelectorAll(".foo.bar");␊ + ␊ + Error 1/1:␊ + > 1 | document.getElementsByClassName("foo bar");␊ + | ^^^^^^^^^^^^^^^^^^^^^^ Prefer `.querySelectorAll()` over `.getElementsByClassName()`.␊ + ` + +## prefer-query-selector - #04 + +> Snapshot 1 + + `␊ + Input:␊ + 1 | document.getElementsByTagName("foo");␊ + ␊ + Output:␊ + 1 | document.querySelectorAll("foo");␊ + ␊ + Error 1/1:␊ + > 1 | document.getElementsByTagName("foo");␊ + | ^^^^^^^^^^^^^^^^^^^^ Prefer `.querySelectorAll()` over `.getElementsByTagName()`.␊ + ` + +## prefer-query-selector - #05 + +> Snapshot 1 + + `␊ + Input:␊ + 1 | document.getElementById("");␊ + ␊ + Output:␊ + [Same as input]␊ + ␊ + Error 1/1:␊ + > 1 | document.getElementById("");␊ + | ^^^^^^^^^^^^^^ Prefer `.querySelector()` over `.getElementById()`.␊ + ` + +## prefer-query-selector - #06 + +> Snapshot 1 + + `␊ + Input:␊ + 1 | document.getElementById('foo');␊ + ␊ + Output:␊ + 1 | document.querySelector('#foo');␊ + ␊ + Error 1/1:␊ + > 1 | document.getElementById('foo');␊ + | ^^^^^^^^^^^^^^ Prefer `.querySelector()` over `.getElementById()`.␊ + ` + +## prefer-query-selector - #07 + +> Snapshot 1 + + `␊ + Input:␊ + 1 | document.getElementsByClassName('foo');␊ + ␊ + Output:␊ + 1 | document.querySelectorAll('.foo');␊ + ␊ + Error 1/1:␊ + > 1 | document.getElementsByClassName('foo');␊ + | ^^^^^^^^^^^^^^^^^^^^^^ Prefer `.querySelectorAll()` over `.getElementsByClassName()`.␊ + ` + +## prefer-query-selector - #08 + +> Snapshot 1 + + `␊ + Input:␊ + 1 | document.getElementsByClassName('foo bar');␊ + ␊ + Output:␊ + 1 | document.querySelectorAll('.foo.bar');␊ + ␊ + Error 1/1:␊ + > 1 | document.getElementsByClassName('foo bar');␊ + | ^^^^^^^^^^^^^^^^^^^^^^ Prefer `.querySelectorAll()` over `.getElementsByClassName()`.␊ + ` + +## prefer-query-selector - #09 + +> Snapshot 1 + + `␊ + Input:␊ + 1 | document.getElementsByTagName('foo');␊ + ␊ + Output:␊ + 1 | document.querySelectorAll('foo');␊ + ␊ + Error 1/1:␊ + > 1 | document.getElementsByTagName('foo');␊ + | ^^^^^^^^^^^^^^^^^^^^ Prefer `.querySelectorAll()` over `.getElementsByTagName()`.␊ + ` + +## prefer-query-selector - #10 + +> Snapshot 1 + + `␊ + Input:␊ + 1 | document.getElementsByClassName('');␊ + ␊ + Output:␊ + [Same as input]␊ + ␊ + Error 1/1:␊ + > 1 | document.getElementsByClassName('');␊ + | ^^^^^^^^^^^^^^^^^^^^^^ Prefer `.querySelectorAll()` over `.getElementsByClassName()`.␊ + ` + +## prefer-query-selector - #11 + +> Snapshot 1 + + `␊ + Input:␊ + 1 | document.getElementById(`foo`);␊ + ␊ + Output:␊ + 1 | document.querySelector(`#foo`);␊ + ␊ + Error 1/1:␊ + > 1 | document.getElementById(`foo`);␊ + | ^^^^^^^^^^^^^^ Prefer `.querySelector()` over `.getElementById()`.␊ + ` + +## prefer-query-selector - #12 + +> Snapshot 1 + + `␊ + Input:␊ + 1 | document.getElementsByClassName(`foo`);␊ + ␊ + Output:␊ + 1 | document.querySelectorAll(`.foo`);␊ + ␊ + Error 1/1:␊ + > 1 | document.getElementsByClassName(`foo`);␊ + | ^^^^^^^^^^^^^^^^^^^^^^ Prefer `.querySelectorAll()` over `.getElementsByClassName()`.␊ + ` + +## prefer-query-selector - #13 + +> Snapshot 1 + + `␊ + Input:␊ + 1 | document.getElementsByClassName(`foo bar`);␊ + ␊ + Output:␊ + 1 | document.querySelectorAll(`.foo.bar`);␊ + ␊ + Error 1/1:␊ + > 1 | document.getElementsByClassName(`foo bar`);␊ + | ^^^^^^^^^^^^^^^^^^^^^^ Prefer `.querySelectorAll()` over `.getElementsByClassName()`.␊ + ` + +## prefer-query-selector - #14 + +> Snapshot 1 + + `␊ + Input:␊ + 1 | document.getElementsByTagName(`foo`);␊ + ␊ + Output:␊ + 1 | document.querySelectorAll(`foo`);␊ + ␊ + Error 1/1:␊ + > 1 | document.getElementsByTagName(`foo`);␊ + | ^^^^^^^^^^^^^^^^^^^^ Prefer `.querySelectorAll()` over `.getElementsByTagName()`.␊ + ` + +## prefer-query-selector - #15 + +> Snapshot 1 + + `␊ + Input:␊ + 1 | document.getElementsByTagName(``);␊ + ␊ + Output:␊ + [Same as input]␊ + ␊ + Error 1/1:␊ + > 1 | document.getElementsByTagName(``);␊ + | ^^^^^^^^^^^^^^^^^^^^ Prefer `.querySelectorAll()` over `.getElementsByTagName()`.␊ + ` + +## prefer-query-selector - #16 + +> Snapshot 1 + + `␊ + Input:␊ + 1 | document.getElementsByClassName(`${fn()}`);␊ + ␊ + Output:␊ + [Same as input]␊ + ␊ + Error 1/1:␊ + > 1 | document.getElementsByClassName(`${fn()}`);␊ + | ^^^^^^^^^^^^^^^^^^^^^^ Prefer `.querySelectorAll()` over `.getElementsByClassName()`.␊ + ` + +## prefer-query-selector - #17 + +> Snapshot 1 + + `␊ + Input:␊ + 1 | document.getElementsByClassName(`foo ${undefined}`);␊ + ␊ + Output:␊ + [Same as input]␊ + ␊ + Error 1/1:␊ + > 1 | document.getElementsByClassName(`foo ${undefined}`);␊ + | ^^^^^^^^^^^^^^^^^^^^^^ Prefer `.querySelectorAll()` over `.getElementsByClassName()`.␊ + ` + +## prefer-query-selector - #18 + +> Snapshot 1 + + `␊ + Input:␊ + 1 | document.getElementsByClassName(null);␊ + ␊ + Output:␊ + 1 | document.querySelectorAll(null);␊ + ␊ + Error 1/1:␊ + > 1 | document.getElementsByClassName(null);␊ + | ^^^^^^^^^^^^^^^^^^^^^^ Prefer `.querySelectorAll()` over `.getElementsByClassName()`.␊ + ` + +## prefer-query-selector - #19 + +> Snapshot 1 + + `␊ + Input:␊ + 1 | document.getElementsByTagName(null);␊ + ␊ + Output:␊ + 1 | document.querySelectorAll(null);␊ + ␊ + Error 1/1:␊ + > 1 | document.getElementsByTagName(null);␊ + | ^^^^^^^^^^^^^^^^^^^^ Prefer `.querySelectorAll()` over `.getElementsByTagName()`.␊ + ` + +## prefer-query-selector - #20 + +> Snapshot 1 + + `␊ + Input:␊ + 1 | document.getElementsByClassName(fn());␊ + ␊ + Output:␊ + [Same as input]␊ + ␊ + Error 1/1:␊ + > 1 | document.getElementsByClassName(fn());␊ + | ^^^^^^^^^^^^^^^^^^^^^^ Prefer `.querySelectorAll()` over `.getElementsByClassName()`.␊ + ` + +## prefer-query-selector - #21 + +> Snapshot 1 + + `␊ + Input:␊ + 1 | document.getElementsByClassName("foo" + fn());␊ + ␊ + Output:␊ + [Same as input]␊ + ␊ + Error 1/1:␊ + > 1 | document.getElementsByClassName("foo" + fn());␊ + | ^^^^^^^^^^^^^^^^^^^^^^ Prefer `.querySelectorAll()` over `.getElementsByClassName()`.␊ + ` + +## prefer-query-selector - #22 + +> Snapshot 1 + + `␊ + Input:␊ + 1 | document.getElementsByClassName(foo + "bar");␊ + ␊ + Output:␊ + [Same as input]␊ + ␊ + Error 1/1:␊ + > 1 | document.getElementsByClassName(foo + "bar");␊ + | ^^^^^^^^^^^^^^^^^^^^^^ Prefer `.querySelectorAll()` over `.getElementsByClassName()`.␊ + ` + +## prefer-query-selector - #23 + +> Snapshot 1 + + `␊ + Input:␊ + 1 | for (const div of document.body.getElementById("id").getElementsByClassName("class")) {␊ + 2 | console.log(div.getElementsByTagName("div"));␊ + 3 | }␊ + ␊ + Output:␊ + 1 | for (const div of document.body.querySelector("#id").querySelectorAll(".class")) {␊ + 2 | console.log(div.querySelectorAll("div"));␊ + 3 | }␊ + ␊ + Error 1/3:␊ + > 1 | for (const div of document.body.getElementById("id").getElementsByClassName("class")) {␊ + | ^^^^^^^^^^^^^^ Prefer `.querySelector()` over `.getElementById()`.␊ + 2 | console.log(div.getElementsByTagName("div"));␊ + 3 | }␊ + Error 2/3:␊ + > 1 | for (const div of document.body.getElementById("id").getElementsByClassName("class")) {␊ + | ^^^^^^^^^^^^^^^^^^^^^^ Prefer `.querySelectorAll()` over `.getElementsByClassName()`.␊ + 2 | console.log(div.getElementsByTagName("div"));␊ + 3 | }␊ + Error 3/3:␊ + 1 | for (const div of document.body.getElementById("id").getElementsByClassName("class")) {␊ + > 2 | console.log(div.getElementsByTagName("div"));␊ + | ^^^^^^^^^^^^^^^^^^^^ Prefer `.querySelectorAll()` over `.getElementsByTagName()`.␊ + 3 | }␊ ` diff --git a/test/snapshots/prefer-query-selector.js.snap b/test/snapshots/prefer-query-selector.js.snap index f2fcae173ee22a57f3a702004e4a67333c97e5a5..f31df81fb1054555effeb8ad96081f6eee481489 100644 GIT binary patch literal 1295 zcmV+q1@QVoRzVJEja?bvEcC2qVKB*gGR6d`fQfLq#IWV`fYUtkr;f&<+L zvxND=rkTzSGm&NN!@xw6Ig}YR&dm=>mN7;5MPysZHWB6+F*t54}*&4naxFjjxLwml3$fgz$;X8cpiS-}0bx{Nvu+7g9SB zQI`m-N@w<|UsOat`uvN&i@P({EI~xogahDJx#>f#-EZTED=+RSKC&GVH9Z30zH)7U zUN1Yw@rC^rpA1|_M2(6B$UORneN*a4LuG%l+lWaniBdxJ~b**EnBqHV%x1wS4S47l`{av4X&x~)ddAtPi86mviBjP9$WzsQ)0XHd~E<|P7zgM#!CiiG&E zyPzzv);S#+N{scm37dJ7vzE0wbk(dQpJUx$H@gbV8kL3TRoYCYa+}lPZ~7i5YjYK| z9BXp$HjOG-oStv9@iwequg_5CNJ@Z((S-jU6&?d`x3L!1h8eGXS_bp=ME8oSWpql9 zdKGBidhMHCd7RO1FErM&bam%)oJOUiDcx_bCnA^!g?kJBXm}*@p^^{)65{!tOb}vTNmaM|2pAnr z>3(yCzE?7{zC}+)C$0*Jj+isly^_9bD4WJRk-T0xN(X}phEsY(legR{%AW=%rO{&5 zXq$slxBsy6Ux1~7SVT&r(`sfdHCEOfbQ=ER%a0|T5{uQzaWuvt&Zp)xyHI{252?&y zk$HHfk=HkVqVx^-?GElGnT;ra6Z}P4I(huSsW|uuydukwVurR01Yb+VD(@$`AT)zV zVEI8Dj_?Fw5w@=}@m9M7n``PZZ~3o+D&Fk!c{!>vtF&{TvdR5brPbm_A$pn+J<1)K z=U5%bS8Ig$b3D2Ue^EW|3xlwz+1rTb+oSheIoYG1=hFGN=ex*fYvPF;#1mgUf*!pw z$B@)2PyW|zK}Yo%M_Q=hkiaNd*D46u3~p?Tdk=F0+ohXzloXB&`=u6f`~y7T8&;nk F003wOhj{=1 literal 209 zcmV;?051PQRzV3chsvnC800000000A1 zU|?WiWLR)$M}OSm7cb}c?$wAE|G0{g0R+ASF*5@@n9az_AjtHBfy*hG_|NQI5j6Vxg@_x zLrEE?+_k7EzevGQ-_VN7jwlNhfS^Vp4lhs$C`wIBEmBB8vsN=fA-@bPj%K)Kf*uzD LZ1;A87XbhOBHdRg