Extracts 'merge non adjacent by selector' optimization into a module.
authorJakub Pawlowicz <contact@jakubpawlowicz.com>
Sun, 21 Jun 2015 11:26:50 +0000 (12:26 +0100)
committerJakub Pawlowicz <contact@jakubpawlowicz.com>
Sun, 21 Jun 2015 11:26:50 +0000 (12:26 +0100)
lib/selectors/advanced.js
lib/selectors/merge-non-adjacent-by-selector.js [new file with mode: 0644]
test/selectors/merge-non-adjacent-by-selector-test.js [new file with mode: 0644]

index cdfa961..7c806f3 100644 (file)
@@ -11,6 +11,7 @@ var stringifySelectors = require('../stringifier/one-time').selectors;
 var removeDuplicates = require('./remove-duplicates');
 var mergeAdjacent = require('./merge-adjacent');
 var reduceNonAdjacent = require('./reduce-non-adjacent');
+var mergeNonAdjacentBySelector = require('./merge-non-adjacent-by-selector');
 
 function AdvancedOptimizer(options, context) {
   this.options = options;
@@ -29,76 +30,6 @@ AdvancedOptimizer.prototype.isSpecial = function (selector) {
   return this.options.compatibility.selectors.special.test(selector);
 };
 
-AdvancedOptimizer.prototype.mergeNonAdjacentBySelector = function (tokens) {
-  var allSelectors = {};
-  var repeatedSelectors = [];
-  var i;
-
-  for (i = tokens.length - 1; i >= 0; i--) {
-    if (tokens[i][0] != 'selector')
-      continue;
-    if (tokens[i][2].length === 0)
-      continue;
-
-    var selector = stringifySelectors(tokens[i][1]);
-    allSelectors[selector] = [i].concat(allSelectors[selector] || []);
-
-    if (allSelectors[selector].length == 2)
-      repeatedSelectors.push(selector);
-  }
-
-  for (i = repeatedSelectors.length - 1; i >= 0; i--) {
-    var positions = allSelectors[repeatedSelectors[i]];
-
-    selectorIterator:
-    for (var j = positions.length - 1; j > 0; j--) {
-      var positionOne = positions[j - 1];
-      var tokenOne = tokens[positionOne];
-      var positionTwo = positions[j];
-      var tokenTwo = tokens[positionTwo];
-
-      directionIterator:
-      for (var direction = 1; direction >= -1; direction -= 2) {
-        var topToBottom = direction == 1;
-        var from = topToBottom ? positionOne + 1 : positionTwo - 1;
-        var to = topToBottom ? positionTwo : positionOne;
-        var delta = topToBottom ? 1 : -1;
-        var moved = topToBottom ? tokenOne : tokenTwo;
-        var target = topToBottom ? tokenTwo : tokenOne;
-        var movedProperties = extractProperties(moved);
-        var joinAt;
-
-        while (from != to) {
-          var traversedProperties = extractProperties(tokens[from]);
-          from += delta;
-
-          // traversed then moved as we move selectors towards the start
-          var reorderable = topToBottom ?
-            canReorder(movedProperties, traversedProperties) :
-            canReorder(traversedProperties, movedProperties);
-
-          if (!reorderable && !topToBottom)
-            continue selectorIterator;
-          if (!reorderable && topToBottom)
-            continue directionIterator;
-        }
-
-        if (topToBottom) {
-          joinAt = [moved[2].length];
-          Array.prototype.push.apply(moved[2], target[2]);
-          target[2] = moved[2];
-        } else {
-          joinAt = [target[2].length];
-          Array.prototype.push.apply(target[2], moved[2]);
-        }
-
-        optimizeProperties(target[1], target[2], joinAt, true, this.options, this.validator);
-        moved[2] = [];
-      }
-    }
-  }
-};
-
 function isBemElement(token) {
   var asString = stringifySelectors(token[1]);
   return asString.indexOf('__') > -1 || asString.indexOf('--') > -1;
@@ -604,7 +535,7 @@ AdvancedOptimizer.prototype.optimize = function (tokens) {
     mergeAdjacent(tokens, self.options, self.validator);
     reduceNonAdjacent(tokens, self.options, self.validator);
 
-    self.mergeNonAdjacentBySelector(tokens);
+    mergeNonAdjacentBySelector(tokens, self.options, self.validator);
     self.mergeNonAdjacentByBody(tokens);
 
     if (self.options.restructuring && withRestructuring) {
diff --git a/lib/selectors/merge-non-adjacent-by-selector.js b/lib/selectors/merge-non-adjacent-by-selector.js
new file mode 100644 (file)
index 0000000..28924b9
--- /dev/null
@@ -0,0 +1,76 @@
+var optimizeProperties = require('../properties/optimizer');
+var stringifySelectors = require('../stringifier/one-time').selectors;
+var extractProperties = require('./extractor');
+var canReorder = require('./reorderable').canReorder;
+
+function mergeNonAdjacentBySelector(tokens, options, validator) {
+  var allSelectors = {};
+  var repeatedSelectors = [];
+  var i;
+
+  for (i = tokens.length - 1; i >= 0; i--) {
+    if (tokens[i][0] != 'selector')
+      continue;
+    if (tokens[i][2].length === 0)
+      continue;
+
+    var selector = stringifySelectors(tokens[i][1]);
+    allSelectors[selector] = [i].concat(allSelectors[selector] || []);
+
+    if (allSelectors[selector].length == 2)
+      repeatedSelectors.push(selector);
+  }
+
+  for (i = repeatedSelectors.length - 1; i >= 0; i--) {
+    var positions = allSelectors[repeatedSelectors[i]];
+
+    selectorIterator:
+    for (var j = positions.length - 1; j > 0; j--) {
+      var positionOne = positions[j - 1];
+      var tokenOne = tokens[positionOne];
+      var positionTwo = positions[j];
+      var tokenTwo = tokens[positionTwo];
+
+      directionIterator:
+      for (var direction = 1; direction >= -1; direction -= 2) {
+        var topToBottom = direction == 1;
+        var from = topToBottom ? positionOne + 1 : positionTwo - 1;
+        var to = topToBottom ? positionTwo : positionOne;
+        var delta = topToBottom ? 1 : -1;
+        var moved = topToBottom ? tokenOne : tokenTwo;
+        var target = topToBottom ? tokenTwo : tokenOne;
+        var movedProperties = extractProperties(moved);
+        var joinAt;
+
+        while (from != to) {
+          var traversedProperties = extractProperties(tokens[from]);
+          from += delta;
+
+          // traversed then moved as we move selectors towards the start
+          var reorderable = topToBottom ?
+            canReorder(movedProperties, traversedProperties) :
+            canReorder(traversedProperties, movedProperties);
+
+          if (!reorderable && !topToBottom)
+            continue selectorIterator;
+          if (!reorderable && topToBottom)
+            continue directionIterator;
+        }
+
+        if (topToBottom) {
+          joinAt = [moved[2].length];
+          Array.prototype.push.apply(moved[2], target[2]);
+          target[2] = moved[2];
+        } else {
+          joinAt = [target[2].length];
+          Array.prototype.push.apply(target[2], moved[2]);
+        }
+
+        optimizeProperties(target[1], target[2], joinAt, true, options, validator);
+        moved[2] = [];
+      }
+    }
+  }
+}
+
+module.exports = mergeNonAdjacentBySelector;
diff --git a/test/selectors/merge-non-adjacent-by-selector-test.js b/test/selectors/merge-non-adjacent-by-selector-test.js
new file mode 100644 (file)
index 0000000..bc13298
--- /dev/null
@@ -0,0 +1,33 @@
+var vows = require('vows');
+var optimizerContext = require('../test-helper').optimizerContext;
+
+vows.describe('merge non djacent by selector')
+  .addBatch(
+    optimizerContext('advanced on', {
+      'up': [
+        '.one{color:red}.two{color:#fff}.one{font-weight:400}',
+        '.one{color:red;font-weight:400}.two{color:#fff}'
+      ],
+      'down': [
+        '.one{color:red}.two{font-weight:700}.one{font-weight:400}',
+        '.two{font-weight:700}.one{color:red;font-weight:400}'
+      ]
+      // 'up - blocked': [
+      //   '.one{color:red;with:100%}.two{display:inline-block;width:10px}.one{font-weight:400;display:block}',
+      //   '.one{color:red;with:100%}.two{display:inline-block;width:10px}.one{font-weight:400;display:block}'
+      // ]
+    })
+  )
+  .addBatch(
+    optimizerContext('advanced off', {
+      'up': [
+        '.one{color:red}.two{color:#fff}.one{font-weight:400}',
+        '.one{color:red}.two{color:#fff}.one{font-weight:400}'
+      ],
+      'down': [
+        '.one{color:red}.two{font-weight:700}.one{font-weight:400}',
+        '.one{color:red}.two{font-weight:700}.one{font-weight:400}'
+      ]
+    }, { advanced: false })
+  )
+  .export(module);