From 18b87f36b8fa04a8fbb5a87f1ec6c809bd6626f0 Mon Sep 17 00:00:00 2001 From: GoalSmashers Date: Mon, 18 Nov 2013 10:43:18 +0100 Subject: [PATCH] Fixes #160 - re-runs advanced optimizations until no changes are detected. * That's because merging adjacent selectors can lead to new optimization opportunities. --- History.md | 1 + lib/selectors/optimizer.js | 44 +++++++++++++++++++++++++++++++++----- test/data/big-min.css | 5 ++--- test/unit-test.js | 6 ++++++ 4 files changed, 48 insertions(+), 8 deletions(-) diff --git a/History.md b/History.md index 385cdb59..36b1c915 100644 --- a/History.md +++ b/History.md @@ -2,6 +2,7 @@ ================== * Skips empty removal if advanced processing is enabled. +* Fixed issue [#160](https://github.com/GoalSmashers/clean-css/issues/160) - re-runs optimizer until a clean pass. * Fixed issue [#161](https://github.com/GoalSmashers/clean-css/issues/161) - improves tokenizer performance. * Fixed issue [#163](https://github.com/GoalSmashers/clean-css/issues/163) - round pixels to 2nd decimal place. * Fixed issue [#165](https://github.com/GoalSmashers/clean-css/issues/165) - extra space after trailing parenthesis. diff --git a/lib/selectors/optimizer.js b/lib/selectors/optimizer.js index 5b018bea..ed683ea1 100644 --- a/lib/selectors/optimizer.js +++ b/lib/selectors/optimizer.js @@ -7,6 +7,8 @@ module.exports = function Optimizer(data, options) { 'ie8': /(\-moz\-|\-ms\-|\-o\-|\-webkit\-|:not|:target|:visited|:empty|:first\-of|:last|:nth|:only|:root)/ }; + var minificationsMade = []; + var propertyOptimizer = new PropertyOptimizer(); var cleanUpSelector = function(selectors) { @@ -54,6 +56,8 @@ module.exports = function Optimizer(data, options) { for (var j = 0, n = forRemoval.length; j < n; j++) { tokens.splice(forRemoval[j] - j, 1); } + + minificationsMade.unshift(forRemoval.length > 0); }; var mergeAdjacent = function(tokens) { @@ -81,12 +85,15 @@ module.exports = function Optimizer(data, options) { for (var j = 0, m = forRemoval.length; j < m; j++) { tokens.splice(forRemoval[j] - j, 1); } + + minificationsMade.unshift(forRemoval.length > 0); }; var reduceNonAdjacent = function(tokens) { var matched = {}; var matchedMoreThanOnce = []; var partiallyReduced = []; + var reduced = false; var token, selector, selectors; for (var i = 0, l = tokens.length; i < l; i++) { @@ -147,7 +154,9 @@ module.exports = function Optimizer(data, options) { } if (token.selector == selector) { - token.body = reducedBody.join(';'); + var joinedBody = reducedBody.join(';'); + reduced = reduced || (token.body != joinedBody); + token.body = joinedBody; } else { token._partials = token._partials || []; token._partials.push(reducedBody.join(';')); @@ -173,15 +182,26 @@ module.exports = function Optimizer(data, options) { break; } - if (k == n) + if (k == n) { token.body = newBody; + reduced = reduced || true; + } } delete token._partials; } + + minificationsMade.unshift(reduced); }; var optimize = function(tokens) { + var firstRun = true; + var noChanges = function() { + return !firstRun && + minificationsMade[0] === false && + minificationsMade[1] === false; + }; + tokens = Array.isArray(tokens) ? tokens : [tokens]; for (var i = 0, l = tokens.length; i < l; i++) { var token = tokens[i]; @@ -194,9 +214,23 @@ module.exports = function Optimizer(data, options) { } } - removeDuplicates(tokens); - mergeAdjacent(tokens); - reduceNonAdjacent(tokens); + // Run until 2 last operations do not yield any changes + minificationsMade = []; + while (true) { + if (noChanges()) + break; + removeDuplicates(tokens); + + if (noChanges()) + break; + mergeAdjacent(tokens); + + if (noChanges()) + break; + reduceNonAdjacent(tokens); + + firstRun = false; + } }; var rebuild = function(tokens) { diff --git a/test/data/big-min.css b/test/data/big-min.css index e2b184bd..26d3140f 100644 --- a/test/data/big-min.css +++ b/test/data/big-min.css @@ -2526,8 +2526,7 @@ body.access-ess #page-paywall .content .arguments .arg{float:none;margin:auto} .site-liberation .w14unit .block-call-items .mini-tpl .big{width:392px;height:261px} .site-liberation .w14unit .block-call-items .mini-tpl .pano{width:392px;height:85px} .site-liberation .w11unit .block-call-items .mini-tpl .pano{width:300px;height:65px} -.site-liberation .block-call-items .tpl-visual-square-left h5,.site-liberation .block-call-items .tpl-visual-square-left-arround h5{text-transform:none} -.site-liberation .block-call-items .tpl-visual-square-left h5,.site-liberation .block-call-items .tpl-visual-square-left-arround h5{font-family:Georgia,"Times New Roman",Times,serif;font-size:13px;font-weight:700} +.site-liberation .block-call-items .tpl-visual-square-left h5,.site-liberation .block-call-items .tpl-visual-square-left-arround h5{text-transform:none;font-family:Georgia,"Times New Roman",Times,serif;font-size:13px;font-weight:700} .site-liberation .block-call-items .mini-tpl h4{font-size:16px;font-weight:700} .site-liberation .block-call-items .tpl-flash-news-29unit,.site-liberation .block-call-items .tpl-flash-news-29unit:last-of-type{border-bottom:1px dotted #ddd;margin:0 0 14px;clear:both} .site-liberation .block-call-items .tpl-flash-news-29unit .big{margin-right:28px} @@ -2983,4 +2982,4 @@ html.js body.dummy div#mainContent div#core-liberation div.col9 div.block div.bl html.js body.dummy div#mainContent div#core-liberation div.col9 div.block div.block-content div.favorites-frontpages div.col-left div.cartridge{width:388px} html.js body.dummy div#mainContent div#core-liberation div.col9 div.block div.block-content div.favorites-folders div.block-call-items div.block-content div.mini-tpl div.cartridge{margin-left:0;width:129px} html.js body.dummy div#mainContent div#core-liberation div.col7 div.block-call-items div.block-content div.mini-tpl div.folder-on-demand div.object-content{margin-right:0;min-height:0;border-bottom:0} -html.js body.dummy div#mainContent div#core-liberation div.col7 div.block-call-items div.block-content div.mini-tpl div.folder-on-demand{border-bottom:1px solid #E7E7E7} +html.js body.dummy div#mainContent div#core-liberation div.col7 div.block-call-items div.block-content div.mini-tpl div.folder-on-demand{border-bottom:1px solid #E7E7E7} \ No newline at end of file diff --git a/test/unit-test.js b/test/unit-test.js index 5fa9bebd..68253763 100644 --- a/test/unit-test.js +++ b/test/unit-test.js @@ -1293,6 +1293,12 @@ title']{display:block}", 'p{color:red}.one{font-size:12px;text-shadow:none}' ] }), + 'rerun optimizers': cssContext({ + 'selectors reducible once': [ + '.one{color:red;margin:0}.two{color:red}.one{margin:0}', + '.one,.two{color:red}.one{margin:0}' + ] + }), 'same bodies': cssContext({ 'of two non-adjacent selectors': '.one{color:red}.two{color:#00f}.three{color:red}', 'of two adjacent single selectors': [ -- 2.34.1