fileMinifier.js 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. var debug = require('debug')('ylt:fileMinifier');
  2. var Q = require('q');
  3. var UglifyJS = require('uglify-js');
  4. var CleanCSS = require('clean-css');
  5. var Minimize = require('minimize');
  6. var FileMinifier = function() {
  7. function minifyFile(entry) {
  8. var deferred = Q.defer();
  9. if (!entry.weightCheck || !entry.weightCheck.body) {
  10. // No valid file available
  11. deferred.resolve(entry);
  12. return deferred.promise;
  13. }
  14. var fileSize = entry.weightCheck.uncompressedSize;
  15. debug('Let\'s try to optimize %s', entry.url);
  16. debug('Current file size is %d', fileSize);
  17. if (entry.isJS) {
  18. debug('File is a JS');
  19. // Starting softly with a lossless compression
  20. return minifyJs(entry.weightCheck.body)
  21. .then(function(newFile) {
  22. if (!newFile) {
  23. debug('Optimization didn\'t work');
  24. return entry;
  25. }
  26. var newFileSize = newFile.length;
  27. debug('JS minification complete for %s', entry.url);
  28. if (gainIsEnough(fileSize, newFileSize)) {
  29. entry.weightCheck.minified = newFileSize;
  30. entry.weightCheck.isMinified = false;
  31. debug('Filesize is %d bytes smaller (-%d%)', fileSize - newFileSize, Math.round((fileSize - newFileSize) * 100 / fileSize));
  32. }
  33. return entry;
  34. })
  35. .fail(function(err) {
  36. return entry;
  37. });
  38. } else if (entry.isCSS) {
  39. debug('File is a CSS');
  40. // Starting softly with a lossless compression
  41. return minifyCss(entry.weightCheck.body)
  42. .then(function(newFile) {
  43. if (!newFile) {
  44. debug('Optimization didn\'t work');
  45. return entry;
  46. }
  47. var newFileSize = newFile.length;
  48. debug('CSS minification complete for %s', entry.url);
  49. if (gainIsEnough(fileSize, newFileSize)) {
  50. entry.weightCheck.minified = newFileSize;
  51. entry.weightCheck.isMinified = false;
  52. debug('Filesize is %d bytes smaller (-%d%)', fileSize - newFileSize, Math.round((fileSize - newFileSize) * 100 / fileSize));
  53. }
  54. return entry;
  55. })
  56. .fail(function(err) {
  57. return entry;
  58. });
  59. } else if (entry.isHTML) {
  60. debug('File is an HTML');
  61. // Starting softly with a lossless compression
  62. return minifyHtml(entry.weightCheck.body)
  63. .then(function(newFile) {
  64. console.log('KKKKKKKKKKKK');
  65. if (!newFile) {
  66. debug('Optimization didn\'t work');
  67. return entry;
  68. }
  69. var newFileSize = newFile.length;
  70. debug('HTML minification complete for %s', entry.url);
  71. if (gainIsEnough(fileSize, newFileSize)) {
  72. entry.weightCheck.minified = newFileSize;
  73. entry.weightCheck.isMinified = false;
  74. debug('Filesize is %d bytes smaller (-%d%)', fileSize - newFileSize, Math.round((fileSize - newFileSize) * 100 / fileSize));
  75. } else {
  76. console.log('OOOO old file size: ' + fileSize);
  77. console.log('OOOO new file size: ' + newFileSize);
  78. console.log(entry.weightCheck);
  79. }
  80. return entry;
  81. })
  82. .fail(function(err) {
  83. console.log('LLLLLLLLLLLLLLL');
  84. console.log(err);
  85. return entry;
  86. });
  87. } else {
  88. debug('File type %s is not an (optimizable) image', entry.contentType);
  89. deferred.resolve(entry);
  90. }
  91. return deferred.promise;
  92. }
  93. // The gain is estimated of enough value if it's over 2KB or over 20%,
  94. // but it's ignored if is below 100 bytes
  95. function gainIsEnough(oldWeight, newWeight) {
  96. var gain = oldWeight - newWeight;
  97. var ratio = gain / oldWeight;
  98. return (gain > 2096 || (ratio > 0.2 && gain > 400));
  99. }
  100. // Uglify
  101. function minifyJs(body) {
  102. var deferred = Q.defer();
  103. try {
  104. var result = UglifyJS.minify(body, {fromString: true});
  105. deferred.resolve(result.code);
  106. } catch(err) {
  107. deferred.reject(err);
  108. }
  109. return deferred.promise;
  110. }
  111. // Clear-css
  112. function minifyCss(body) {
  113. var deferred = Q.defer();
  114. try {
  115. var result = new CleanCSS({compatibility: 'ie8'}).minify(body);
  116. deferred.resolve(result.styles);
  117. } catch(err) {
  118. deferred.reject(err);
  119. }
  120. return deferred.promise;
  121. }
  122. // HTMLMinifier
  123. function minifyHtml(body) {
  124. var deferred = Q.defer();
  125. var minimize = new Minimize({
  126. empty: true, // KEEP empty attributes
  127. conditionals: true, // KEEP conditional internet explorer comments
  128. spare: true // KEEP redundant attributes
  129. });
  130. minimize.parse(body, function (error, data) {
  131. if (error) {
  132. deferred.reject(error);
  133. } else {
  134. deferred.resolve(data);
  135. }
  136. });
  137. return deferred.promise;
  138. }
  139. return {
  140. minifyFile: minifyFile,
  141. minifyJs: minifyJs,
  142. minifyCss: minifyCss,
  143. minifyHtml: minifyHtml,
  144. gainIsEnough: gainIsEnough
  145. };
  146. };
  147. module.exports = new FileMinifier();