From dd66e5d9bae0e7c75c387e6f4c2ef90480705e6c Mon Sep 17 00:00:00 2001 From: Romain Menke Date: Sat, 27 May 2023 20:01:47 +0200 Subject: [PATCH 1/5] Fix `at-rule-property-required-list` performance --- .../at-rule-property-required-list/index.js | 43 ++++++++++++++----- 1 file changed, 32 insertions(+), 11 deletions(-) diff --git a/lib/rules/at-rule-property-required-list/index.js b/lib/rules/at-rule-property-required-list/index.js index 1e88e0b16e..800a5b6961 100644 --- a/lib/rules/at-rule-property-required-list/index.js +++ b/lib/rules/at-rule-property-required-list/index.js @@ -30,6 +30,19 @@ const rule = (primary) => { return; } + /** @type {Map|undefined>} */ + const propLists = new Map(); + + for (const key in primary) { + if (!Object.hasOwnProperty.call(primary, key)) continue; + + const propList = flattenArray(primary[key]); + + if (!propList) continue; + + propLists.set(key, new Set(propList)); + } + root.walkAtRules((atRule) => { if (!isStandardSyntaxAtRule(atRule)) { return; @@ -37,32 +50,40 @@ 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(); + /** @type {Set} */ + const currentPropList = new Set(); - const hasProperty = nodes.find( - (node) => node.type === 'decl' && node.prop.toLowerCase() === propertyName, - ); + nodes.forEach((node) => { + if (!node || node.type !== 'decl') return; + + const propName = node.prop.toLowerCase(); + + if (!propList.has(propName)) return; + + 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; } }); }; From bd89b1dd229ad7b6bc4e7d2adbfde68c34cdef14 Mon Sep 17 00:00:00 2001 From: Romain Menke Date: Sat, 27 May 2023 20:07:31 +0200 Subject: [PATCH 2/5] reuse existing objects --- lib/rules/at-rule-property-required-list/index.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/rules/at-rule-property-required-list/index.js b/lib/rules/at-rule-property-required-list/index.js index 800a5b6961..6f504a8770 100644 --- a/lib/rules/at-rule-property-required-list/index.js +++ b/lib/rules/at-rule-property-required-list/index.js @@ -43,6 +43,9 @@ const rule = (primary) => { propLists.set(key, new Set(propList)); } + /** @type {Set} */ + const currentPropList = new Set(); + root.walkAtRules((atRule) => { if (!isStandardSyntaxAtRule(atRule)) { return; @@ -56,8 +59,7 @@ const rule = (primary) => { return; } - /** @type {Set} */ - const currentPropList = new Set(); + currentPropList.clear(); nodes.forEach((node) => { if (!node || node.type !== 'decl') return; From 891168414b0d3b0d85260ee01291aab7a8c39928 Mon Sep 17 00:00:00 2001 From: Romain Menke <11521496+romainmenke@users.noreply.github.com> Date: Sat, 27 May 2023 20:10:04 +0200 Subject: [PATCH 3/5] Create tender-cows-invite.md --- .changeset/tender-cows-invite.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/tender-cows-invite.md 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 From a9fddc89ac02670b57c28191ee275793b826f421 Mon Sep 17 00:00:00 2001 From: Romain Menke Date: Mon, 29 May 2023 12:31:22 +0200 Subject: [PATCH 4/5] add `createMapWithSet` utility --- .../at-rule-property-required-list/index.js | 15 ++--------- lib/utils/createMapWithSet.js | 27 +++++++++++++++++++ 2 files changed, 29 insertions(+), 13 deletions(-) create mode 100644 lib/utils/createMapWithSet.js diff --git a/lib/rules/at-rule-property-required-list/index.js b/lib/rules/at-rule-property-required-list/index.js index 6f504a8770..e0ff280c9b 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,18 +30,7 @@ const rule = (primary) => { return; } - /** @type {Map|undefined>} */ - const propLists = new Map(); - - for (const key in primary) { - if (!Object.hasOwnProperty.call(primary, key)) continue; - - const propList = flattenArray(primary[key]); - - if (!propList) continue; - - propLists.set(key, new Set(propList)); - } + const propLists = createMapWithSet(primary); /** @type {Set} */ const currentPropList = new Set(); diff --git a/lib/utils/createMapWithSet.js b/lib/utils/createMapWithSet.js new file mode 100644 index 0000000000..efa24a10f6 --- /dev/null +++ b/lib/utils/createMapWithSet.js @@ -0,0 +1,27 @@ +'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 in record) { + if (!Object.hasOwnProperty.call(record, key)) continue; + + const list = flattenArray(record[key]); + + if (!list) continue; + + map.set(key, new Set(list)); + } + + return map; +}; From f007bb2ddb8ea119c19f327b9c6bed47522d0236 Mon Sep 17 00:00:00 2001 From: Romain Menke Date: Mon, 29 May 2023 12:39:29 +0200 Subject: [PATCH 5/5] apply review suggestions --- lib/rules/at-rule-property-required-list/index.js | 8 ++++---- lib/utils/createMapWithSet.js | 6 ++---- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/lib/rules/at-rule-property-required-list/index.js b/lib/rules/at-rule-property-required-list/index.js index e0ff280c9b..a093305a1b 100644 --- a/lib/rules/at-rule-property-required-list/index.js +++ b/lib/rules/at-rule-property-required-list/index.js @@ -50,15 +50,15 @@ const rule = (primary) => { currentPropList.clear(); - nodes.forEach((node) => { - if (!node || node.type !== 'decl') return; + for (const node of nodes) { + if (!node || node.type !== 'decl') continue; const propName = node.prop.toLowerCase(); - if (!propList.has(propName)) return; + if (!propList.has(propName)) continue; currentPropList.add(propName); - }); + } if (currentPropList.size === propList.size) { return; diff --git a/lib/utils/createMapWithSet.js b/lib/utils/createMapWithSet.js index efa24a10f6..e6d2cb9c4e 100644 --- a/lib/utils/createMapWithSet.js +++ b/lib/utils/createMapWithSet.js @@ -13,10 +13,8 @@ module.exports = function createMapWithSet(record) { /** @type {Map>} */ const map = new Map(); - for (const key in record) { - if (!Object.hasOwnProperty.call(record, key)) continue; - - const list = flattenArray(record[key]); + for (const [key, value] of Object.entries(record)) { + const list = flattenArray(value); if (!list) continue;