Fixes #528 - better support for IE<9 hacks.
authorJakub Pawlowicz <contact@jakubpawlowicz.com>
Wed, 15 Apr 2015 07:25:02 +0000 (08:25 +0100)
committerJakub Pawlowicz <contact@jakubpawlowicz.com>
Wed, 15 Apr 2015 07:31:01 +0000 (08:31 +0100)
Adds better hack handling in property overriding and compacting too.

Also normalizes all suffix hacks to '\0' as IE treats them equally.

14 files changed:
History.md
lib/properties/optimizer.js
lib/properties/override-compactor.js
lib/properties/shorthand-compactor.js
lib/selectors/optimization-metadata.js
lib/selectors/optimizers/simple.js
lib/stringifier/helpers.js
test/integration-test.js
test/properties/optimizer-test.js
test/properties/override-compacting-test.js
test/properties/shorthand-compacting-test.js
test/properties/wrap-for-optimizing-test.js
test/selectors/optimization-metadata-test.js
test/selectors/optimizers/simple-test.js

index cec96cc..1b4fd7c 100644 (file)
@@ -19,6 +19,7 @@
 * Fixed issue [#508](https://github.com/jakubpawlowicz/clean-css/issues/508) - removing duplicate media queries.
 * Fixed issue [#521](https://github.com/jakubpawlowicz/clean-css/issues/521) - unit optimizations inside `calc()`.
 * Fixed issue [#526](https://github.com/jakubpawlowicz/clean-css/issues/526) - shorthand overriding into a function.
+* Fixed issue [#528](https://github.com/jakubpawlowicz/clean-css/issues/528) - better support for IE<9 hacks.
 
 [3.1.9 / 2015-04-04](https://github.com/jakubpawlowicz/clean-css/compare/v3.1.8...v3.1.9)
 ==================
index f777a52..55cb387 100644 (file)
@@ -143,6 +143,9 @@ function _optimize(properties, mergeAdjacent, aggressiveMerging, validator) {
         if (!wasImportant && (wasHack && !isHack || !wasHack && isHack))
           continue;
 
+        if (wasImportant && (isHack == 'star' || isHack == 'underscore'))
+          continue;
+
         if (!wasHack && !isHack && !longhandToShorthand && canOverride && !canOverride(toRemove, property, validator))
           continue;
 
index 0942dc0..8a7e07d 100644 (file)
@@ -200,6 +200,9 @@ function compactOverrides(properties, compatibility, validator) {
       if (left.unused || right.unused)
         continue;
 
+      if (left.hack && !right.hack || !left.hack && right.hack)
+        continue;
+
       if (hasInherit(right))
         continue;
 
index 59637cb..be22088 100644 (file)
@@ -110,6 +110,9 @@ function compactShortands(properties, sourceMaps, validator) {
     if (property.unused)
       continue;
 
+    if (property.hack)
+      continue;
+
     var descriptor = compactable[property.name];
     if (!descriptor || !descriptor.componentOf)
       continue;
index b05296c..d5b469e 100644 (file)
@@ -1,6 +1,6 @@
 // TODO: we should wrap it under `wrap for optimizing`
 
-var BACKSLASH_HACK = '\\9';
+var BACKSLASH_HACK = '\\';
 var IMPORTANT = '!important';
 var STAR_HACK = '*';
 var UNDERSCORE_HACK = '_';
@@ -31,16 +31,25 @@ function addToProperty(property) {
   var name = property[0][0];
   var lastValue = property[property.length - 1];
   var isImportant = lastValue[0].indexOf(IMPORTANT) > 0;
-  var isHack = name[0] == UNDERSCORE_HACK ||
-    name[0] == STAR_HACK ||
-    lastValue[0].indexOf(BACKSLASH_HACK) > 0 && lastValue[0].indexOf(BACKSLASH_HACK) == lastValue[0].length - BACKSLASH_HACK.length;
+  var hackType = false;
+
+  if (name[0] == UNDERSCORE_HACK) {
+    property[0][0] = name.substring(1);
+    hackType = 'underscore';
+  } else if (name[0] == STAR_HACK) {
+    property[0][0] = name.substring(1);
+    hackType = 'star';
+  } else if (lastValue[0].indexOf(BACKSLASH_HACK) > 0 && lastValue[0].indexOf(BACKSLASH_HACK) == lastValue[0].length - BACKSLASH_HACK.length - 1) {
+    lastValue[0] = lastValue[0].substring(0, lastValue[0].length - BACKSLASH_HACK.length - 1);
+    hackType = 'suffix';
+  }
 
   // TODO: this should be done at tokenization step
   // with adding importance info
   if (isImportant)
     lastValue[0] = lastValue[0].substring(0, lastValue[0].length - IMPORTANT.length);
 
-  property[0].splice(1, 0, isImportant, isHack);
+  property[0].splice(1, 0, isImportant, hackType);
 }
 
 module.exports = addOptimizationMetadata;
index f4f47bb..342113f 100644 (file)
@@ -198,9 +198,9 @@ function optimizeBody(properties, options) {
 
     name = property[0][0];
 
-    if (property[0][2]) {
-      var isPrefixHack = name[0] == '_';
-      if (isPrefixHack && !options.compatibility.properties.iePrefixHack || !isPrefixHack && !options.compatibility.properties.ieSuffixHack)
+    var hackType = property[0][2];
+    if (hackType) {
+      if ((hackType == 'star' || hackType == 'underscore') && !options.compatibility.properties.iePrefixHack || hackType == 'suffix' && !options.compatibility.properties.ieSuffixHack)
         unused = true;
     }
 
index b8b3dc8..ffb6b81 100644 (file)
@@ -1,5 +1,9 @@
 var lineBreak = require('os').EOL;
 
+var STAR_HACK = '*';
+var SUFFIX_HACK = '\\0';
+var UNDERSCORE_HACK = '_';
+
 function hasMoreProperties(tokens, index) {
   for (var i = index, l = tokens.length; i < l; i++) {
     if (typeof tokens[i] != 'string')
@@ -41,6 +45,14 @@ function inSpecialContext(token, valueIndex, context) {
     afterComma(token, valueIndex);
 }
 
+function storePrefixHack(name, context) {
+  var hackType = name[2];
+  if (hackType == 'underscore')
+    context.store(UNDERSCORE_HACK, context);
+  else if (hackType == 'star')
+    context.store(STAR_HACK, context);
+}
+
 function selectors(tokens, context) {
   var store = context.store;
 
@@ -65,6 +77,7 @@ function property(tokens, position, isLast, context) {
   if (typeof token == 'string') {
     store(token, context);
   } else {
+    storePrefixHack(token[0], context);
     store(token[0], context);
     store(':', context);
     value(tokens, position, isLast, context);
@@ -75,10 +88,13 @@ function value(tokens, position, isLast, context) {
   var store = context.store;
   var token = tokens[position];
   var isImportant = token[0][1];
+  var hackType = token[0][2];
 
   for (var j = 1, m = token.length; j < m; j++) {
     store(token[j], context);
 
+    if (j == m - 1 && hackType == 'suffix')
+      store(SUFFIX_HACK, context);
     if (j == m - 1 && isImportant)
       store('!important', context);
 
index ba9ef9d..ffe8689 100644 (file)
@@ -1121,16 +1121,10 @@ path")}',
   'IE hacks': cssContext({
     'star': 'a{*color:#fff}',
     'unserscore': 'a{_color:#fff}',
-    'backslash': 'a{color:#fff\\9}',
+    'backslash': 'a{color:#fff\\0}',
     'overriding by a star': 'a{color:red;display:block;*color:#fff}',
     'overriding by a unserscore': 'a{color:red;display:block;_color:#fff}',
-    'overriding by a backslash': 'a{color:red;display:block;color:#fff\\9}',
-    'overriding !important by a star': 'a{color:red!important;display:block;*color:#fff}',
-    'overriding !important by a unserscore': 'a{color:red!important;display:block;_color:#fff}',
-    'overriding !important by a backslash': [
-      'a{color:red!important;display:block;color:#fff\\9}',
-      'a{color:red!important;display:block}'
-    ],
+    'overriding by a backslash': 'a{color:red;display:block;color:#fff\\0}',
     'overriding a star': [
       'a{*color:red;display:block;*color:#fff}',
       'a{display:block;*color:#fff}'
@@ -1141,11 +1135,12 @@ path")}',
     ],
     'overriding a backslash': [
       'a{color:red\\9;display:block;color:#fff\\9}',
-      'a{display:block;color:#fff\\9}'
+      'a{display:block;color:#fff\\0}'
     ],
     'overriding a star by a non-ajacent selector': 'a{color:red}.one{color:#000}a{*color:#fff}',
     'overriding an underscore by a non-ajacent selector': 'a{color:red}.one{color:#000}a{_color:#fff}',
-    'overriding a backslash by a non-ajacent selector': 'a{color:red}.one{color:#fff}a{color:#fff\\9}',
+    'overriding a backslash by a non-ajacent selector': 'a{color:red}.one{color:#fff}a{color:#fff\\0}',
+    'preserving backslash in overriddable': 'a{border:1px solid #ccc\\0}',
     'keeps rgba(0,0,0,0)': 'a{color:rgba(0,0,0,0)}',
     'keeps rgba(255,255,255,0)': 'a{color:rgba(255,255,255,0)}',
     'keeps hsla(120,100%,50%,0)': 'a{color:hsla(120,100%,50%,0)}'
index bc10216..fbff5b7 100644 (file)
@@ -180,7 +180,7 @@ vows.describe(optimize)
         assert.deepEqual(_optimize(topic, false, true), [
           [['color', false , false], ['red']],
           [['display', false , false], ['none']],
-          [['color', false , true], ['#fff\\9']]
+          [['color', false , 'suffix'], ['#fff']]
         ]);
       }
     },
@@ -188,7 +188,7 @@ vows.describe(optimize)
       'topic': 'p{color:red\\9;display:none;color:#fff}',
       'into': function (topic) {
         assert.deepEqual(_optimize(topic, false, true), [
-          [['color', false , true], ['red\\9']],
+          [['color', false , 'suffix'], ['red']],
           [['display', false , false], ['none']],
           [['color', false , false], ['#fff']]
         ]);
@@ -199,7 +199,7 @@ vows.describe(optimize)
       'into': function (topic) {
         assert.deepEqual(_optimize(topic, false, true), [
           [['display', false , false], ['none']],
-          [['color', false , true], ['#fff\\9']]
+          [['color', false , 'suffix'], ['#fff']]
         ]);
       }
     }
index e643b9d..4268195 100644 (file)
@@ -524,4 +524,35 @@ vows.describe(optimize)
       }
     }
   })
+  .addBatch({
+    'overriding !important by a star hack': {
+      'topic': 'a{color:red!important;display:block;*color:#fff}',
+      'into': function (topic) {
+        assert.deepEqual(_optimize(topic), [
+          [['color', true , false], ['red']],
+          [['display', false , false], ['block']],
+          [['color', false , 'star'], ['#fff']]
+        ]);
+      }
+    },
+    'overriding !important by an underscore hack': {
+      'topic': 'a{color:red!important;display:block;_color:#fff}',
+      'into': function (topic) {
+        assert.deepEqual(_optimize(topic), [
+          [['color', true , false], ['red']],
+          [['display', false , false], ['block']],
+          [['color', false , 'underscore'], ['#fff']]
+        ]);
+      }
+    },
+    'overriding !important by an backslash hack': {
+      'topic': 'a{color:red!important;display:block;color:#fff\\0}',
+      'into': function (topic) {
+        assert.deepEqual(_optimize(topic), [
+          [['color', true , false], ['red']],
+          [['display', false , false], ['block']]
+        ]);
+      }
+    }
+  })
   .export(module);
index 4244158..b3bf0cb 100644 (file)
@@ -137,6 +137,17 @@ vows.describe(optimize)
           [['padding', false, false], ['10px'], ['2px'], ['3px'], ['5px']]
         ]);
       }
+    },
+    'with hacks': {
+      'topic': 'a{padding-top:10px;padding-left:5px;padding-bottom:3px;_padding-right:2px}',
+      'into': function (topic) {
+        assert.deepEqual(_optimize(topic), [
+          [['padding-top', false, false], ['10px']],
+          [['padding-left', false, false], ['5px']],
+          [['padding-bottom', false, false], ['3px']],
+          [['padding-right', false, 'underscore'], ['2px']]
+        ]);
+      }
     }
   })
   .addBatch({
index e6d76b5..9a16af7 100644 (file)
@@ -98,6 +98,17 @@ vows.describe(wrapForOptimizing)
       'is unused': function (wrapped) {
         assert.isTrue(wrapped[0].unused);
       }
+    },
+    'hack': {
+      'topic': function () {
+        return wrapForOptimizing([[['margin', false, 'suffix']]]);
+      },
+      'has one wrap': function (wrapped) {
+        assert.lengthOf(wrapped, 1);
+      },
+      'is a hack': function (wrapped) {
+        assert.equal(wrapped[0].hack, 'suffix');
+      }
     }
   })
   .export(module);
index 1fb9c61..15907b3 100644 (file)
@@ -30,21 +30,21 @@ vows.describe(addOptimizationMetadata)
       'topic': [['selector', ['a'], [[['_color'], ['red']]] ]],
       'metadata': function (tokens) {
         addOptimizationMetadata(tokens);
-        assert.deepEqual(tokens, [['selector', ['a'], [[['_color', false, true], ['red']]] ]]);
+        assert.deepEqual(tokens, [['selector', ['a'], [[['color', false, 'underscore'], ['red']]] ]]);
       }
     },
     'star hack': {
-      'topic': [['selector', ['a'], [[['_color'], ['red']]] ]],
+      'topic': [['selector', ['a'], [[['*color'], ['red']]] ]],
       'metadata': function (tokens) {
         addOptimizationMetadata(tokens);
-        assert.deepEqual(tokens, [['selector', ['a'], [[['_color', false, true], ['red']]] ]]);
+        assert.deepEqual(tokens, [['selector', ['a'], [[['color', false, 'star'], ['red']]] ]]);
       }
     },
     'backslash hack': {
       'topic': [['selector', ['a'], [[['color'], ['red\\9']]] ]],
       'metadata': function (tokens) {
         addOptimizationMetadata(tokens);
-        assert.deepEqual(tokens, [['selector', ['a'], [[['color', false, true], ['red\\9']]] ]]);
+        assert.deepEqual(tokens, [['selector', ['a'], [[['color', false, 'suffix'], ['red']]] ]]);
       }
     },
     'backslash hack - value of length 1': {
index 5bc0fc6..d2e4ead 100644 (file)
@@ -332,15 +332,15 @@ vows.describe(SimpleOptimizer)
     propertyContext('ie hacks in compatibility mode', {
       'underscore': [
         'a{_width:100px}',
-        [['_width', '100px']]
+        [['width', '100px']]
       ],
       'star': [
         'a{*width:100px}',
-        [['*width', '100px']]
+        [['width', '100px']]
       ],
       'backslash': [
         'a{width:100px\\9}',
-        [['width', '100px\\9',]]
+        [['width', '100px']]
       ]
     }, { compatibility: 'ie8' })
   )