diff --git a/plugins/postcss-custom-properties/CHANGELOG.md b/plugins/postcss-custom-properties/CHANGELOG.md index 4693432ee..ec10fd379 100644 --- a/plugins/postcss-custom-properties/CHANGELOG.md +++ b/plugins/postcss-custom-properties/CHANGELOG.md @@ -1,5 +1,9 @@ # Changes to PostCSS Custom Properties +### Unreleased + +- Improve plugin performance + ### 12.1.10 (October 20, 2022) - Fix how `preserve: false` interacts with logic around duplicate code (see `12.1.9`). diff --git a/plugins/postcss-custom-properties/src/lib/get-custom-properties-from-root.ts b/plugins/postcss-custom-properties/src/lib/get-custom-properties-from-root.ts index e177478fa..1ea953d63 100644 --- a/plugins/postcss-custom-properties/src/lib/get-custom-properties-from-root.ts +++ b/plugins/postcss-custom-properties/src/lib/get-custom-properties-from-root.ts @@ -1,15 +1,18 @@ import valuesParser from 'postcss-value-parser'; -import { isBlockIgnored } from './is-ignored'; +import { isBlockIgnored, isDeclarationIgnored } from './is-ignored'; // return custom selectors from the css root, conditionally removing them export default function getCustomPropertiesFromRoot(root, opts): Map { // initialize custom selectors - const customPropertiesFromHtmlElement: Map = new Map(); - const customPropertiesFromRootPseudo: Map = new Map(); - const out: Map = new Map(); + const customPropertiesFromHtmlElement: Map = new Map(); + const customPropertiesFromRootPseudo: Map = new Map(); // for each html or :root rule root.nodes.slice().forEach(rule => { + if (isBlockIgnored(rule)) { + return; + } + const customPropertiesObject = isHtmlRule(rule) ? customPropertiesFromHtmlElement : isRootRule(rule) @@ -19,11 +22,11 @@ export default function getCustomPropertiesFromRoot(root, opts): Map { - if (decl.variable && !isBlockIgnored(decl)) { + if (decl.variable && !isDeclarationIgnored(decl)) { const { prop } = decl; // write the parsed value to the custom property - customPropertiesObject.set(prop, valuesParser(decl.value)); + customPropertiesObject.set(prop, decl.value); // conditionally remove the custom property declaration if (!opts.preserve) { @@ -33,18 +36,19 @@ export default function getCustomPropertiesFromRoot(root, opts): Map = new Map(); for (const [name, value] of customPropertiesFromHtmlElement.entries()) { - out.set(name, value); + out.set(name, valuesParser(value)); } for (const [name, value] of customPropertiesFromRootPseudo.entries()) { - out.set(name, value); + out.set(name, valuesParser(value)); } // return all custom properties, preferring :root properties over html properties diff --git a/plugins/postcss-custom-properties/src/lib/is-ignored.ts b/plugins/postcss-custom-properties/src/lib/is-ignored.ts index e1c5ccc5c..9c3842f76 100644 --- a/plugins/postcss-custom-properties/src/lib/is-ignored.ts +++ b/plugins/postcss-custom-properties/src/lib/is-ignored.ts @@ -1,20 +1,43 @@ -function isBlockIgnored(ruleOrDeclaration) { - const rule = ruleOrDeclaration.selector ? - ruleOrDeclaration : ruleOrDeclaration.parent; +import type { Comment, Container, Declaration, Node } from 'postcss'; - return /(!\s*)?postcss-custom-properties:\s*off\b/i.test(rule.toString()); +const blockRegExp = /(!\s*)?postcss-custom-properties:\s*off\b/i; + +const blockIgnoredCache = new WeakMap(); + +function isBlockIgnored(container: Container) { + if (!container || !container.nodes) { + return false; + } + + if (blockIgnoredCache.has(container)) { + return blockIgnoredCache.get(container); + } + + const result = container.some((child) => isIgnoreComment(child, blockRegExp)); + blockIgnoredCache.set(container, result); + + return result; } -function isRuleIgnored(rule) { - const previous = rule.prev(); +const declarationRegExp = /(!\s*)?postcss-custom-properties:\s*ignore\s+next\b/i; + +function isDeclarationIgnored(decl: Declaration) { + if (!decl) { + return false; + } + + if (isBlockIgnored(decl.parent)) { + return true; + } + + return isIgnoreComment(decl.prev(), declarationRegExp); +} - return Boolean(isBlockIgnored(rule) || - previous && - previous.type === 'comment' && - /(!\s*)?postcss-custom-properties:\s*ignore\s+next\b/i.test(previous.text)); +function isIgnoreComment(node: Node, regexp: RegExp) { + return node && node.type === 'comment' && regexp.test((node as Comment).text); } export { isBlockIgnored, - isRuleIgnored, + isDeclarationIgnored, }; diff --git a/plugins/postcss-custom-properties/src/lib/transform-properties.ts b/plugins/postcss-custom-properties/src/lib/transform-properties.ts index da2a8147a..f0ac25055 100644 --- a/plugins/postcss-custom-properties/src/lib/transform-properties.ts +++ b/plugins/postcss-custom-properties/src/lib/transform-properties.ts @@ -1,11 +1,11 @@ import valuesParser from 'postcss-value-parser'; import transformValueAST from './transform-value-ast'; -import { isRuleIgnored } from './is-ignored'; import { Declaration } from 'postcss'; +import { isDeclarationIgnored } from './is-ignored'; // transform custom pseudo selectors with custom selectors export default (decl, customProperties, opts) => { - if (isTransformableDecl(decl) && !isRuleIgnored(decl)) { + if (isTransformableDecl(decl) && !isDeclarationIgnored(decl)) { const originalValue = decl.value; const valueAST = valuesParser(originalValue); let value = transformValueAST(valueAST, customProperties);