From: Jakub Pawlowicz Date: Sun, 21 Jun 2015 10:40:18 +0000 (+0100) Subject: Extracts 'remove duplicates' optimization into a module. X-Git-Url: https://git.ndcode.org/public/gitweb.cgi?a=commitdiff_plain;h=a53529c82cc21c9519d7d0d6628eb9d5432953c4;p=clean-css.git Extracts 'remove duplicates' optimization into a module. --- diff --git a/lib/selectors/advanced.js b/lib/selectors/advanced.js index b78d63b4..0bde05b7 100644 --- a/lib/selectors/advanced.js +++ b/lib/selectors/advanced.js @@ -8,6 +8,8 @@ var stringifyAll = require('../stringifier/one-time').all; var stringifyBody = require('../stringifier/one-time').body; var stringifySelectors = require('../stringifier/one-time').selectors; +var removeDuplicates = require('./remove-duplicates'); + function AdvancedOptimizer(options, context) { this.options = options; this.validator = context.validator; @@ -25,43 +27,6 @@ AdvancedOptimizer.prototype.isSpecial = function (selector) { return this.options.compatibility.selectors.special.test(selector); }; -AdvancedOptimizer.prototype.removeDuplicates = function (tokens) { - var matched = {}; - var moreThanOnce = []; - var id, token; - var body, bodies; - - for (var i = 0, l = tokens.length; i < l; i++) { - token = tokens[i]; - if (token[0] != 'selector') - continue; - - id = stringifySelectors(token[1]); - - if (matched[id] && matched[id].length == 1) - moreThanOnce.push(id); - else - matched[id] = matched[id] || []; - - matched[id].push(i); - } - - for (i = 0, l = moreThanOnce.length; i < l; i++) { - id = moreThanOnce[i]; - bodies = []; - - for (var j = matched[id].length - 1; j >= 0; j--) { - token = tokens[matched[id][j]]; - body = stringifyBody(token[2]); - - if (bodies.indexOf(body) > -1) - token[2] = []; - else - bodies.push(body); - } - } -}; - AdvancedOptimizer.prototype.mergeAdjacent = function (tokens) { var lastToken = [null, [], []]; var adjacentSpace = this.options.compatibility.selectors.adjacentSpace; @@ -813,7 +778,7 @@ AdvancedOptimizer.prototype.optimize = function (tokens) { recursivelyOptimizeProperties(tokens, self.options, self.validator); - self.removeDuplicates(tokens); + removeDuplicates(tokens); self.mergeAdjacent(tokens); self.reduceNonAdjacent(tokens); diff --git a/lib/selectors/remove-duplicates.js b/lib/selectors/remove-duplicates.js new file mode 100644 index 00000000..3a2ce959 --- /dev/null +++ b/lib/selectors/remove-duplicates.js @@ -0,0 +1,41 @@ +var stringifyBody = require('../stringifier/one-time').body; +var stringifySelectors = require('../stringifier/one-time').selectors; + +function removeDuplicates(tokens) { + var matched = {}; + var moreThanOnce = []; + var id, token; + var body, bodies; + + for (var i = 0, l = tokens.length; i < l; i++) { + token = tokens[i]; + if (token[0] != 'selector') + continue; + + id = stringifySelectors(token[1]); + + if (matched[id] && matched[id].length == 1) + moreThanOnce.push(id); + else + matched[id] = matched[id] || []; + + matched[id].push(i); + } + + for (i = 0, l = moreThanOnce.length; i < l; i++) { + id = moreThanOnce[i]; + bodies = []; + + for (var j = matched[id].length - 1; j >= 0; j--) { + token = tokens[matched[id][j]]; + body = stringifyBody(token[2]); + + if (bodies.indexOf(body) > -1) + token[2] = []; + else + bodies.push(body); + } + } +} + +module.exports = removeDuplicates; diff --git a/test/integration-test.js b/test/integration-test.js index a8d95ae0..9c9bc2a5 100644 --- a/test/integration-test.js +++ b/test/integration-test.js @@ -2146,38 +2146,6 @@ vows.describe('integration tests') ] }) ) - .addBatch( - optimizerContext('duplicate selectors in a scope', { - 'of two successive selectors': [ - 'a{color:red}a{color:red}', - 'a{color:red}' - ], - 'of two successive selectors with different body': [ - 'a{color:red}a{display:block}', - 'a{color:red;display:block}' - ], - 'of many successive selectors': [ - 'a{color:red}a{color:red}a{color:red}a{color:red}', - 'a{color:red}' - ], - 'of two non-successive selectors': [ - 'a{color:red}p{color:#fff}a{color:red}', - 'p{color:#fff}a{color:red}' - ], - 'of many non-successive selectors': [ - 'div{width:100%}a{color:red}a{color:red}p{color:#fff}div{width:100%}ol{margin:0}p{color:#fff}', - 'a{color:red}div{width:100%}ol{margin:0}p{color:#fff}' - ], - 'with global and media scope': [ - 'a{color:red}@media screen{a{color:red}p{width:100px}a{color:red}}', - 'a{color:red}@media screen{p{width:100px}a{color:red}}' - ], - 'with two media scopes': [ - '@media (min-width:100px){a{color:red}}@media screen{a{color:red}p{width:100px}a{color:red}}', - '@media (min-width:100px){a{color:red}}@media screen{p{width:100px}a{color:red}}' - ] - }) - ) .addBatch( optimizerContext('duplicate properties with aggressive merging disabled', { 'of (yet) unmergeable properties': [ diff --git a/test/selectors/advanced-test.js b/test/selectors/advanced-test.js index c26463b6..04152dea 100644 --- a/test/selectors/advanced-test.js +++ b/test/selectors/advanced-test.js @@ -209,14 +209,6 @@ vows.describe('advanced optimizer') 'a{color:red;color:red}', 'a{color:red}' ], - 'duplicates - same context': [ - 'a{color:red}div{color:blue}a{color:red}', - 'div{color:#00f}a{color:red}' - ], - 'duplicates - different contexts': [ - 'a{color:red}div{color:blue}@media screen{a{color:red}}', - 'a{color:red}div{color:#00f}@media screen{a{color:red}}' - ], 'adjacent': [ 'a{color:red}a{display:block;width:100px}div{color:#fff}', 'a{color:red;display:block;width:100px}div{color:#fff}' @@ -257,14 +249,6 @@ vows.describe('advanced optimizer') 'a{color:red;color:red}', 'a{color:red;color:red}' ], - 'duplicates - same context': [ - 'a{color:red}div{color:blue}a{color:red}', - 'a{color:red}div{color:#00f}a{color:red}' - ], - 'duplicates - different contexts': [ - 'a{color:red}div{color:blue}@media screen{a{color:red}}', - 'a{color:red}div{color:#00f}@media screen{a{color:red}}' - ], 'adjacent': [ 'a{color:red}a{display:block;width:100px}div{color:#fff}', 'a{color:red}a{display:block;width:100px}div{color:#fff}' diff --git a/test/selectors/remove-duplicates-test.js b/test/selectors/remove-duplicates-test.js new file mode 100644 index 00000000..6886e9cd --- /dev/null +++ b/test/selectors/remove-duplicates-test.js @@ -0,0 +1,57 @@ +var vows = require('vows'); +var optimizerContext = require('../test-helper').optimizerContext; + +vows.describe('remove duplicates') + .addBatch( + optimizerContext('advanced on', { + 'same context': [ + 'a{color:red}div{color:blue}a{color:red}', + 'div{color:#00f}a{color:red}' + ], + 'different contexts': [ + 'a{color:red}div{color:blue}@media screen{a{color:red}}', + 'a{color:red}div{color:#00f}@media screen{a{color:red}}' + ], + 'of two successive selectors': [ + 'a{color:red}a{color:red}', + 'a{color:red}' + ], + 'of two successive selectors with different body': [ + 'a{color:red}a{display:block}', + 'a{color:red;display:block}' + ], + 'of many successive selectors': [ + 'a{color:red}a{color:red}a{color:red}a{color:red}', + 'a{color:red}' + ], + 'of two non-successive selectors': [ + 'a{color:red}p{color:#fff}a{color:red}', + 'p{color:#fff}a{color:red}' + ], + 'of many non-successive selectors': [ + 'div{width:100%}a{color:red}a{color:red}p{color:#fff}div{width:100%}ol{margin:0}p{color:#fff}', + 'a{color:red}div{width:100%}ol{margin:0}p{color:#fff}' + ], + 'with global and media scope': [ + 'a{color:red}@media screen{a{color:red}p{width:100px}a{color:red}}', + 'a{color:red}@media screen{p{width:100px}a{color:red}}' + ], + 'with two media scopes': [ + '@media (min-width:100px){a{color:red}}@media screen{a{color:red}p{width:100px}a{color:red}}', + '@media (min-width:100px){a{color:red}}@media screen{p{width:100px}a{color:red}}' + ] + }) + ) + .addBatch( + optimizerContext('advanced off', { + 'same context': [ + 'a{color:red}div{color:blue}a{color:red}', + 'a{color:red}div{color:#00f}a{color:red}' + ], + 'different contexts': [ + 'a{color:red}div{color:blue}@media screen{a{color:red}}', + 'a{color:red}div{color:#00f}@media screen{a{color:red}}' + ], + }, { advanced: false }) + ) + .export(module);