From: Jakub Pawlowicz Date: Sun, 21 Jun 2015 10:53:03 +0000 (+0100) Subject: Extracts 'merge adjacent' optimization into a module. X-Git-Url: https://git.ndcode.org/public/gitweb.cgi?a=commitdiff_plain;h=4ec3a7b87f5b2bc8ef55374af9af4ac35a9280bc;p=clean-css.git Extracts 'merge adjacent' optimization into a module. --- diff --git a/lib/selectors/advanced.js b/lib/selectors/advanced.js index 0bde05b7..ef6c4e74 100644 --- a/lib/selectors/advanced.js +++ b/lib/selectors/advanced.js @@ -9,6 +9,7 @@ var stringifyBody = require('../stringifier/one-time').body; var stringifySelectors = require('../stringifier/one-time').selectors; var removeDuplicates = require('./remove-duplicates'); +var mergeAdjacent = require('./merge-adjacent'); function AdvancedOptimizer(options, context) { this.options = options; @@ -27,33 +28,6 @@ AdvancedOptimizer.prototype.isSpecial = function (selector) { return this.options.compatibility.selectors.special.test(selector); }; -AdvancedOptimizer.prototype.mergeAdjacent = function (tokens) { - var lastToken = [null, [], []]; - var adjacentSpace = this.options.compatibility.selectors.adjacentSpace; - - for (var i = 0, l = tokens.length; i < l; i++) { - var token = tokens[i]; - - if (token[0] != 'selector') { - lastToken = [null, [], []]; - continue; - } - - if (lastToken[0] == 'selector' && stringifySelectors(token[1]) == stringifySelectors(lastToken[1])) { - var joinAt = [lastToken[2].length]; - Array.prototype.push.apply(lastToken[2], token[2]); - optimizeProperties(token[1], lastToken[2], joinAt, true, this.options, this.validator); - token[2] = []; - } else if (lastToken[0] == 'selector' && stringifyBody(token[2]) == stringifyBody(lastToken[2]) && - !this.isSpecial(stringifySelectors(token[1])) && !this.isSpecial(stringifySelectors(lastToken[1]))) { - lastToken[1] = CleanUp.selectors(lastToken[1].concat(token[1]), false, adjacentSpace); - token[2] = []; - } else { - lastToken = token; - } - } -}; - AdvancedOptimizer.prototype.reduceNonAdjacent = function (tokens) { var candidates = {}; var repeated = []; @@ -779,7 +753,7 @@ AdvancedOptimizer.prototype.optimize = function (tokens) { recursivelyOptimizeProperties(tokens, self.options, self.validator); removeDuplicates(tokens); - self.mergeAdjacent(tokens); + mergeAdjacent(tokens, self.options, self.validator); self.reduceNonAdjacent(tokens); self.mergeNonAdjacentBySelector(tokens); @@ -787,7 +761,7 @@ AdvancedOptimizer.prototype.optimize = function (tokens) { if (self.options.restructuring && withRestructuring) { self.restructure(tokens); - self.mergeAdjacent(tokens); + mergeAdjacent(tokens, self.options, self.validator); } if (self.options.mediaMerging) { diff --git a/lib/selectors/is-special.js b/lib/selectors/is-special.js new file mode 100644 index 00000000..96315141 --- /dev/null +++ b/lib/selectors/is-special.js @@ -0,0 +1,5 @@ +function isSpecial(options, selector) { + return options.compatibility.selectors.special.test(selector); +} + +module.exports = isSpecial; diff --git a/lib/selectors/merge-adjacent.js b/lib/selectors/merge-adjacent.js new file mode 100644 index 00000000..1485707c --- /dev/null +++ b/lib/selectors/merge-adjacent.js @@ -0,0 +1,35 @@ +var optimizeProperties = require('../properties/optimizer'); + +var stringifyBody = require('../stringifier/one-time').body; +var stringifySelectors = require('../stringifier/one-time').selectors; +var cleanUpSelectors = require('./clean-up').selectors; +var isSpecial = require('./is-special'); + +function mergeAdjacent(tokens, options, validator) { + var lastToken = [null, [], []]; + var adjacentSpace = options.compatibility.selectors.adjacentSpace; + + for (var i = 0, l = tokens.length; i < l; i++) { + var token = tokens[i]; + + if (token[0] != 'selector') { + lastToken = [null, [], []]; + continue; + } + + if (lastToken[0] == 'selector' && stringifySelectors(token[1]) == stringifySelectors(lastToken[1])) { + var joinAt = [lastToken[2].length]; + Array.prototype.push.apply(lastToken[2], token[2]); + optimizeProperties(token[1], lastToken[2], joinAt, true, options, validator); + token[2] = []; + } else if (lastToken[0] == 'selector' && stringifyBody(token[2]) == stringifyBody(lastToken[2]) && + !isSpecial(options, stringifySelectors(token[1])) && !isSpecial(options, stringifySelectors(lastToken[1]))) { + lastToken[1] = cleanUpSelectors(lastToken[1].concat(token[1]), false, adjacentSpace); + token[2] = []; + } else { + lastToken = token; + } + } +} + +module.exports = mergeAdjacent; diff --git a/test/integration-test.js b/test/integration-test.js index 9c9bc2a5..eb8487d7 100644 --- a/test/integration-test.js +++ b/test/integration-test.js @@ -2158,62 +2158,6 @@ vows.describe('integration tests') ] }, { aggressiveMerging: false }) ) - .addBatch( - optimizerContext('same selectors', { - 'of two non-adjacent selectors': [ - '.one{color:red}.two{color:#00f}.one{font-weight:700}', - '.one{color:red;font-weight:700}.two{color:#00f}' - ], - 'of two adjacent single selectors': [ - '.one{color:red}.one{font-weight:700}', - '.one{color:red;font-weight:700}' - ], - 'of three adjacent single selectors': [ - '.one{color:red}.one{font-weight:700}.one{font-size:12px}', - '.one{color:red;font-weight:700;font-size:12px}' - ], - 'of two adjacent single, complex selectors': [ - '#box>.one{color:red}#box>.one{font-weight:700}', - '#box>.one{color:red;font-weight:700}' - ], - 'of two adjacent multiple, complex selectors': [ - '#box>.one,.zero{color:red}#box>.one,.zero{font-weight:700}', - '#box>.one,.zero{color:red;font-weight:700}' - ], - 'of two adjacent selectors with duplicate properties #1': [ - '.one{color:red}.one{color:#fff}', - '.one{color:#fff}' - ], - 'of two adjacent selectors with duplicate properties #2': [ - '.one{color:red;font-weight:bold}.one{color:#fff;font-weight:400}', - '.one{color:#fff;font-weight:400}' - ], - 'of two adjacent complex selectors with different selector order': [ - '.one,.two{color:red}.two,.one{line-height:1em}', - '.one,.two{color:red;line-height:1em}' - ], - 'two adjacent with hex color definitions': [ - 'a:link,a:visited{color:#fff}.one{display:block}a:link,a:visited{color:red}', - '.one{display:block}a:link,a:visited{color:red}' - ], - 'in two passes': [ - 'a{color:red}a{background:red}b{color:red}b{background:red}', - 'a,b{color:red;background:red}' - ], - 'when overriden with a browser specific selector': [ - 'a{color:red}::-webkit-scrollbar,a{color:#fff}', - 'a{color:red}::-webkit-scrollbar,a{color:#fff}' - ], - 'two same selectors over a block': [ - '.one{color:red}@media print{.two{display:block}}.one{display:none}', - '@media print{.two{display:block}}.one{color:red;display:none}' - ], - 'two same bodies over a block': [ - '.one{color:red}@media print{.two{display:block}}.three{color:red}', - '.one,.three{color:red}@media print{.two{display:block}}' - ] - }) - ) .addBatch( optimizerContext('same non-adjacent selectors', { 'with one redefined property': [ diff --git a/test/selectors/advanced-test.js b/test/selectors/advanced-test.js index 04152dea..f47b11c8 100644 --- a/test/selectors/advanced-test.js +++ b/test/selectors/advanced-test.js @@ -209,10 +209,6 @@ vows.describe('advanced optimizer') 'a{color:red;color:red}', '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}' - ], 'non-adjacent': [ 'a{color:red;display:block}.one{margin:12px}a{color:#fff;margin:2px}', '.one{margin:12px}a{display:block;color:#fff;margin:2px}' @@ -249,10 +245,6 @@ vows.describe('advanced optimizer') 'a{color:red;color:red}', 'a{color:red;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}' - ], 'non-adjacent': [ 'a{color:red;display:block}.one{font-size:12px}a{color:#fff;margin:2px}', 'a{color:red;display:block}.one{font-size:12px}a{color:#fff;margin:2px}' diff --git a/test/selectors/merge-adjacent-test.js b/test/selectors/merge-adjacent-test.js new file mode 100644 index 00000000..fe29de70 --- /dev/null +++ b/test/selectors/merge-adjacent-test.js @@ -0,0 +1,73 @@ +var vows = require('vows'); +var optimizerContext = require('../test-helper').optimizerContext; + +vows.describe('remove duplicates') + .addBatch( + optimizerContext('advanced on', { + 'same context': [ + 'a{color:red}a{display:block;width:100px}div{color:#fff}', + 'a{color:red;display:block;width:100px}div{color:#fff}' + ], + 'of two non-adjacent selectors': [ + '.one{color:red}.two{color:#00f}.one{font-weight:700}', + '.one{color:red;font-weight:700}.two{color:#00f}' + ], + 'of two adjacent single selectors': [ + '.one{color:red}.one{font-weight:700}', + '.one{color:red;font-weight:700}' + ], + 'of three adjacent single selectors': [ + '.one{color:red}.one{font-weight:700}.one{font-size:12px}', + '.one{color:red;font-weight:700;font-size:12px}' + ], + 'of two adjacent single, complex selectors': [ + '#box>.one{color:red}#box>.one{font-weight:700}', + '#box>.one{color:red;font-weight:700}' + ], + 'of two adjacent multiple, complex selectors': [ + '#box>.one,.zero{color:red}#box>.one,.zero{font-weight:700}', + '#box>.one,.zero{color:red;font-weight:700}' + ], + 'of two adjacent selectors with duplicate properties #1': [ + '.one{color:red}.one{color:#fff}', + '.one{color:#fff}' + ], + 'of two adjacent selectors with duplicate properties #2': [ + '.one{color:red;font-weight:bold}.one{color:#fff;font-weight:400}', + '.one{color:#fff;font-weight:400}' + ], + 'of two adjacent complex selectors with different selector order': [ + '.one,.two{color:red}.two,.one{line-height:1em}', + '.one,.two{color:red;line-height:1em}' + ], + 'two adjacent with hex color definitions': [ + 'a:link,a:visited{color:#fff}.one{display:block}a:link,a:visited{color:red}', + '.one{display:block}a:link,a:visited{color:red}' + ], + 'in two passes': [ + 'a{color:red}a{background:red}b{color:red}b{background:red}', + 'a,b{color:red;background:red}' + ], + 'when overriden with a browser specific selector': [ + 'a{color:red}::-webkit-scrollbar,a{color:#fff}', + 'a{color:red}::-webkit-scrollbar,a{color:#fff}' + ], + 'two same selectors over a block': [ + '.one{color:red}@media print{.two{display:block}}.one{display:none}', + '@media print{.two{display:block}}.one{color:red;display:none}' + ], + 'two same bodies over a block': [ + '.one{color:red}@media print{.two{display:block}}.three{color:red}', + '.one,.three{color:red}@media print{.two{display:block}}' + ] + }) + ) + .addBatch( + optimizerContext('advanced off', { + 'same context': [ + 'a{color:red}a{display:block;width:100px}div{color:#fff}', + 'a{color:red}a{display:block;width:100px}div{color:#fff}' + ], + }, { advanced: false }) + ) + .export(module);