url.js 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. /*!
  2. * Stylus - plugin - url
  3. * Copyright (c) Automattic <developer.wordpress.com>
  4. * MIT Licensed
  5. */
  6. /**
  7. * Module dependencies.
  8. */
  9. var Compiler = require('../visitor/compiler')
  10. , events = require('../renderer').events
  11. , nodes = require('../nodes')
  12. , parse = require('url').parse
  13. , extname = require('path').extname
  14. , utils = require('../utils')
  15. , fs = require('fs');
  16. /**
  17. * Mime table.
  18. */
  19. var defaultMimes = {
  20. '.gif': 'image/gif'
  21. , '.png': 'image/png'
  22. , '.jpg': 'image/jpeg'
  23. , '.jpeg': 'image/jpeg'
  24. , '.svg': 'image/svg+xml'
  25. , '.webp': 'image/webp'
  26. , '.ttf': 'application/x-font-ttf'
  27. , '.eot': 'application/vnd.ms-fontobject'
  28. , '.woff': 'application/font-woff'
  29. , '.woff2': 'application/font-woff2'
  30. };
  31. /**
  32. * Supported encoding types
  33. */
  34. var encodingTypes = {
  35. BASE_64: 'base64',
  36. UTF8: 'charset=utf-8'
  37. }
  38. /**
  39. * Return a url() function with the given `options`.
  40. *
  41. * Options:
  42. *
  43. * - `limit` bytesize limit defaulting to 30Kb
  44. * - `paths` image resolution path(s), merged with general lookup paths
  45. *
  46. * Examples:
  47. *
  48. * stylus(str)
  49. * .set('filename', __dirname + '/css/test.styl')
  50. * .define('url', stylus.url({ paths: [__dirname + '/public'] }))
  51. * .render(function(err, css) { ... })
  52. *
  53. * @param {Object} options
  54. * @return {Function}
  55. * @api public
  56. */
  57. module.exports = function(options) {
  58. options = options || {};
  59. var _paths = options.paths || [];
  60. var sizeLimit = null != options.limit ? options.limit : 30000;
  61. var mimes = options.mimes || defaultMimes;
  62. /**
  63. * @param {object} url - The path to the image you want to encode.
  64. * @param {object} enc - The encoding for the image. Defaults to base64, the
  65. * other valid option is `utf8`.
  66. */
  67. function fn(url, enc) {
  68. // Compile the url
  69. var compiler = new Compiler(url)
  70. , encoding = encodingTypes.BASE_64;
  71. compiler.isURL = true;
  72. url = url.nodes.map(function(node) {
  73. return compiler.visit(node);
  74. }).join('');
  75. // Parse literal
  76. url = parse(url);
  77. var ext = extname(url.pathname || '')
  78. , mime = mimes[ext]
  79. , hash = url.hash || ''
  80. , literal = new nodes.Literal('url("' + url.href + '")')
  81. , paths = _paths.concat(this.paths)
  82. , buf
  83. , result;
  84. // Not supported
  85. if(!mime) return literal;
  86. // Absolute
  87. if(url.protocol) return literal;
  88. // Lookup
  89. var found = utils.lookup(url.pathname, paths);
  90. // Failed to lookup
  91. if(!found) {
  92. events.emit(
  93. 'file not found'
  94. , 'File ' + literal + ' could not be found, literal url retained!'
  95. );
  96. return literal;
  97. }
  98. // Read data
  99. buf = fs.readFileSync(found);
  100. // Too large
  101. if(false !== sizeLimit && buf.length > sizeLimit) return literal;
  102. if(enc && 'utf8' == enc.first.val.toLowerCase()) {
  103. encoding = encodingTypes.UTF8;
  104. result = buf.toString().replace(/\s+/g, ' ')
  105. .replace(/[{}\|\\\^~\[\]`"<>#%]/g, function(match) {
  106. return '%' + match[0].charCodeAt(0).toString(16).toUpperCase();
  107. }).trim();
  108. } else {
  109. result = buf.toString(encoding) + hash;
  110. }
  111. // Encode
  112. return new nodes.Literal('url("data:' + mime + ';' + encoding + ',' + result + '")');
  113. };
  114. fn.raw = true;
  115. return fn;
  116. };
  117. // Exporting default mimes so we could easily access them
  118. module.exports.mimes = defaultMimes;