Skip to content

Commit

Permalink
Merge pull request #27 from macbre/duplicated-selectors
Browse files Browse the repository at this point in the history
Report duplicated selectors
  • Loading branch information
macbre committed Dec 23, 2013
2 parents 1405f0e + eb58fdd commit 081cf9e
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 1 deletion.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ new analyzer('.foo {margin: 0 !important}', function(err, results) {
"comments": 1,
"commentsLength": 68,
"complexSelectors": 32,
"duplicatedSelectors": 7,
"emptyRules": 0,
"oldIEFixes": 51,
"importants": 3,
Expand Down Expand Up @@ -86,6 +87,7 @@ new analyzer('.foo {margin: 0 !important}', function(err, results) {
* **comments**: number of comments in CSS source
* **commentsLength**: length of comments content in CSS source
* **complexSelectors**: number of complex selectors (consisting of more than three expressions, e.g. ``header ul li .foo``)
* **duplicatedSelectors**: number of CSS selectors defined more than once in CSS source
* **emptyRules**: number of rules with no properties (e.g. ``.foo { }``)
* **oldIEFixes**: number of fixes for old versions of Internet Explorer (e.g. ``* html .foo {}`` and ``.foo { *zoom: 1 }``, [read](http://blogs.msdn.com/b/ie/archive/2005/09/02/460115.aspx) [more](http://www.impressivewebs.com/ie7-ie8-css-hacks/))
* **importants**: number of properties with value forced by ``!important``
Expand Down
45 changes: 45 additions & 0 deletions lib/collection.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/**
* Push items and count them
*/
function collection() {
this.items = {};
}

collection.prototype = {
push: function(item) {
if (typeof this.items[item] === 'undefined') {
this.items[item] = {
cnt: 1
};
}
else {
this.items[item].cnt++;
}
},

sort: function() {
var newItems = {},
sortedKeys;

// sort in descending order (by cnt)
sortedKeys = Object.keys(this.items).sort((function(a, b) {
return this.items[b].cnt - this.items[a].cnt;
}).bind(this));

// build new items dictionary
sortedKeys.forEach(function(key) {
newItems[key] = this.items[key];
}, this);

this.items = newItems;
return this;
},

forEach: function(callback) {
Object.keys(this.items).forEach(function(key) {
callback(key, this.items[key].cnt);
}, this);
}
};

module.exports = collection;
24 changes: 24 additions & 0 deletions rules/duplicated.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
var collection = require('../lib/collection'),
format = require('util').format;

function rule(analyzer) {
var selectors = new collection();

analyzer.setMetric('duplicatedSelectors');

analyzer.on('rule', function(rule) {
selectors.push(rule.selectors.join(', '));
});

analyzer.on('report', function() {
selectors.sort().forEach(function(selector, cnt) {
if (cnt > 1) {
analyzer.incrMetric('duplicatedSelectors');
analyzer.addOffender('duplicatedSelectors', format('%s (%d times)', selector, cnt));
}
});
});
}

rule.description = 'Reports duplicated CSS selectors';
module.exports = rule;
40 changes: 39 additions & 1 deletion test/tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,44 @@ tests = [
}
},

// duplicated selectors
{
css: '.foo { } .bar { }',
metrics: {
duplicatedSelectors: 0
}
},
{
css: '.foo, #bar { } .foo { }',
metrics: {
duplicatedSelectors: 0
}
},
{
css: '.foo { } .foo { }',
metrics: {
duplicatedSelectors: 1
}
},
{
css: '.foo { } .bar .foo { } .foo { }',
metrics: {
duplicatedSelectors: 1
}
},
{
css: '.foo { } .foo { } .foo { }',
metrics: {
duplicatedSelectors: 1
}
},
{
css: '.foo { } .bar { } .foo { } .bar { } #foo { } .bar { }',
metrics: {
duplicatedSelectors: 2
}
},

// empty rules
{
css: '.foo { } .bar { color: red } ',
Expand Down Expand Up @@ -293,7 +331,7 @@ testSelector = function(test) {
expectedMetrics = test.metrics;

Object.keys(expectedMetrics).forEach(function(metric) {
assert.equal(expectedMetrics[metric], actualMetrics[metric], metric + ' should equal ' + expectedMetrics[metric] + ' (was ' + actualMetrics[metric] + ')');
assert.strictEqual(expectedMetrics[metric], actualMetrics[metric], metric + ' should equal ' + expectedMetrics[metric] + ' (got ' + actualMetrics[metric] + ')');
});

done();
Expand Down

0 comments on commit 081cf9e

Please sign in to comment.