From 29d7e73f1474055482b54196f7956c5bc448bc31 Mon Sep 17 00:00:00 2001 From: Romain Menke Date: Sun, 2 Jul 2023 18:09:07 +0200 Subject: [PATCH 1/2] Fix `no-descending-specificity` performance --- .../__tests__/index.js | 52 +++++++++++++++++++ lib/rules/no-descending-specificity/index.js | 36 +++++++------ 2 files changed, 71 insertions(+), 17 deletions(-) diff --git a/lib/rules/no-descending-specificity/__tests__/index.js b/lib/rules/no-descending-specificity/__tests__/index.js index dd02f2877c..73fcc4301e 100644 --- a/lib/rules/no-descending-specificity/__tests__/index.js +++ b/lib/rules/no-descending-specificity/__tests__/index.js @@ -190,6 +190,58 @@ testRule({ endLine: 1, endColumn: 29, }, + { + code: '.a .b .c {} .b .c {} .c {}', + warnings: [ + { + message: messages.rejected('.b .c', '.a .b .c'), + line: 1, + column: 13, + endLine: 1, + endColumn: 18, + }, + { + message: messages.rejected('.c', '.a .b .c'), + line: 1, + column: 22, + endLine: 1, + endColumn: 24, + }, + ], + }, + { + code: '.a .b .c {} .b .c {} .c {} .d .b .c {} .b .c {} .c {}', + warnings: [ + { + message: messages.rejected('.b .c', '.a .b .c'), + line: 1, + column: 13, + endLine: 1, + endColumn: 18, + }, + { + message: messages.rejected('.c', '.a .b .c'), + line: 1, + column: 22, + endLine: 1, + endColumn: 24, + }, + { + message: messages.rejected('.b .c', '.a .b .c'), + line: 1, + column: 40, + endLine: 1, + endColumn: 45, + }, + { + message: messages.rejected('.c', '.a .b .c'), + line: 1, + column: 49, + endLine: 1, + endColumn: 51, + }, + ], + }, ], }); diff --git a/lib/rules/no-descending-specificity/index.js b/lib/rules/no-descending-specificity/index.js index ac4a639913..4bbe50b0bc 100644 --- a/lib/rules/no-descending-specificity/index.js +++ b/lib/rules/no-descending-specificity/index.js @@ -13,7 +13,6 @@ const parseSelector = require('../../utils/parseSelector'); const report = require('../../utils/report'); const ruleMessages = require('../../utils/ruleMessages'); const validateOptions = require('../../utils/validateOptions'); -const { assert } = require('../../utils/validateTypes'); const ruleName = 'no-descending-specificity'; @@ -63,8 +62,10 @@ const rule = (primary, secondaryOptions) => { return; } + const selectors = ruleNode.selectors; + // Ignores selectors within list of selectors - if (ignoreSelectorsWithinList && ruleNode.selectors.length > 1) { + if (ignoreSelectorsWithinList && selectors.length > 1) { return; } @@ -74,43 +75,41 @@ const rule = (primary, secondaryOptions) => { findAtRuleContext(ruleNode), ); - for (const selector of ruleNode.selectors) { - const trimSelector = selector.trim(); - + for (const selector of selectors) { // Ignore `.selector, { }` - if (trimSelector === '') { + if (selector.trim() === '') { continue; } // Resolve any nested selectors before checking for (const resolvedSelector of resolvedNestedSelector(selector, ruleNode)) { - parseSelector(resolvedSelector, result, ruleNode, (s) => { - if (!isStandardSyntaxSelector(resolvedSelector)) { - return; - } + if (!isStandardSyntaxSelector(resolvedSelector)) { + continue; + } - checkSelector(s, ruleNode, comparisonContext); + parseSelector(resolvedSelector, result, ruleNode, (s) => { + checkSelector(resolvedSelector, s, ruleNode, comparisonContext); }); } } }); /** + * @param {string} selector * @param {import('postcss-selector-parser').Root} selectorNode * @param {import('postcss').Rule} ruleNode * @param {Map} comparisonContext */ - function checkSelector(selectorNode, ruleNode, comparisonContext) { - const selector = selectorNode.toString(); + function checkSelector(selector, selectorNode, ruleNode, comparisonContext) { const referenceSelector = lastCompoundSelectorWithoutPseudoClasses(selectorNode); - if (referenceSelector === undefined) return; + if (!referenceSelector) return; const selectorSpecificity = calculate(selectorNode); const entry = { selector, specificity: selectorSpecificity }; const priorComparableSelectors = comparisonContext.get(referenceSelector); - if (priorComparableSelectors === undefined) { + if (!priorComparableSelectors) { comparisonContext.set(referenceSelector, [entry]); return; @@ -126,6 +125,8 @@ const rule = (primary, secondaryOptions) => { messageArgs: [selector, priorEntry.selector], word: selector, }); + + break; } } @@ -141,11 +142,12 @@ const rule = (primary, secondaryOptions) => { function lastCompoundSelectorWithoutPseudoClasses(selectorNode) { const firstChild = selectorNode.nodes[0]; - assert(firstChild); + if (!firstChild) return undefined; + const nodesByCombinator = firstChild.split((node) => node.type === 'combinator'); const nodesAfterLastCombinator = nodesByCombinator[nodesByCombinator.length - 1]; - assert(nodesAfterLastCombinator); + if (!nodesAfterLastCombinator) return undefined; const nodesWithoutPseudoClasses = nodesAfterLastCombinator.filter((node) => { return ( From 84db52e9bb247631ca6ebb07579b7086f4a1b986 Mon Sep 17 00:00:00 2001 From: Richard Hallows Date: Sun, 2 Jul 2023 18:01:34 +0100 Subject: [PATCH 2/2] Create thirty-swans-complain.md --- .changeset/thirty-swans-complain.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/thirty-swans-complain.md diff --git a/.changeset/thirty-swans-complain.md b/.changeset/thirty-swans-complain.md new file mode 100644 index 0000000000..aaf466e6ce --- /dev/null +++ b/.changeset/thirty-swans-complain.md @@ -0,0 +1,5 @@ +--- +"stylelint": patch +--- + +Fixed: `no-descending-specificity` performance