Fixes #662 - !important properties and selector reducing.
authorJakub Pawlowicz <contact@jakubpawlowicz.com>
Sat, 5 Sep 2015 20:12:49 +0000 (21:12 +0100)
committerJakub Pawlowicz <contact@jakubpawlowicz.com>
Mon, 14 Sep 2015 12:12:51 +0000 (13:12 +0100)
It was possible that an !important property was overridden during
advanced reducing and an optimization was discarded, but due to
lack of cloning the change propagated nevertheless.

History.md
lib/selectors/reduce-non-adjacent.js
lib/utils/clone-array.js [new file with mode: 0644]
test/selectors/reduce-non-adjacent-test.js
test/utils/clone-array-test.js [new file with mode: 0644]

index a3aa7be..3ccd4fe 100644 (file)
@@ -2,6 +2,7 @@
 ==================
 
 * Fixed issue [#660](https://github.com/jakubpawlowicz/clean-css/issues/660) - !important token overriding.
+* Fixed issue [#662](https://github.com/jakubpawlowicz/clean-css/issues/662) - !important selector reducing.
 
 [3.4.1 / 2015-08-27](https://github.com/jakubpawlowicz/clean-css/compare/v3.4.0...v3.4.1)
 ==================
index 1ad9c06..0bb4c31 100644 (file)
@@ -2,6 +2,7 @@ var optimizeProperties = require('../properties/optimizer');
 var stringifyBody = require('../stringifier/one-time').body;
 var stringifySelectors = require('../stringifier/one-time').selectors;
 var isSpecial = require('./is-special');
+var cloneArray = require('../utils/clone-array');
 
 function reduceNonAdjacent(tokens, options, validator) {
   var candidates = {};
@@ -137,9 +138,10 @@ function reduceSelector(tokens, selector, data, context, options, validator) {
 
     var where = data[j].where;
     var token = tokens[where];
+    var clonedBody = cloneArray(token[2]);
 
-    bodies = bodies.concat(token[2]);
-    bodiesAsList.push(token[2]);
+    bodies = bodies.concat(clonedBody);
+    bodiesAsList.push(clonedBody);
     processedTokens.push(where);
   }
 
diff --git a/lib/utils/clone-array.js b/lib/utils/clone-array.js
new file mode 100644 (file)
index 0000000..b95ee68
--- /dev/null
@@ -0,0 +1,12 @@
+function cloneArray(array) {
+  var cloned = array.slice(0);
+
+  for (var i = 0, l = cloned.length; i < l; i++) {
+    if (Array.isArray(cloned[i]))
+      cloned[i] = cloneArray(cloned[i]);
+  }
+
+  return cloned;
+}
+
+module.exports = cloneArray;
index 22c8354..73f0ff4 100644 (file)
@@ -99,6 +99,10 @@ vows.describe('remove duplicates')
       'over a @media block': [
         '.one{color:red;margin:0}@media{.two{font-weight:700}}.one{font-size:13px}',
         '.one{color:red;margin:0;font-size:13px}@media{.two{font-weight:700}}'
+      ],
+      '!important values': [
+        '.one,.two{margin:0!important}.one{margin:1px!important}',
+        '.one,.two{margin:0!important}.one{margin:1px!important}'
       ]
     })
   )
diff --git a/test/utils/clone-array-test.js b/test/utils/clone-array-test.js
new file mode 100644 (file)
index 0000000..3340fab
--- /dev/null
@@ -0,0 +1,39 @@
+var vows = require('vows');
+var assert = require('assert');
+var cloneArray = require('../../lib/utils/clone-array');
+
+vows.describe(cloneArray)
+  .addBatch({
+    'one level': {
+      'topic': [1, 2, 3],
+      'not equal': function (topic) {
+        assert.notEqual(topic, cloneArray(topic));
+      },
+      'deep equal': function (topic) {
+        assert.deepEqual(topic, cloneArray(topic));
+      }
+    },
+    'two levels': {
+      'topic': [[1], [2], [3]],
+      'not equal': function (topic) {
+        assert.notEqual(topic[0], cloneArray(topic)[0]);
+        assert.notEqual(topic[1], cloneArray(topic)[1]);
+        assert.notEqual(topic[2], cloneArray(topic)[2]);
+      },
+      'deep equal': function (topic) {
+        assert.deepEqual(topic, cloneArray(topic));
+      }
+    },
+    'mixed levels': {
+      'topic': [[1], 2, 3],
+      'not equal': function (topic) {
+        assert.notEqual(topic[0], cloneArray(topic)[0]);
+        assert.equal(topic[1], cloneArray(topic)[1]);
+        assert.equal(topic[2], cloneArray(topic)[2]);
+      },
+      'deep equal': function (topic) {
+        assert.deepEqual(topic, cloneArray(topic));
+      }
+    }
+  })
+  .export(module);