From 5a5e531e37bcc45b825db4a4571931afc4d46ce9 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowicz Date: Sun, 21 Jun 2015 15:03:44 +0100 Subject: [PATCH] Extracts 'merge media queries' optimization into a module. --- lib/selectors/advanced.js | 51 +----------- lib/selectors/merge-media-queries.js | 49 ++++++++++++ test/selectors/merge-media-queries-test.js | 93 ++++++++++++++++++++++ 3 files changed, 144 insertions(+), 49 deletions(-) create mode 100644 lib/selectors/merge-media-queries.js create mode 100644 test/selectors/merge-media-queries-test.js diff --git a/lib/selectors/advanced.js b/lib/selectors/advanced.js index 6487106e..48e55fe9 100644 --- a/lib/selectors/advanced.js +++ b/lib/selectors/advanced.js @@ -1,8 +1,5 @@ var optimizeProperties = require('../properties/optimizer'); -var extractProperties = require('./extractor'); -var canReorder = require('./reorderable').canReorder; - var removeDuplicates = require('./remove-duplicates'); var mergeAdjacent = require('./merge-adjacent'); var reduceNonAdjacent = require('./reduce-non-adjacent'); @@ -10,57 +7,13 @@ var mergeNonAdjacentBySelector = require('./merge-non-adjacent-by-selector'); var mergeNonAdjacentByBody = require('./merge-non-adjacent-by-body'); var restructure = require('./restructure'); var removeDuplicateMediaQueries = require('./remove-duplicate-media-queries'); +var mergeMediaQueries = require('./merge-media-queries'); function AdvancedOptimizer(options, context) { this.options = options; this.validator = context.validator; } -AdvancedOptimizer.prototype.mergeMediaQueries = function (tokens) { - var candidates = {}; - var reduced = []; - - for (var i = tokens.length - 1; i >= 0; i--) { - var token = tokens[i]; - if (token[0] != 'block') - continue; - - var candidate = candidates[token[1][0]]; - if (!candidate) { - candidate = []; - candidates[token[1][0]] = candidate; - } - - candidate.push(i); - } - - for (var name in candidates) { - var positions = candidates[name]; - - positionLoop: - for (var j = positions.length - 1; j > 0; j--) { - var source = tokens[positions[j]]; - var target = tokens[positions[j - 1]]; - var movedProperties = extractProperties(source); - - for (var k = positions[j] + 1; k < positions[j - 1]; k++) { - var traversedProperties = extractProperties(tokens[k]); - - // moved then traversed as we move @media towards the end - if (!canReorder(movedProperties, traversedProperties)) - continue positionLoop; - } - - target[2] = source[2].concat(target[2]); - source[2] = []; - - reduced.push(target); - } - } - - return reduced; -}; - AdvancedOptimizer.prototype.removeEmpty = function (tokens) { for (var i = 0, l = tokens.length; i < l; i++) { var token = tokens[i]; @@ -124,7 +77,7 @@ AdvancedOptimizer.prototype.optimize = function (tokens) { if (self.options.mediaMerging) { removeDuplicateMediaQueries(tokens); - var reduced = self.mergeMediaQueries(tokens); + var reduced = mergeMediaQueries(tokens); for (var i = reduced.length - 1; i >= 0; i--) { _optimize(reduced[i][2]); } diff --git a/lib/selectors/merge-media-queries.js b/lib/selectors/merge-media-queries.js new file mode 100644 index 00000000..89ea5720 --- /dev/null +++ b/lib/selectors/merge-media-queries.js @@ -0,0 +1,49 @@ +var canReorder = require('./reorderable').canReorder; +var extractProperties = require('./extractor'); + +function mergeMediaQueries(tokens) { + var candidates = {}; + var reduced = []; + + for (var i = tokens.length - 1; i >= 0; i--) { + var token = tokens[i]; + if (token[0] != 'block') + continue; + + var candidate = candidates[token[1][0]]; + if (!candidate) { + candidate = []; + candidates[token[1][0]] = candidate; + } + + candidate.push(i); + } + + for (var name in candidates) { + var positions = candidates[name]; + + positionLoop: + for (var j = positions.length - 1; j > 0; j--) { + var source = tokens[positions[j]]; + var target = tokens[positions[j - 1]]; + var movedProperties = extractProperties(source); + + for (var k = positions[j] + 1; k < positions[j - 1]; k++) { + var traversedProperties = extractProperties(tokens[k]); + + // moved then traversed as we move @media towards the end + if (!canReorder(movedProperties, traversedProperties)) + continue positionLoop; + } + + target[2] = source[2].concat(target[2]); + source[2] = []; + + reduced.push(target); + } + } + + return reduced; +} + +module.exports = mergeMediaQueries; diff --git a/test/selectors/merge-media-queries-test.js b/test/selectors/merge-media-queries-test.js new file mode 100644 index 00000000..86a3bb2d --- /dev/null +++ b/test/selectors/merge-media-queries-test.js @@ -0,0 +1,93 @@ +var vows = require('vows'); +var optimizerContext = require('../test-helper').optimizerContext; + +vows.describe('merge media queries') + .addBatch( + optimizerContext('different ones', { + 'different ones': [ + '@media screen{a{color:red}}@media print{div{display:block}}', + '@media screen{a{color:red}}@media print{div{display:block}}' + ], + 'other than @media': [ + '@font-face{font-family:A}@font-face{font-family:B}', + '@font-face{font-family:A}@font-face{font-family:B}' + ], + 'with empty selector': [ + '@media screen{a{color:red}div{}}', + '@media screen{a{color:red}}' + ] + }) + ) + .addBatch( + optimizerContext('adjacent', { + 'same two adjacent': [ + '@media screen{a{color:red}}@media screen{div{display:block}}', + '@media screen{a{color:red}div{display:block}}' + ], + 'same three adjacent': [ + '@media screen{a{color:red}}@media screen{div{display:block}}@media screen{body{width:100%}}', + '@media screen{a{color:red}div{display:block}body{width:100%}}' + ], + 'same two with selectors in between': [ + '@media screen{a{color:red}}body{width:100%}.one{height:100px}@media screen{div{display:block}}', + 'body{width:100%}.one{height:100px}@media screen{a{color:red}div{display:block}}' + ], + 'same two with other @media in between': [ + '@media screen{a{color:red}}@media (min-width:1024px){body{width:100%}}@media screen{div{display:block}}', + '@media (min-width:1024px){body{width:100%}}@media screen{a{color:red}div{display:block}}' + ], + 'same two with breaking properties in between': [ + '@media screen{a{color:red}}.one{color:#00f}@media screen{div{display:block}}', + '@media screen{a{color:red}}.one{color:#00f}@media screen{div{display:block}}' + ], + 'same two with breaking @media in between': [ + '@media screen{a{color:red}}@media (min-width:1024px){.one{color:#00f}}@media screen{div{display:block}}', + '@media screen{a{color:red}}@media (min-width:1024px){.one{color:#00f}}@media screen{div{display:block}}' + ], + 'same two with breaking nested @media in between': [ + '@media screen{a{color:red}}@media (min-width:1024px){@media screen{.one{color:#00f}}}@media screen{div{display:block}}', + '@media screen{a{color:red}}@media (min-width:1024px){@media screen{.one{color:#00f}}}@media screen{div{display:block}}' + ], + 'intermixed': [ + '@media screen{a{color:red}}@media (min-width:1024px){p{width:100%}}@media screen{div{display:block}}@media (min-width:1024px){body{height:100%}}', + '@media screen{a{color:red}div{display:block}}@media (min-width:1024px){p{width:100%}body{height:100%}}' + ], + 'same two with overriding shorthand in between': [ + '@media screen{a{font-size:10px}}@media (min-width:1024px){.one{font:12px Helvetica}}@media screen{div{display:block}}', + '@media screen{a{font-size:10px}}@media (min-width:1024px){.one{font:12px Helvetica}}@media screen{div{display:block}}' + ], + 'same two with different component property in between': [ + '@media screen{a{font-size:10px}}@media (min-width:1024px){.one{font-weight:700}}@media screen{div{display:block}}', + '@media (min-width:1024px){.one{font-weight:700}}@media screen{a{font-size:10px}div{display:block}}' + ], + 'same two with same values as moved in between': [ + '@media screen{a{color:red}}@media (min-width:1024px){.one{color:red}}@media screen{div{display:block}}', + '@media (min-width:1024px){.one{color:red}}@media screen{a{color:red}div{display:block}}' + ], + 'further optimizations': [ + '@media screen{a{color:red}}@media screen{a{display:block}}', + '@media screen{a{color:red;display:block}}' + ], + 'with comments': [ + '@media screen{a{color:red}}/*! a comment */@media screen{a{display:block}}', + '/*! a comment */@media screen{a{color:red;display:block}}' + ] + }) + ) + .addBatch( + optimizerContext('advanced off', { + 'keeps content same': [ + '@media screen{a{color:red}}@media screen{a{display:block}}', + '@media screen{a{color:red}}@media screen{a{display:block}}' + ] + }, { advanced: false }) + ) + .addBatch( + optimizerContext('media merging off', { + 'keeps content same': [ + '@media screen{a{color:red}}@media screen{a{display:block}}', + '@media screen{a{color:red}}@media screen{a{display:block}}' + ] + }, { mediaMerging: false }) + ) + .export(module); -- 2.34.1