Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix alpha-value-notation performance with improved benchmark script #6864

5 changes: 5 additions & 0 deletions .changeset/tricky-gorillas-drive.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"stylelint": patch
---

Fixed: `alpha-value-notation` performance with improved benchmark script
51 changes: 23 additions & 28 deletions lib/rules/alpha-value-notation/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,27 +23,11 @@ const meta = {
fixable: true,
};

const ALPHA_PROPS = new Set([
'opacity',
'shape-image-threshold',
// SVG properties
'fill-opacity',
'flood-opacity',
'stop-opacity',
'stroke-opacity',
]);
const ALPHA_FUNCS = new Set([
'color',
'hsl',
'hsla',
'hwb',
'lab',
'lch',
'oklab',
'oklch',
'rgb',
'rgba',
]);
const ALPHA_PROPS_REGEXP =
/^(?:opacity|shape-image-threshold|fill-opacity|flood-opacity|stop-opacity|stroke-opacity)$/i;
const ALPHA_FUNCTION_REGEXP = /(?:color|hsla?|rgba?|hwb|lab|lch|oklab|oklch)\(/i;
Mouvedia marked this conversation as resolved.
Show resolved Hide resolved
const ALPHA_FUNCTION_NAME_REGEXP = /^(?:color|hsla?|rgba?|hwb|lab|lch|oklab|oklch)$/i;
const DIGIT_REGEXP = /\d/;

/** @type {import('stylelint').Rule} */
const rule = (primary, secondaryOptions, context) => {
Expand Down Expand Up @@ -78,20 +62,31 @@ const rule = (primary, secondaryOptions, context) => {
});

root.walkDecls((decl) => {
const declarationValue = getDeclarationValue(decl);
const isAlphaProp = ALPHA_PROPS_REGEXP.test(decl.prop);

if (
// If the property is not an alpha property
(!isAlphaProp &&
// And the value is not an alpha function
!ALPHA_FUNCTION_REGEXP.test(declarationValue)) ||
// Or the value does not contain digits
!DIGIT_REGEXP.test(declarationValue)
) {
// Abort early
return;
}

let needsFix = false;
const parsedValue = valueParser(getDeclarationValue(decl));
const parsedValue = valueParser(declarationValue);

parsedValue.walk((node) => {
/** @type {import('postcss-value-parser').Node | undefined} */
let alpha;

if (ALPHA_PROPS.has(decl.prop.toLowerCase())) {
if (isAlphaProp && DIGIT_REGEXP.test(node.value)) {
alpha = findAlphaInValue(node);
} else {
if (node.type !== 'function') return;

if (!ALPHA_FUNCS.has(node.value.toLowerCase())) return;

} else if (node.type === 'function' && ALPHA_FUNCTION_NAME_REGEXP.test(node.value)) {
alpha = findAlphaInFunction(node);
}

Expand Down
61 changes: 36 additions & 25 deletions scripts/benchmark-rule.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ let parsedOptions = ruleOptions;
/* eslint-disable eqeqeq */
if (
ruleOptions[0] === '[' ||
ruleOptions[0] === '{' ||
parsedOptions === 'true' ||
parsedOptions === 'false' ||
Number(parsedOptions) == parsedOptions
Expand All @@ -51,9 +52,43 @@ const processor = postcss().use(rule);
fetch(CSS_URL)
.then((response) => response.text())
.then((response) => {
let css = '';

for (let i = 0; i < 20; i++) {
Mouvedia marked this conversation as resolved.
Show resolved Hide resolved
css += `${response}\n\n`;
}

let firstTime = true;
let lazyResult;

const bench = new Benchmark('rule test', {
defer: true,
fn: (deferred) => benchFn(response, () => deferred.resolve()),
setup: () => {
lazyResult = processor.process(css, { from: undefined });
},
onCycle: () => {
lazyResult = processor.process(css, { from: undefined });
},
fn: (deferred) => {
lazyResult
.then((result) => {
if (firstTime) {
firstTime = false;
result.messages
.filter((m) => m.stylelintType === 'invalidOption')
.forEach((m) => {
console.log(bold(yellow(`>> ${m.text}`)));
});
console.log(`${bold('Warnings')}: ${result.warnings().length}`);
}

deferred.resolve();
})
.catch((err) => {
console.log(err.stack);
deferred.resolve();
});
},
});

bench.on('complete', () => {
Expand All @@ -64,28 +99,4 @@ fetch(CSS_URL)
bench.run();
})
.catch((error) => console.log('error:', error));

let firstTime = true;

function benchFn(css, done) {
processor
.process(css, { from: undefined })
.then((result) => {
if (firstTime) {
firstTime = false;
result.messages
.filter((m) => m.stylelintType === 'invalidOption')
.forEach((m) => {
console.log(bold(yellow(`>> ${m.text}`)));
});
console.log(`${bold('Warnings')}: ${result.warnings().length}`);
}

done();
})
.catch((err) => {
console.log(err.stack);
done();
});
}
/* eslint-enable no-console */