Skip to content

Commit

Permalink
Fix alpha-value-notation performance with improved benchmark script (
Browse files Browse the repository at this point in the history
…#6864)


---------

Co-authored-by: Masafumi Koba <473530+ybiquitous@users.noreply.github.com>
  • Loading branch information
romainmenke and ybiquitous committed May 29, 2023
1 parent 40b4a6d commit 2c7ae85
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 53 deletions.
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 =
/^(?:opacity|shape-image-threshold|fill-opacity|flood-opacity|stop-opacity|stroke-opacity)$/i;
const ALPHA_FUNCTION = /(?:color|hsla?|rgba?|hwb|lab|lch|oklab|oklch)\(/i;
const ALPHA_FUNCTION_NAME = /^(?:color|hsla?|rgba?|hwb|lab|lch|oklab|oklch)$/i;
const DIGIT = /\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.test(decl.prop);

if (
// If the property is not an alpha property
(!isAlphaProp &&
// And the value is not an alpha function
!ALPHA_FUNCTION.test(declarationValue)) ||
// Or the value does not contain digits
!DIGIT.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.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.test(node.value)) {
alpha = findAlphaInFunction(node);
}

Expand Down
68 changes: 43 additions & 25 deletions scripts/benchmark-rule.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,19 @@ if (!ruleOptions) {

const CSS_URL = 'https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.css';

// PostCSS and modern hardware is too fast to benchmark with a small source.
// Duplicating the source CSS N times gives a larger mean while reducing the deviation.
//
// 20 was chosen because it gives a mean in the 50-200ms range
// with a deviation that is ±10% of the mean.
const DUPLICATE_SOURCE_N_TIMES = 20;

let parsedOptions = ruleOptions;

/* eslint-disable eqeqeq */
if (
ruleOptions[0] === '[' ||
ruleOptions[0] === '{' ||
parsedOptions === 'true' ||
parsedOptions === 'false' ||
Number(parsedOptions) == parsedOptions
Expand All @@ -51,9 +59,43 @@ const processor = postcss().use(rule);
fetch(CSS_URL)
.then((response) => response.text())
.then((response) => {
let css = '';

for (let i = 0; i < DUPLICATE_SOURCE_N_TIMES; i++) {
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 +106,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 */

0 comments on commit 2c7ae85

Please sign in to comment.