contentTypeChecker.js 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. var debug = require('debug')('ylt:contentTypeChecker');
  2. var Q = require('q');
  3. var isJpg = require('is-jpg');
  4. var isPng = require('is-png');
  5. var isSvg = require('is-svg');
  6. var isGif = require('is-gif');
  7. var isWebp = require('is-webp');
  8. var isWoff = require('is-woff');
  9. var isWoff2 = require('is-woff2');
  10. var isOtf = require('is-otf');
  11. var isTtf = require('is-ttf');
  12. var isEot = require('is-eot');
  13. var ContentTypeChecker = function() {
  14. function checkContentType(entry) {
  15. var deferred = Q.defer();
  16. debug('Entering contentTypeChecker');
  17. // Setting isSomething values:
  18. switch(entry.type) {
  19. case 'html':
  20. entry.isHTML = true;
  21. break;
  22. case 'xml':
  23. entry.isXML = true;
  24. break;
  25. case 'css':
  26. entry.isCSS = true;
  27. break;
  28. case 'js':
  29. entry.isJS = true;
  30. break;
  31. case 'json':
  32. entry.isJSON = true;
  33. break;
  34. case 'image':
  35. entry.isImage = true;
  36. break;
  37. case 'webfont':
  38. entry.isWebFont = true;
  39. break;
  40. case 'video':
  41. entry.isVideo = true;
  42. break;
  43. case 'favicon':
  44. entry.isFavicon = true;
  45. break;
  46. }
  47. // Now let's check for mistakes by analysing body content. It happens more often then we think!
  48. // Ignore very small files as they are generally tracking pixels
  49. if (entry.weightCheck && entry.weightCheck.bodyBuffer && entry.weightCheck.bodySize > 100) {
  50. var foundType;
  51. try {
  52. foundType = findContentType(entry.weightCheck.bodyBuffer);
  53. // Note: as of Phantomas v2, entry.contentType is always undefined
  54. // It could change, so let's keep the following code in place:
  55. if (!entry.contentType || entry.contentType === '') {
  56. if (foundType === null) {
  57. debug('ContentType is empty for file %s', entry.url);
  58. } else {
  59. debug('ContentType is empty for file %s. It should be %s.', entry.url, foundType.mimes[0]);
  60. entry.oldContentType = null;
  61. rewriteContentType(entry, foundType);
  62. }
  63. } else {
  64. if (foundType !== null && foundType.mimes.indexOf(entry.contentType) === -1) {
  65. debug('ContentType %s is wrong for %s. It should be %s.', entry.contentType, entry.url, foundType.mimes[0]);
  66. entry.oldContentType = entry.contentType;
  67. rewriteContentType(entry, foundType);
  68. }
  69. }
  70. } catch(err) {
  71. debug('Error while analyzing the contentType of %s', entry.url);
  72. debug(err);
  73. }
  74. }
  75. deferred.resolve(entry);
  76. return deferred.promise;
  77. }
  78. function findContentType(bodyBuffer) {
  79. var bodyStr = bodyBuffer.toString();
  80. if (isJpg(bodyBuffer)) {
  81. return contentTypes.jpeg;
  82. }
  83. if (isPng(bodyBuffer)) {
  84. return contentTypes.png;
  85. }
  86. // https://github.com/sindresorhus/is-svg/issues/7
  87. if (/<svg/.test(bodyStr) && isSvg(bodyStr)) {
  88. return contentTypes.svg;
  89. }
  90. if (isGif(bodyBuffer)) {
  91. return contentTypes.gif;
  92. }
  93. if (isWebp(bodyBuffer)) {
  94. return contentTypes.webp;
  95. }
  96. if (isWoff(bodyBuffer)) {
  97. return contentTypes.woff;
  98. }
  99. if (isWoff2(bodyBuffer)) {
  100. return contentTypes.woff2;
  101. }
  102. if (isOtf(bodyBuffer)) {
  103. return contentTypes.otf;
  104. }
  105. if (isTtf(bodyBuffer)) {
  106. return contentTypes.ttf;
  107. }
  108. if (isEot(bodyBuffer)) {
  109. return contentTypes.eot;
  110. }
  111. return null;
  112. }
  113. function rewriteContentType(entry, contentTypeObj) {
  114. delete(entry.isHTML);
  115. delete(entry.isXML);
  116. delete(entry.isCSS);
  117. delete(entry.isJS);
  118. delete(entry.isJSON);
  119. delete(entry.isImage);
  120. delete(entry.isSVG);
  121. delete(entry.isVideo);
  122. delete(entry.isWebFont);
  123. delete(entry.isTTF);
  124. delete(entry.isFavicon);
  125. entry.contentType = contentTypeObj.mimes[0];
  126. contentTypeObj.updateFn(entry);
  127. }
  128. var contentTypes = {
  129. jpeg: {
  130. mimes: ['image/jpeg'],
  131. updateFn: function(entry) {
  132. entry.type = 'image';
  133. entry.isImage = true;
  134. }
  135. },
  136. png: {
  137. mimes: ['image/png'],
  138. updateFn: function(entry) {
  139. entry.type = 'image';
  140. entry.isImage = true;
  141. }
  142. },
  143. svg: {
  144. mimes: ['image/svg+xml'],
  145. updateFn: function(entry) {
  146. entry.type = 'image';
  147. entry.isImage = true;
  148. entry.isSVG = true;
  149. }
  150. },
  151. gif: {
  152. mimes: ['image/gif'],
  153. updateFn: function(entry) {
  154. entry.type = 'image';
  155. entry.isImage = true;
  156. }
  157. },
  158. webp: {
  159. mimes: ['image/webp'],
  160. updateFn: function(entry) {
  161. entry.type = 'image';
  162. entry.isImage = true;
  163. }
  164. },
  165. woff: {
  166. mimes: ['application/x-font-woff', 'application/font-woff', 'font/woff'],
  167. updateFn: function(entry) {
  168. entry.type = 'webfont';
  169. entry.isWebFont = true;
  170. }
  171. },
  172. woff2: {
  173. mimes: ['font/woff2', 'application/x-font-woff2', 'application/font-woff2'],
  174. updateFn: function(entry) {
  175. entry.type = 'webfont';
  176. entry.isWebFont = true;
  177. }
  178. },
  179. otf: {
  180. mimes: ['application/x-font-otf', 'font/otf', 'font/opentype', 'application/x-font-opentype'],
  181. updateFn: function(entry) {
  182. entry.type = 'webfont';
  183. entry.isWebFont = true;
  184. }
  185. },
  186. ttf: {
  187. mimes: ['application/x-font-ttf', 'font/ttf', 'application/x-font-truetype'],
  188. updateFn: function(entry) {
  189. entry.type = 'webfont';
  190. entry.isWebFont = true;
  191. }
  192. },
  193. eot: {
  194. mimes: ['application/vnd.ms-fontobject', 'font/eot'],
  195. updateFn: function(entry) {
  196. entry.type = 'webfont';
  197. entry.isWebFont = true;
  198. }
  199. }
  200. };
  201. return {
  202. checkContentType: checkContentType,
  203. findContentType: findContentType
  204. };
  205. };
  206. module.exports = new ContentTypeChecker();