See #829 - some performance tweaks.
authorJakub Pawlowicz <contact@jakubpawlowicz.com>
Fri, 30 Dec 2016 21:50:09 +0000 (22:50 +0100)
committerJakub Pawlowicz <contact@jakubpawlowicz.com>
Fri, 30 Dec 2016 21:50:09 +0000 (22:50 +0100)
Why:

* Uses flat loops instead of callbacks;
* Checks for number of matches before invoking costly calculations.

lib/optimizer/is-mergeable.js

index ba46e23..df9ddd5 100644 (file)
@@ -22,11 +22,21 @@ var Level = {
 };
 
 function isMergeable(selector, mergeablePseudoClasses, mergeablePseudoElements) {
-  return split(selector, Marker.COMMA).every(function (singleSelector) {
-    return singleSelector.length > 0 &&
-      !isDeepSelector(singleSelector) &&
-      areMergable(singleSelector, extractPseudoFrom(singleSelector), mergeablePseudoClasses, mergeablePseudoElements);
-  });
+  var singleSelectors = split(selector, Marker.COMMA);
+  var singleSelector;
+  var i, l;
+
+  for (i = 0, l = singleSelectors.length; i < l; i++) {
+    singleSelector = singleSelectors[i];
+
+    if (singleSelector.length === 0 ||
+        isDeepSelector(singleSelector) ||
+        !areMergable(singleSelector, extractPseudoFrom(singleSelector), mergeablePseudoClasses, mergeablePseudoElements)) {
+      return false;
+    }
+  }
+
+  return true;
 }
 
 function isDeepSelector(selector) {
@@ -115,47 +125,74 @@ function extractPseudoFrom(selector) {
 function areMergable(selector, matches, mergeablePseudoClasses, mergeablePseudoElements) {
   return areAllowed(matches, mergeablePseudoClasses, mergeablePseudoElements) &&
     needArguments(matches) &&
-    !someIncorrectlyChained(selector, matches) &&
-    !someMixed(matches);
+    (matches.length < 2 || !someIncorrectlyChained(selector, matches)) &&
+    (matches.length < 2 || !someMixed(matches));
 }
 
 function areAllowed(matches, mergeablePseudoClasses, mergeablePseudoElements) {
-  return matches.every(function (match) {
-    var name = match.indexOf(Marker.OPEN_ROUND_BRACKET) > -1 ?
+  var match;
+  var name;
+  var i, l;
+
+  for (i = 0, l = matches.length; i < l; i++) {
+    match = matches[i];
+    name = match.indexOf(Marker.OPEN_ROUND_BRACKET) > -1 ?
       match.substring(0, match.indexOf(Marker.OPEN_ROUND_BRACKET)) :
       match;
 
-    return mergeablePseudoClasses.indexOf(name) > -1 || mergeablePseudoElements.indexOf(name) > -1;
-  });
+    if (mergeablePseudoClasses.indexOf(name) === -1 && mergeablePseudoElements.indexOf(name) === -1) {
+      return false;
+    }
+  }
+
+  return true;
 }
 
 function needArguments(matches) {
-  return matches.every(function (match) {
-    var bracketOpensAt = match.indexOf(Marker.OPEN_ROUND_BRACKET);
-    var hasArguments = bracketOpensAt > -1;
-    var name = hasArguments ?
+  var match;
+  var name;
+  var bracketOpensAt;
+  var hasArguments;
+  var i, l;
+
+  for (i = 0, l = matches.length; i < l; i++) {
+    match = matches[i];
+
+    bracketOpensAt = match.indexOf(Marker.OPEN_ROUND_BRACKET);
+    hasArguments = bracketOpensAt > -1;
+    name = hasArguments ?
       match.substring(0, bracketOpensAt) :
       match;
 
-    return hasArguments ?
-      PSEUDO_CLASSES_WITH_ARGUMENTS.indexOf(name) > -1 :
-      PSEUDO_CLASSES_WITH_ARGUMENTS.indexOf(name) == -1;
-  });
+    if (hasArguments && PSEUDO_CLASSES_WITH_ARGUMENTS.indexOf(name) == -1) {
+      return false;
+    }
+
+    if (!hasArguments && PSEUDO_CLASSES_WITH_ARGUMENTS.indexOf(name) > -1) {
+      return false;
+    }
+  }
+
+  return true;
 }
 
 function someIncorrectlyChained(selector, matches) {
   var positionInSelector = 0;
-
-  return matches.some(function (match, index) {
-    var matchAt;
-    var nextMatch = matches[index + 1];
-    var nextMatchAt;
-    var name;
-    var nextName;
-    var areChained;
+  var match;
+  var matchAt;
+  var nextMatch;
+  var nextMatchAt;
+  var name;
+  var nextName;
+  var areChained;
+  var i, l;
+
+  for (i = 0, l = matches.length; i < l; i++) {
+    match = matches[i];
+    nextMatch = matches[i + 1];
 
     if (!nextMatch) {
-      return false;
+      break;
     }
 
     matchAt = selector.indexOf(match, positionInSelector);
@@ -171,19 +208,29 @@ function someIncorrectlyChained(selector, matches) {
         nextMatch.substring(0, nextMatch.indexOf(Marker.OPEN_ROUND_BRACKET)) :
         nextMatch;
 
-      return name != NOT_PSEUDO || nextName != NOT_PSEUDO;
-    } else {
-      return false;
+      if (name != NOT_PSEUDO || nextName != NOT_PSEUDO) {
+        return true;
+      }
     }
-  });
+  }
+
+  return false;
 }
 
 function someMixed(matches) {
   var firstIsPseudoElement = DOUBLE_COLON_PATTERN.test(matches[0]);
+  var match;
+  var i, l;
+
+  for (i = 0, l = matches.length; i < l; i++) {
+    match = matches[i];
+
+    if (DOUBLE_COLON_PATTERN.test(match) != firstIsPseudoElement) {
+      return true;
+    }
+  }
 
-  return matches.some(function (match) {
-    return DOUBLE_COLON_PATTERN.test(match) != firstIsPseudoElement;
-  });
+  return false;
 }
 
 module.exports = isMergeable;