index.js 3.7 KB
'use strict';

Object.defineProperty(exports, "__esModule", {
    value: true
});

var _postcss = require('postcss');

var _postcss2 = _interopRequireDefault(_postcss);

var _postcssValueParser = require('postcss-value-parser');

var _postcssValueParser2 = _interopRequireDefault(_postcssValueParser);

var _svgo = require('svgo');

var _svgo2 = _interopRequireDefault(_svgo);

var _isSvg = require('is-svg');

var _isSvg2 = _interopRequireDefault(_isSvg);

var _url = require('./lib/url');

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

const PLUGIN = 'postcss-svgo';
const dataURI = /data:image\/svg\+xml(;((charset=)?utf-8|base64))?,/i;
const dataURIBase64 = /data:image\/svg\+xml;base64,/i;

function minifyPromise(decl, getSvgo, opts) {
    const promises = [];
    const parsed = (0, _postcssValueParser2.default)(decl.value);

    decl.value = parsed.walk(node => {
        if (node.type !== 'function' || node.value.toLowerCase() !== 'url' || !node.nodes.length) {
            return;
        }

        let { value, quote } = node.nodes[0];
        let isBase64, isUriEncoded;
        let svg = value.replace(dataURI, '');

        if (dataURIBase64.test(value)) {
            svg = Buffer.from(svg, 'base64').toString('utf8');
            isBase64 = true;
        } else {
            let decodedUri;

            try {
                decodedUri = (0, _url.decode)(svg);
                isUriEncoded = decodedUri !== svg;
            } catch (e) {
                // Swallow exception if we cannot decode the value
                isUriEncoded = false;
            }

            if (isUriEncoded) {
                svg = decodedUri;
            }

            if (opts.encode !== undefined) {
                isUriEncoded = opts.encode;
            }
        }

        if (!(0, _isSvg2.default)(svg)) {
            return;
        }

        promises.push(getSvgo().optimize(svg).then(result => {
            let data, optimizedValue;

            if (isBase64) {
                data = Buffer.from(result.data).toString('base64');
                optimizedValue = 'data:image/svg+xml;base64,' + data;
            } else {
                data = isUriEncoded ? (0, _url.encode)(result.data) : result.data;
                // Should always encode # otherwise we yield a broken SVG
                // in Firefox (works in Chrome however). See this issue:
                // https://github.com/cssnano/cssnano/issues/245
                data = data.replace(/#/g, '%23');
                optimizedValue = 'data:image/svg+xml;charset=utf-8,' + data;
                quote = isUriEncoded ? '"' : '\'';
            }

            node.nodes[0] = Object.assign({}, node.nodes[0], {
                value: optimizedValue,
                quote: quote,
                type: 'string',
                before: '',
                after: ''
            });
        }).catch(error => {
            throw new Error(`${PLUGIN}: ${error}`);
        }));

        return false;
    });

    return Promise.all(promises).then(() => decl.value = decl.value.toString());
}

exports.default = _postcss2.default.plugin(PLUGIN, (opts = {}) => {
    let svgo = null;

    const getSvgo = () => {
        if (!svgo) {
            svgo = new _svgo2.default(opts);
        }

        return svgo;
    };

    return css => {
        return new Promise((resolve, reject) => {
            const svgoQueue = [];

            css.walkDecls(decl => {
                if (!dataURI.test(decl.value)) {
                    return;
                }

                svgoQueue.push(minifyPromise(decl, getSvgo, opts));
            });

            return Promise.all(svgoQueue).then(resolve, reject);
        });
    };
});
module.exports = exports['default'];