css-b64-images.js 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. 'use strict';
  2. var fs = require('fs'),
  3. Path = require('path'),
  4. MAX_SIZE = 4096,
  5. /* Adapted from https://gist.github.com/2594980 */
  6. imgRegex = /url\s?\(['"]?(.*?)(?=['"]?\))/gi,
  7. absoluteUrlRegex = /^\//,
  8. externalUrlRegex = /http/,
  9. mediatypes = {
  10. '.eot' : 'application/vnd.ms-fontobject',
  11. '.gif' : 'image/gif',
  12. '.ico' : 'image/vnd.microsoft.icon',
  13. '.jpg' : 'image/jpeg',
  14. '.jpeg' : 'image/jpeg',
  15. '.otf' : 'application/x-font-opentype',
  16. '.png' : 'image/png',
  17. '.svg' : 'image/svg+xml',
  18. '.ttf' : 'application/x-font-ttf',
  19. '.webp' : 'image/webp',
  20. '.woff' : 'application/x-font-woff',
  21. '.woff2' : 'application/font-woff2'
  22. };
  23. module.exports = {
  24. fromFile: fromFile,
  25. fromString: fromString
  26. };
  27. function fromString(css, relativePath, rootPath , options, cb) {
  28. if(!cb) {
  29. cb = options;
  30. options = {maxSize: MAX_SIZE};
  31. }
  32. if(!css.replace && css.toString) css = css.toString();
  33. var urls = [],
  34. match = imgRegex.exec(css);
  35. while(match) {
  36. urls.push(match[1]);
  37. match = imgRegex.exec(css)
  38. }
  39. forEachSeries(urls, base64img, function(err){
  40. if(err) return cb(err, css);
  41. cb(null, css);
  42. });
  43. function base64img(imageUrl, cb){
  44. if(externalUrlRegex.test(imageUrl)) {
  45. return cb(new Error('Skip ' + imageUrl + ' External file.'), css);
  46. }
  47. var imagePath;
  48. if(absoluteUrlRegex.test(imageUrl)) {
  49. imagePath = Path.join(rootPath, imageUrl.substr(1));
  50. }else{
  51. imagePath = Path.join(relativePath, imageUrl);
  52. }
  53. replaceUrlByB64(imageUrl, imagePath, css, options, function (err, newCss){
  54. if(err) return cb(err, css);
  55. css = newCss;
  56. cb();
  57. });
  58. }
  59. }
  60. function fromFile(cssFile, root, options, cb) {
  61. if(!cb) {
  62. cb = options;
  63. options = {maxSize: MAX_SIZE};
  64. }
  65. fs.readFile(cssFile, function(err, css){
  66. if(err) return cb(err, css);
  67. fromString(css.toString(), Path.dirname(cssFile), root, options, cb);
  68. });
  69. }
  70. function replaceUrlByB64(imageUrl, imagePath, css, options, cb){
  71. imagePath = imagePath.replace(/[?#].*/g, '');
  72. fs.stat(imagePath, function(err, stat){
  73. if(err) return cb(err, css);
  74. if (stat.size > options.maxSize){
  75. return cb(new Error('Skip ' + imageUrl + ' Exceed max size'), css);
  76. }
  77. fs.readFile(imagePath, 'base64', function(err, img){
  78. if(err) return cb(err, css);
  79. var ext = Path.extname(imagePath);
  80. var newCss = css.replace(imageUrl, 'data:' + mediatypes[ext] + ';base64,' + img);
  81. cb(null, newCss);
  82. });
  83. });
  84. }
  85. /* Adapted from async. Continue on error. */
  86. function forEachSeries(arr, iterator, callback) {
  87. callback = callback || function () {};
  88. if (!arr.length) {
  89. return callback();
  90. }
  91. var completed = 0, errs = [];
  92. var iterate = function () {
  93. iterator(arr[completed], function (err) {
  94. if (err) {
  95. errs.push(err);
  96. }
  97. completed += 1;
  98. if (completed === arr.length) {
  99. if(errs.length) return callback(errs);
  100. callback(null);
  101. }
  102. else {
  103. iterate();
  104. }
  105. });
  106. };
  107. iterate();
  108. }