From 3f606244f0752f6a0efbdfada5ed8c57cb8ac132 Mon Sep 17 00:00:00 2001 From: David Godfrey Date: Tue, 24 Sep 2019 17:28:50 +0100 Subject: [PATCH] fix(no-invalid-selectors): stop false-positives (html, non-select calls) --- lib/rules/no-invalid-selectors.js | 126 +++++++++++------- .../jquery-compat/no-invalid-selectors.js | 6 + 2 files changed, 84 insertions(+), 48 deletions(-) diff --git a/lib/rules/no-invalid-selectors.js b/lib/rules/no-invalid-selectors.js index 92c81f2..cd41919 100644 --- a/lib/rules/no-invalid-selectors.js +++ b/lib/rules/no-invalid-selectors.js @@ -1,6 +1,28 @@ const { isJQuery, detectJQueryCollections, resolve } = require('../util') const CSSselect = require('css-select') +const selectorMethods = [ + "add", + "addBack", + "children", + "closest", + "filter", + "find", + "has", + "is", + "next", + "nextAll", + "nextUntil", + "not", + "parent", + "parents", + "parentsUntil", + "prev", + "prevAll", + "prevUntil", + "siblings" +] + const jQueryPseudoClasses = [ 'animated', 'button', @@ -31,10 +53,6 @@ const jQueryPseudoFunctions = [ 'lt' ] -const jQueryPseudoAttributeComparators = [ - '!=' -] - const otherPseudoCorrections = [ 'hover' ] @@ -78,58 +96,70 @@ module.exports = { create: detectJQueryCollections(( context, { foundAPositiveMatch } - ) => ({ - "CallExpression:exit": node => { - const optionsObject = { - ...{ allowJQueryExtensions: false }, - ...(context.options.filter(option => typeof option === 'object')[0] || {}) - } + ) => { + const optionsObject = { + ...{allowJQueryExtensions: false}, + ...(context.options.filter(option => typeof option === 'object')[0] || {}) + } - const { allowJQueryExtensions } = optionsObject + const { allowJQueryExtensions } = optionsObject - if ( - !(isJQuery(node.callee) || foundAPositiveMatch(node)) || - !Array.isArray(node.arguments) || - node.arguments.length < 1 - ) { - return - } + return ({ + "CallExpression:exit": node => { + if ( + !(isJQuery(node.callee) || foundAPositiveMatch(node)) || + !Array.isArray(node.arguments) || + node.arguments.length < 1 + ) { + return + } - const maybeSelector = resolve()(node.arguments[0], context) + if ( + node.callee.type === "MemberExpression" && + !selectorMethods.includes(node.callee.property.name) + ) { + return + } - if (typeof maybeSelector !== "string") { - return - } + const maybeSelector = resolve()(node.arguments[0], context) - const selector = maybeSelector - let correctedSelector = selector + if ( + typeof maybeSelector !== "string" || + maybeSelector.indexOf('<') === 0 + ) { + return + } - if (allowJQueryExtensions) { - jQueryPseudoFunctions.forEach(pseudoFunc => { - correctedSelector = correctedSelector.replace(RegExp(`/:${pseudoFunc}\\b/`, "g"), ':not') - }) - jQueryPseudoClasses.forEach(pseudoClass => { - correctedSelector = correctedSelector.replace(RegExp(`:${pseudoClass}\\b`, "g"), ':empty') - }) - } + const selector = maybeSelector + let correctedSelector = selector - otherPseudoCorrections.forEach(pseudoClass => { - correctedSelector = correctedSelector.replace(RegExp(`:${pseudoClass}\\b`, "g"), ':empty') - }) + if (allowJQueryExtensions) { + jQueryPseudoFunctions.forEach(pseudoFunc => { + correctedSelector = correctedSelector.replace(RegExp(`/:${pseudoFunc}\\b/`, "g"), ':not') + }) + jQueryPseudoClasses.forEach(pseudoClass => { + correctedSelector = correctedSelector.replace(RegExp(`:${pseudoClass}\\b`, "g"), ':empty') + }) + } - try { - CSSselect.compile(correctedSelector) - return; - } catch (error) { - context.report({ - node: node.arguments[0], - data: { - method: isJQuery(node.callee) ? "$()" : `$.fn.${node.callee.property.name}()`, - selector: selector - }, - messageId: 'no-invalid-selectors' + otherPseudoCorrections.forEach(pseudoClass => { + correctedSelector = correctedSelector.replace(RegExp(`:${pseudoClass}\\b`, "g"), ':empty') }) + + try { + CSSselect.compile(correctedSelector) + return; + } catch (error) { + context.report({ + node: node.arguments[0], + data: { + method: isJQuery(node.callee) ? "$()" : `$.fn.${node.callee.property.name}()`, + selector: selector + }, + messageId: 'no-invalid-selectors' + }) + } } - } - })) + }) + }) } diff --git a/test/lib/rules/jquery-compat/no-invalid-selectors.js b/test/lib/rules/jquery-compat/no-invalid-selectors.js index 561f3ac..ec8e562 100644 --- a/test/lib/rules/jquery-compat/no-invalid-selectors.js +++ b/test/lib/rules/jquery-compat/no-invalid-selectors.js @@ -53,6 +53,12 @@ ruleTester.run('jquery-compat/no-invalid-selectors', rules['no-invalid-selectors allowJQueryExtensions: false } ] + }, + { + code: `$foo.append('div:first')` + }, + { + code: `$('div:first')` } ]), invalid: selectorMethods