index.js 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. 'use strict';
  2. Object.defineProperty(exports, "__esModule", {
  3. value: true
  4. });
  5. var _postcss = require('postcss');
  6. var _postcss2 = _interopRequireDefault(_postcss);
  7. var _postcssValueParser = require('postcss-value-parser');
  8. var _postcssValueParser2 = _interopRequireDefault(_postcssValueParser);
  9. var _svgo = require('svgo');
  10. var _svgo2 = _interopRequireDefault(_svgo);
  11. var _url = require('./lib/url');
  12. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
  13. const PLUGIN = 'postcss-svgo';
  14. const dataURI = /data:image\/svg\+xml(;((charset=)?utf-8|base64))?,/i;
  15. const dataURIBase64 = /data:image\/svg\+xml;base64,/i;
  16. function minifyPromise(decl, getSvgo, opts, postcssResult) {
  17. const promises = [];
  18. const parsed = (0, _postcssValueParser2.default)(decl.value);
  19. decl.value = parsed.walk(node => {
  20. if (node.type !== 'function' || node.value.toLowerCase() !== 'url' || !node.nodes.length) {
  21. return;
  22. }
  23. let { value, quote } = node.nodes[0];
  24. let isBase64, isUriEncoded;
  25. let svg = value.replace(dataURI, '');
  26. if (dataURIBase64.test(value)) {
  27. svg = Buffer.from(svg, 'base64').toString('utf8');
  28. isBase64 = true;
  29. } else {
  30. let decodedUri;
  31. try {
  32. decodedUri = (0, _url.decode)(svg);
  33. isUriEncoded = decodedUri !== svg;
  34. } catch (e) {
  35. // Swallow exception if we cannot decode the value
  36. isUriEncoded = false;
  37. }
  38. if (isUriEncoded) {
  39. svg = decodedUri;
  40. }
  41. if (opts.encode !== undefined) {
  42. isUriEncoded = opts.encode;
  43. }
  44. }
  45. promises.push(getSvgo().optimize(svg).then(result => {
  46. if (result.error) {
  47. decl.warn(postcssResult, `${result.error}`);
  48. return;
  49. }
  50. let data, optimizedValue;
  51. if (isBase64) {
  52. data = Buffer.from(result.data).toString('base64');
  53. optimizedValue = 'data:image/svg+xml;base64,' + data;
  54. } else {
  55. data = isUriEncoded ? (0, _url.encode)(result.data) : result.data;
  56. // Should always encode # otherwise we yield a broken SVG
  57. // in Firefox (works in Chrome however). See this issue:
  58. // https://github.com/cssnano/cssnano/issues/245
  59. data = data.replace(/#/g, '%23');
  60. optimizedValue = 'data:image/svg+xml;charset=utf-8,' + data;
  61. quote = isUriEncoded ? '"' : '\'';
  62. }
  63. node.nodes[0] = Object.assign({}, node.nodes[0], {
  64. value: optimizedValue,
  65. quote: quote,
  66. type: 'string',
  67. before: '',
  68. after: ''
  69. });
  70. }).catch(error => {
  71. decl.warn(postcssResult, `${error}`);
  72. }));
  73. return false;
  74. });
  75. return Promise.all(promises).then(() => decl.value = decl.value.toString());
  76. }
  77. exports.default = _postcss2.default.plugin(PLUGIN, (opts = {}) => {
  78. let svgo = null;
  79. const getSvgo = () => {
  80. if (!svgo) {
  81. svgo = new _svgo2.default(opts);
  82. }
  83. return svgo;
  84. };
  85. return (css, result) => {
  86. return new Promise((resolve, reject) => {
  87. const svgoQueue = [];
  88. css.walkDecls(decl => {
  89. if (!dataURI.test(decl.value)) {
  90. return;
  91. }
  92. svgoQueue.push(minifyPromise(decl, getSvgo, opts, result));
  93. });
  94. return Promise.all(svgoQueue).then(resolve, reject);
  95. });
  96. };
  97. });
  98. module.exports = exports['default'];