Extracts 'merge adjacent' optimization into a module.
authorJakub Pawlowicz <contact@jakubpawlowicz.com>
Sun, 21 Jun 2015 10:53:03 +0000 (11:53 +0100)
committerJakub Pawlowicz <contact@jakubpawlowicz.com>
Sun, 21 Jun 2015 10:53:22 +0000 (11:53 +0100)
lib/selectors/advanced.js
lib/selectors/is-special.js [new file with mode: 0644]
lib/selectors/merge-adjacent.js [new file with mode: 0644]
test/integration-test.js
test/selectors/advanced-test.js
test/selectors/merge-adjacent-test.js [new file with mode: 0644]

index 0bde05b..ef6c4e7 100644 (file)
@@ -9,6 +9,7 @@ var stringifyBody = require('../stringifier/one-time').body;
 var stringifySelectors = require('../stringifier/one-time').selectors;
 
 var removeDuplicates = require('./remove-duplicates');
+var mergeAdjacent = require('./merge-adjacent');
 
 function AdvancedOptimizer(options, context) {
   this.options = options;
@@ -27,33 +28,6 @@ AdvancedOptimizer.prototype.isSpecial = function (selector) {
   return this.options.compatibility.selectors.special.test(selector);
 };
 
-AdvancedOptimizer.prototype.mergeAdjacent = function (tokens) {
-  var lastToken = [null, [], []];
-  var adjacentSpace = this.options.compatibility.selectors.adjacentSpace;
-
-  for (var i = 0, l = tokens.length; i < l; i++) {
-    var token = tokens[i];
-
-    if (token[0] != 'selector') {
-      lastToken = [null, [], []];
-      continue;
-    }
-
-    if (lastToken[0] == 'selector' && stringifySelectors(token[1]) == stringifySelectors(lastToken[1])) {
-      var joinAt = [lastToken[2].length];
-      Array.prototype.push.apply(lastToken[2], token[2]);
-      optimizeProperties(token[1], lastToken[2], joinAt, true, this.options, this.validator);
-      token[2] = [];
-    } else if (lastToken[0] == 'selector' && stringifyBody(token[2]) == stringifyBody(lastToken[2]) &&
-        !this.isSpecial(stringifySelectors(token[1])) && !this.isSpecial(stringifySelectors(lastToken[1]))) {
-      lastToken[1] = CleanUp.selectors(lastToken[1].concat(token[1]), false, adjacentSpace);
-      token[2] = [];
-    } else {
-      lastToken = token;
-    }
-  }
-};
-
 AdvancedOptimizer.prototype.reduceNonAdjacent = function (tokens) {
   var candidates = {};
   var repeated = [];
@@ -779,7 +753,7 @@ AdvancedOptimizer.prototype.optimize = function (tokens) {
     recursivelyOptimizeProperties(tokens, self.options, self.validator);
 
     removeDuplicates(tokens);
-    self.mergeAdjacent(tokens);
+    mergeAdjacent(tokens, self.options, self.validator);
     self.reduceNonAdjacent(tokens);
 
     self.mergeNonAdjacentBySelector(tokens);
@@ -787,7 +761,7 @@ AdvancedOptimizer.prototype.optimize = function (tokens) {
 
     if (self.options.restructuring && withRestructuring) {
       self.restructure(tokens);
-      self.mergeAdjacent(tokens);
+      mergeAdjacent(tokens, self.options, self.validator);
     }
 
     if (self.options.mediaMerging) {
diff --git a/lib/selectors/is-special.js b/lib/selectors/is-special.js
new file mode 100644 (file)
index 0000000..9631514
--- /dev/null
@@ -0,0 +1,5 @@
+function isSpecial(options, selector) {
+  return options.compatibility.selectors.special.test(selector);
+}
+
+module.exports = isSpecial;
diff --git a/lib/selectors/merge-adjacent.js b/lib/selectors/merge-adjacent.js
new file mode 100644 (file)
index 0000000..1485707
--- /dev/null
@@ -0,0 +1,35 @@
+var optimizeProperties = require('../properties/optimizer');
+
+var stringifyBody = require('../stringifier/one-time').body;
+var stringifySelectors = require('../stringifier/one-time').selectors;
+var cleanUpSelectors = require('./clean-up').selectors;
+var isSpecial = require('./is-special');
+
+function mergeAdjacent(tokens, options, validator) {
+  var lastToken = [null, [], []];
+  var adjacentSpace = options.compatibility.selectors.adjacentSpace;
+
+  for (var i = 0, l = tokens.length; i < l; i++) {
+    var token = tokens[i];
+
+    if (token[0] != 'selector') {
+      lastToken = [null, [], []];
+      continue;
+    }
+
+    if (lastToken[0] == 'selector' && stringifySelectors(token[1]) == stringifySelectors(lastToken[1])) {
+      var joinAt = [lastToken[2].length];
+      Array.prototype.push.apply(lastToken[2], token[2]);
+      optimizeProperties(token[1], lastToken[2], joinAt, true, options, validator);
+      token[2] = [];
+    } else if (lastToken[0] == 'selector' && stringifyBody(token[2]) == stringifyBody(lastToken[2]) &&
+        !isSpecial(options, stringifySelectors(token[1])) && !isSpecial(options, stringifySelectors(lastToken[1]))) {
+      lastToken[1] = cleanUpSelectors(lastToken[1].concat(token[1]), false, adjacentSpace);
+      token[2] = [];
+    } else {
+      lastToken = token;
+    }
+  }
+}
+
+module.exports = mergeAdjacent;
index 9c9bc2a..eb8487d 100644 (file)
@@ -2158,62 +2158,6 @@ vows.describe('integration tests')
       ]
     }, { aggressiveMerging: false })
   )
-  .addBatch(
-    optimizerContext('same selectors', {
-      'of two non-adjacent selectors': [
-        '.one{color:red}.two{color:#00f}.one{font-weight:700}',
-        '.one{color:red;font-weight:700}.two{color:#00f}'
-      ],
-      'of two adjacent single selectors': [
-        '.one{color:red}.one{font-weight:700}',
-        '.one{color:red;font-weight:700}'
-      ],
-      'of three adjacent single selectors': [
-        '.one{color:red}.one{font-weight:700}.one{font-size:12px}',
-        '.one{color:red;font-weight:700;font-size:12px}'
-      ],
-      'of two adjacent single, complex selectors': [
-        '#box>.one{color:red}#box>.one{font-weight:700}',
-        '#box>.one{color:red;font-weight:700}'
-      ],
-      'of two adjacent multiple, complex selectors': [
-        '#box>.one,.zero{color:red}#box>.one,.zero{font-weight:700}',
-        '#box>.one,.zero{color:red;font-weight:700}'
-      ],
-      'of two adjacent selectors with duplicate properties #1': [
-        '.one{color:red}.one{color:#fff}',
-        '.one{color:#fff}'
-      ],
-      'of two adjacent selectors with duplicate properties #2': [
-        '.one{color:red;font-weight:bold}.one{color:#fff;font-weight:400}',
-        '.one{color:#fff;font-weight:400}'
-      ],
-      'of two adjacent complex selectors with different selector order': [
-        '.one,.two{color:red}.two,.one{line-height:1em}',
-        '.one,.two{color:red;line-height:1em}'
-      ],
-      'two adjacent with hex color definitions': [
-        'a:link,a:visited{color:#fff}.one{display:block}a:link,a:visited{color:red}',
-        '.one{display:block}a:link,a:visited{color:red}'
-      ],
-      'in two passes': [
-        'a{color:red}a{background:red}b{color:red}b{background:red}',
-        'a,b{color:red;background:red}'
-      ],
-      'when overriden with a browser specific selector': [
-        'a{color:red}::-webkit-scrollbar,a{color:#fff}',
-        'a{color:red}::-webkit-scrollbar,a{color:#fff}'
-      ],
-      'two same selectors over a block': [
-        '.one{color:red}@media print{.two{display:block}}.one{display:none}',
-        '@media print{.two{display:block}}.one{color:red;display:none}'
-      ],
-      'two same bodies over a block': [
-        '.one{color:red}@media print{.two{display:block}}.three{color:red}',
-        '.one,.three{color:red}@media print{.two{display:block}}'
-      ]
-    })
-  )
   .addBatch(
     optimizerContext('same non-adjacent selectors', {
       'with one redefined property': [
index 04152de..f47b11c 100644 (file)
@@ -209,10 +209,6 @@ vows.describe('advanced optimizer')
         'a{color:red;color:red}',
         'a{color:red}'
       ],
-      'adjacent': [
-        'a{color:red}a{display:block;width:100px}div{color:#fff}',
-        'a{color:red;display:block;width:100px}div{color:#fff}'
-      ],
       'non-adjacent': [
         'a{color:red;display:block}.one{margin:12px}a{color:#fff;margin:2px}',
         '.one{margin:12px}a{display:block;color:#fff;margin:2px}'
@@ -249,10 +245,6 @@ vows.describe('advanced optimizer')
         'a{color:red;color:red}',
         'a{color:red;color:red}'
       ],
-      'adjacent': [
-        'a{color:red}a{display:block;width:100px}div{color:#fff}',
-        'a{color:red}a{display:block;width:100px}div{color:#fff}'
-      ],
       'non-adjacent': [
         'a{color:red;display:block}.one{font-size:12px}a{color:#fff;margin:2px}',
         'a{color:red;display:block}.one{font-size:12px}a{color:#fff;margin:2px}'
diff --git a/test/selectors/merge-adjacent-test.js b/test/selectors/merge-adjacent-test.js
new file mode 100644 (file)
index 0000000..fe29de7
--- /dev/null
@@ -0,0 +1,73 @@
+var vows = require('vows');
+var optimizerContext = require('../test-helper').optimizerContext;
+
+vows.describe('remove duplicates')
+  .addBatch(
+    optimizerContext('advanced on', {
+      'same context': [
+        'a{color:red}a{display:block;width:100px}div{color:#fff}',
+        'a{color:red;display:block;width:100px}div{color:#fff}'
+      ],
+      'of two non-adjacent selectors': [
+        '.one{color:red}.two{color:#00f}.one{font-weight:700}',
+        '.one{color:red;font-weight:700}.two{color:#00f}'
+      ],
+      'of two adjacent single selectors': [
+        '.one{color:red}.one{font-weight:700}',
+        '.one{color:red;font-weight:700}'
+      ],
+      'of three adjacent single selectors': [
+        '.one{color:red}.one{font-weight:700}.one{font-size:12px}',
+        '.one{color:red;font-weight:700;font-size:12px}'
+      ],
+      'of two adjacent single, complex selectors': [
+        '#box>.one{color:red}#box>.one{font-weight:700}',
+        '#box>.one{color:red;font-weight:700}'
+      ],
+      'of two adjacent multiple, complex selectors': [
+        '#box>.one,.zero{color:red}#box>.one,.zero{font-weight:700}',
+        '#box>.one,.zero{color:red;font-weight:700}'
+      ],
+      'of two adjacent selectors with duplicate properties #1': [
+        '.one{color:red}.one{color:#fff}',
+        '.one{color:#fff}'
+      ],
+      'of two adjacent selectors with duplicate properties #2': [
+        '.one{color:red;font-weight:bold}.one{color:#fff;font-weight:400}',
+        '.one{color:#fff;font-weight:400}'
+      ],
+      'of two adjacent complex selectors with different selector order': [
+        '.one,.two{color:red}.two,.one{line-height:1em}',
+        '.one,.two{color:red;line-height:1em}'
+      ],
+      'two adjacent with hex color definitions': [
+        'a:link,a:visited{color:#fff}.one{display:block}a:link,a:visited{color:red}',
+        '.one{display:block}a:link,a:visited{color:red}'
+      ],
+      'in two passes': [
+        'a{color:red}a{background:red}b{color:red}b{background:red}',
+        'a,b{color:red;background:red}'
+      ],
+      'when overriden with a browser specific selector': [
+        'a{color:red}::-webkit-scrollbar,a{color:#fff}',
+        'a{color:red}::-webkit-scrollbar,a{color:#fff}'
+      ],
+      'two same selectors over a block': [
+        '.one{color:red}@media print{.two{display:block}}.one{display:none}',
+        '@media print{.two{display:block}}.one{color:red;display:none}'
+      ],
+      'two same bodies over a block': [
+        '.one{color:red}@media print{.two{display:block}}.three{color:red}',
+        '.one,.three{color:red}@media print{.two{display:block}}'
+      ]
+    })
+  )
+  .addBatch(
+    optimizerContext('advanced off', {
+      'same context': [
+        'a{color:red}a{display:block;width:100px}div{color:#fff}',
+        'a{color:red}a{display:block;width:100px}div{color:#fff}'
+      ],
+    }, { advanced: false })
+  )
+  .export(module);