Strips main clean.js transforms to bare minimum.
authorJakub Pawlowicz <contact@jakubpawlowicz.com>
Thu, 18 Sep 2014 07:44:42 +0000 (09:44 +0200)
committerJakub Pawlowicz <contact@jakubpawlowicz.com>
Fri, 10 Oct 2014 20:22:44 +0000 (21:22 +0100)
lib/clean.js

index 42c7123..743fa03 100644 (file)
@@ -5,20 +5,13 @@
  * Copyright (C) 2014 JakubPawlowicz.com
  */
 
-var ColorShortener = require('./colors/shortener');
-var ColorHSLToHex = require('./colors/hsl-to-hex');
-var ColorRGBToHex = require('./colors/rgb-to-hex');
-var ColorLongToShortHex = require('./colors/long-to-short-hex');
-
 var ImportInliner = require('./imports/inliner');
 var UrlRebase = require('./images/url-rebase');
 
-var CommentsProcessor = require('./text/comments');
-var ExpressionsProcessor = require('./text/expressions');
-var FreeTextProcessor = require('./text/free');
-var UrlsProcessor = require('./text/urls');
-var NameQuotesProcessor = require('./text/name-quotes');
-var Splitter = require('./utils/splitter');
+var CommentsProcessor = require('./text/comments-processor');
+var ExpressionsProcessor = require('./text/expressions-processor');
+var FreeTextProcessor = require('./text/free-text-processor');
+var UrlsProcessor = require('./text/urls-processor');
 
 var SelectorsOptimizer = require('./selectors/optimizer');
 
@@ -70,7 +63,7 @@ CleanCSS.prototype.minify = function(data, callback) {
   }
 };
 
-var minify = function(data, callback) {
+function minify(data, callback) {
   var startedAt;
   var stats = this.stats;
   var options = this.options;
@@ -80,7 +73,6 @@ var minify = function(data, callback) {
   var expressionsProcessor = new ExpressionsProcessor();
   var freeTextProcessor = new FreeTextProcessor();
   var urlsProcessor = new UrlsProcessor();
-  var nameQuotesProcessor = new NameQuotesProcessor();
 
   if (options.debug) {
     this.startedAt = process.hrtime();
@@ -119,57 +111,10 @@ var minify = function(data, callback) {
     data = commentsProcessor.escape(data);
   });
 
-  // replace all escaped line breaks
-  replace(/\\(\r\n|\n)/gm, '');
-
-  // strip parentheses in urls if possible (no spaces inside)
-  replace(/url\((['"])([^\)]+)['"]\)/g, function(match, quote, url) {
-    var unsafeDataURI = url.indexOf('data:') === 0 && url.match(/data:\w+\/[^;]+;base64,/) === null;
-    if (url.match(/[ \t]/g) !== null || unsafeDataURI)
-      return 'url(' + quote + url + quote + ')';
-    else
-      return 'url(' + url + ')';
-  });
-
-  // strip parentheses in animation & font names
-  replace(function removeQuotes() {
-    data = nameQuotesProcessor.process(data);
-  });
-
-  // strip parentheses in @keyframes
-  replace(/@(\-moz\-|\-o\-|\-webkit\-)?keyframes ([^{]+)/g, function(match, prefix, name) {
-    prefix = prefix || '';
-    return '@' + prefix + 'keyframes ' + (name.indexOf(' ') > -1 ? name : name.replace(/['"]/g, ''));
-  });
-
-  // IE shorter filters, but only if single (IE 7 issue)
-  replace(/progid:DXImageTransform\.Microsoft\.(Alpha|Chroma)(\([^\)]+\))([;}'"])/g, function(match, filter, args, suffix) {
-    return filter.toLowerCase() + args + suffix;
-  });
-
   replace(function escapeExpressions() {
     data = expressionsProcessor.escape(data);
   });
 
-  // strip parentheses in attribute values
-  replace(/\[([^\]]+)\]/g, function(match, content) {
-    var eqIndex = content.indexOf('=');
-    var singleQuoteIndex = content.indexOf('\'');
-    var doubleQuoteIndex = content.indexOf('"');
-    if (eqIndex < 0 && singleQuoteIndex < 0 && doubleQuoteIndex < 0)
-      return match;
-    if (singleQuoteIndex === 0 || doubleQuoteIndex === 0)
-      return match;
-
-    var key = content.substring(0, eqIndex);
-    var value = content.substring(eqIndex + 1, content.length);
-
-    if (/^['"](?:[a-zA-Z][a-zA-Z\d\-_]+)['"]$/.test(value))
-      return '[' + key + '=' + value.substring(1, value.length - 1) + ']';
-    else
-      return match;
-  });
-
   replace(function escapeFreeText() {
     data = freeTextProcessor.escape(data);
   });
@@ -178,242 +123,26 @@ var minify = function(data, callback) {
     data = urlsProcessor.escape(data);
   });
 
-  // remove invalid special declarations
-  replace(/@charset [^;]+;/ig, function (match) {
-    return match.indexOf('@charset') > -1 ? match : '';
-  });
-
-  // whitespace inside attribute selectors brackets
-  replace(/\[([^\]]+)\]/g, function(match) {
-    return match.replace(/\s/g, '');
-  });
-
-  // line breaks
-  replace(/[\r]?\n/g, ' ');
-
-  // multiple whitespace
-  replace(/[\t ]+/g, ' ');
-
-  // multiple semicolons (with optional whitespace)
-  replace(/;[ ]?;+/g, ';');
-
-  // multiple line breaks to one
-  replace(/ (?:\r\n|\n)/g, lineBreak);
-  replace(/(?:\r\n|\n)+/g, lineBreak);
-
-  // remove spaces around selectors
-  replace(/ ([+~>]) /g, '$1');
-
-  // remove extra spaces inside content
-  replace(/([!\(\{\}:;=,\n]) /g, '$1');
-  replace(/ ([!\)\{\};=,\n])/g, '$1');
-  replace(/(?:\r\n|\n)\}/g, '}');
-  replace(/([\{;,])(?:\r\n|\n)/g, '$1');
-  replace(/ :([^\{\};]+)([;}])/g, ':$1$2');
-
-  // restore spaces inside IE filters (IE 7 issue)
-  replace(/progid:[^(]+\(([^\)]+)/g, function(match) {
-    return match.replace(/,/g, ', ');
-  });
-
-  // trailing semicolons
-  replace(/;\}/g, '}');
-
-  replace(function hsl2Hex() {
-    data = new ColorHSLToHex(data).process();
-  });
-
-  replace(function rgb2Hex() {
-    data = new ColorRGBToHex(data).process();
-  });
-
-  replace(function longToShortHex() {
-    data = new ColorLongToShortHex(data).process();
-  });
-
-  replace(function shortenColors() {
-    data = new ColorShortener(data).process();
-  });
-
-  // replace font weight with numerical value
-  replace(/(font\-weight|font):(normal|bold)([ ;\}!])(\w*)/g, function(match, property, weight, suffix, next) {
-    if (suffix == ' ' && (next.indexOf('/') > -1 || next == 'normal' || /[1-9]00/.test(next)))
-      return match;
-
-    if (weight == 'normal')
-      return property + ':400' + suffix + next;
-    else if (weight == 'bold')
-      return property + ':700' + suffix + next;
-    else
-      return match;
-  });
-
-  // minus zero to zero
-  // repeated twice on purpose as if not it doesn't process rgba(-0,-0,-0,-0) correctly
-  var zerosRegexp = /(\s|:|,|\()\-0([^\.])/g;
-  replace(zerosRegexp, '$10$2');
-  replace(zerosRegexp, '$10$2');
-
-  // zero(s) + value to value
-  replace(/(\s|:|,)0+([1-9])/g, '$1$2');
-
-  // round pixels to 2nd decimal place
-  var precision = 'roundingPrecision' in options ? options.roundingPrecision : 2;
-  var decimalMultiplier = Math.pow(10, precision);
-  replace(new RegExp('\\.(\\d{' + (precision + 1) + ',})px', 'g'), function(match, decimalPlaces) {
-    return precision === 0 ?
-      'px' :
-      '.' + Math.round(parseFloat('.' + decimalPlaces) * decimalMultiplier) / decimalMultiplier + 'px';
-  });
-
-  // .0 to 0
-  // repeated twice on purpose as if not it doesn't process {padding: .0 .0 .0 .0} correctly
-  var leadingDecimalRegexp = /(\D)\.0+(\D)/g;
-  replace(leadingDecimalRegexp, '$10$2');
-  replace(leadingDecimalRegexp, '$10$2');
-
-  // fraction zeros removal
-  replace(/\.([1-9]*)0+(\D)/g, function(match, nonZeroPart, suffix) {
-    return (nonZeroPart.length > 0 ? '.' : '') + nonZeroPart + suffix;
-  });
-
-  // zero + unit to zero
-  var units = ['px', 'em', 'ex', 'cm', 'mm', 'in', 'pt', 'pc', '%'];
-  if (['ie7', 'ie8'].indexOf(options.compatibility) == -1)
-    units.push('rem');
-
-  replace(new RegExp('(\\s|:|,)\\-?0(?:' + units.join('|') + ')', 'g'), '$1' + '0');
-  replace(new RegExp('(\\s|:|,)\\-?(\\d+)\\.(\\D)', 'g'), '$1$2$3');
-  replace(new RegExp('rect\\(0(?:' + units.join('|') + ')', 'g'), 'rect(0');
-
-  // restore % in rgb/rgba and hsl/hsla
-  replace(/(rgb|rgba|hsl|hsla)\(([^\)]+)\)/g, function(match, colorFunction, colorDef) {
-    var tokens = colorDef.split(',');
-    var applies = colorFunction == 'hsl' || colorFunction == 'hsla' || tokens[0].indexOf('%') > -1;
-    if (!applies)
-      return match;
-
-    if (tokens[1].indexOf('%') == -1)
-      tokens[1] += '%';
-    if (tokens[2].indexOf('%') == -1)
-      tokens[2] += '%';
-    return colorFunction + '(' + tokens.join(',') + ')';
-  });
-
-  // transparent rgba/hsla to 'transparent' unless in compatibility mode
-  if (!options.compatibility) {
-    replace(/:([^;]*)(?:rgba|hsla)\(0,0%?,0%?,0\)/g, function (match, prefix) {
-      if (new Splitter(',').split(match).pop().indexOf('gradient(') > -1)
-        return match;
-
-      return ':' + prefix + 'transparent';
-    });
-  }
-
-  // none to 0
-  replace(/outline:none/g, 'outline:0');
-
-  // background:none to background:0 0
-  replace(/background:(?:none|transparent)([;}])/g, 'background:0 0$1');
-
-  // multiple zeros into one
-  replace(/box-shadow:0 0 0 0([^\.])/g, 'box-shadow:0 0$1');
-  replace(/:0 0 0 0([^\.])/g, ':0$1');
-  replace(/([: ,=\-])0\.(\d)/g, '$1.$2');
-
-  // restore rect(...) zeros syntax for 4 zeros
-  replace(/rect\(\s?0(\s|,)0[ ,]0[ ,]0\s?\)/g, 'rect(0$10$10$10)');
-
-  // remove universal selector when not needed (*#id, *.class etc)
-  // pending a better fix
-  if (options.compatibility != 'ie7') {
-    replace(/([^,]?)(\*[^ \+\{]*\+html[^\{]*)(\{[^\}]*\})/g, function (match, prefix, selector, body) {
-      var notHackedSelectors = new Splitter(',').split(selector).filter(function (m) {
-        return !/^\*[^ \+\{]*\+html/.test(m);
-      });
-
-      return notHackedSelectors.length > 0 ?
-        prefix + notHackedSelectors.join(',') + body :
-        prefix;
-    });
-    replace(/\*([\.#:\[])/g, '$1');
-  }
-
-  // Restore spaces inside calc back
-  replace(/calc\([^\}]+\}/g, function(match) {
-    return match.replace(/\+/g, ' + ');
-  });
-
-  // get rid of IE hacks if not in compatibility mode
-  if (!options.compatibility)
-    replace(/([;\{])[\*_][\w\-]+:[^;\}]+/g, '$1');
-
-  if (options.noAdvanced) {
-    if (options.keepBreaks)
-      replace(/\}/g, '}' + lineBreak);
-  } else {
-    replace(function optimizeSelectors() {
-      data = new SelectorsOptimizer(data, context, {
-        keepBreaks: options.keepBreaks,
-        lineBreak: lineBreak,
-        compatibility: options.compatibility,
-        aggressiveMerging: !options.noAggressiveMerging
-      }).process();
-    });
-  }
-
-  // replace ' / ' in border-*-radius with '/'
-  replace(/(border-\w+-\w+-radius:\S+)\s+\/\s+/g, '$1/');
-
-  // replace same H/V values in border-radius
-  replace(/(border-\w+-\w+-radius):([^;\}]+)/g, function (match, property, value) {
-    var parts = value.split('/');
-
-    if (parts.length > 1 && parts[0] == parts[1])
-      return property + ':' + parts[0];
-    else
-      return match;
-  });
-
   replace(function restoreUrls() {
     data = urlsProcessor.restore(data);
   });
+
   replace(function rebaseUrls() {
     data = options.noRebase ? data : new UrlRebase(options, context).process(data);
   });
+
   replace(function restoreFreeText() {
     data = freeTextProcessor.restore(data);
   });
+
   replace(function restoreComments() {
     data = commentsProcessor.restore(data);
   });
+
   replace(function restoreExpressions() {
     data = expressionsProcessor.restore(data);
   });
 
-  // move first charset to the beginning
-  replace(function moveCharset() {
-    // get first charset in stylesheet
-    var match = data.match(/@charset [^;]+;/);
-    var firstCharset = match ? match[0] : null;
-    if (!firstCharset)
-      return;
-
-    // reattach first charset and remove all subsequent
-    data = firstCharset +
-      (options.keepBreaks ? lineBreak : '') +
-      data.replace(new RegExp('@charset [^;]+;(' + lineBreak + ')?', 'g'), '').trim();
-  });
-
-  if (options.noAdvanced) {
-    replace(function removeEmptySelectors() {
-      data = new EmptyRemoval(data).process();
-    });
-  }
-
-  // trim spaces at beginning and end
-  data = data.trim();
-
   if (options.debug) {
     var elapsed = process.hrtime(startedAt);
     stats.timeSpent = ~~(elapsed[0] * 1e3 + elapsed[1] / 1e6);