Fixes #160 - re-runs advanced optimizations until no changes are detected.
authorGoalSmashers <jakub@goalsmashers.com>
Mon, 18 Nov 2013 09:43:18 +0000 (10:43 +0100)
committerGoalSmashers <jakub@goalsmashers.com>
Mon, 18 Nov 2013 10:04:11 +0000 (11:04 +0100)
* That's because merging adjacent selectors can lead to new optimization opportunities.

History.md
lib/selectors/optimizer.js
test/data/big-min.css
test/unit-test.js

index 385cdb5..36b1c91 100644 (file)
@@ -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.
index 5b018be..ed683ea 100644 (file)
@@ -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) {
index e2b184b..26d3140 100644 (file)
@@ -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
index 5fa9beb..6825376 100644 (file)
@@ -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': [