From e7625446bea68503b16656a4cf30fda655b7c6ba Mon Sep 17 00:00:00 2001 From: Shekhar0109 Date: Sun, 28 Sep 2025 17:19:49 +0530 Subject: [PATCH] audit: detect repeated CSS rules (fixes #20) --- README.md | 3 +++ src/audits/repeated-rules.js | 25 +++++++++++++++++++++++++ src/run.js | 4 ++++ 3 files changed, 32 insertions(+) create mode 100644 src/audits/repeated-rules.js diff --git a/README.md b/README.md index 09e7116..ffb61e9 100644 --- a/README.md +++ b/README.md @@ -121,6 +121,9 @@ npm run css-audit -- v5.5/**/* --recommended - Places where `display: none` is used - `typography` - A collection of information about various typography-related properties +- `repeated-rules` + - Detects duplicate `property: value` pairs across different selectors + - Example: both `.foo` and `.bar` using `border: 1px solid red;` ## Technical details diff --git a/src/audits/repeated-rules.js b/src/audits/repeated-rules.js new file mode 100644 index 0000000..544bade --- /dev/null +++ b/src/audits/repeated-rules.js @@ -0,0 +1,25 @@ +module.exports = { + name: "repeated-rules", + description: "Detect repeated CSS property/value pairs across selectors", + + run: (astRoot, result) => { + const seen = new Map(); + const repeated = []; + + // Walk through all rules in the CSS AST + astRoot.walkDecls((decl) => { + const rule = `${decl.prop}: ${decl.value}`; + if (seen.has(rule)) { + repeated.push(rule); + } else { + seen.set(rule, decl.parent.selector); + } + }); + + if (repeated.length > 0) { + result.warn( + `Repeated rules found: ${[...new Set(repeated)].join(", ")}` + ); + } + }, +}; diff --git a/src/run.js b/src/run.js index 96abe94..13d2305 100644 --- a/src/run.js +++ b/src/run.js @@ -30,6 +30,10 @@ const runAudits = ( cssFiles ) => { if ( getArg( '--alphas' ) ) { audits.push( require( './audits/alphas' )( cssFiles ) ); } + if ( runAll || getArg( '--repeated-rules' ) ) { + audits.push( require( './audits/repeated-rules' )( cssFiles ) ); + } + const propertyValues = getArg( '--property-values' ); const isPropertyValuesArray =