diff --git a/.changeset/tender-cows-invite.md b/.changeset/tender-cows-invite.md new file mode 100644 index 0000000000..d8179d1545 --- /dev/null +++ b/.changeset/tender-cows-invite.md @@ -0,0 +1,5 @@ +--- +"stylelint": patch +--- + +Fixed: `at-rule-property-required-list` performance diff --git a/lib/rules/at-rule-property-required-list/index.js b/lib/rules/at-rule-property-required-list/index.js index 1e88e0b16e..a093305a1b 100644 --- a/lib/rules/at-rule-property-required-list/index.js +++ b/lib/rules/at-rule-property-required-list/index.js @@ -1,6 +1,6 @@ 'use strict'; -const flattenArray = require('../../utils/flattenArray'); +const createMapWithSet = require('../../utils/createMapWithSet'); const isStandardSyntaxAtRule = require('../../utils/isStandardSyntaxAtRule'); const report = require('../../utils/report'); const ruleMessages = require('../../utils/ruleMessages'); @@ -30,6 +30,11 @@ const rule = (primary) => { return; } + const propLists = createMapWithSet(primary); + + /** @type {Set} */ + const currentPropList = new Set(); + root.walkAtRules((atRule) => { if (!isStandardSyntaxAtRule(atRule)) { return; @@ -37,32 +42,39 @@ const rule = (primary) => { const { name, nodes } = atRule; const atRuleName = name.toLowerCase(); - const propList = flattenArray(primary[atRuleName]); + const propList = propLists.get(atRuleName); if (!propList) { return; } - for (const property of propList) { - const propertyName = property.toLowerCase(); + currentPropList.clear(); + + for (const node of nodes) { + if (!node || node.type !== 'decl') continue; + + const propName = node.prop.toLowerCase(); + + if (!propList.has(propName)) continue; - const hasProperty = nodes.find( - (node) => node.type === 'decl' && node.prop.toLowerCase() === propertyName, - ); + currentPropList.add(propName); + } + + if (currentPropList.size === propList.size) { + return; + } - if (hasProperty) { - continue; - } + for (const requiredProp of propList) { + if (currentPropList.has(requiredProp)) continue; report({ message: messages.expected, - messageArgs: [atRuleName, propertyName], + messageArgs: [atRuleName, requiredProp], node: atRule, word: `@${atRule.name}`, result, ruleName, }); - continue; } }); }; diff --git a/lib/utils/createMapWithSet.js b/lib/utils/createMapWithSet.js new file mode 100644 index 0000000000..e6d2cb9c4e --- /dev/null +++ b/lib/utils/createMapWithSet.js @@ -0,0 +1,25 @@ +'use strict'; + +const flattenArray = require('./flattenArray'); + +/** + * Create a map with unique sets of values from a record. + * + * @template T + * @param {Record} record + * @returns {Map>} + */ +module.exports = function createMapWithSet(record) { + /** @type {Map>} */ + const map = new Map(); + + for (const [key, value] of Object.entries(record)) { + const list = flattenArray(value); + + if (!list) continue; + + map.set(key, new Set(list)); + } + + return map; +};