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

var helpers_js = require('../helpers.js');

const postcssOptions = {
    // Prevent the following warning from being shown:
    // > Without `from` option PostCSS could generate wrong source map and will not find Browserslist config.
    // > Set it to CSS file path or to `undefined` to prevent this warning.
    from: undefined
};
const inlineCssExcludedPlugins = {
    mergeRules: false,
    minifySelectors: false,
    minifyParams: false,
    normalizeCharset: false,
    uniqueSelectors: false,
    normalizeUnicode: false
};
/** Minify CSS with cssnano */ const mod = {
    async default (tree, _, cssnanoOptions) {
        const cssnano = await helpers_js.optionalImport('cssnano');
        const postcss = await helpers_js.optionalImport('postcss');
        if (!cssnano || !postcss) {
            return tree;
        }
        const processor = createCssProcessor(postcss, cssnano, cssnanoOptions);
        const inlineStyleProcessor = createCssProcessor(postcss, cssnano, getInlineCssnanoOptions(cssnanoOptions));
        const minifiedCssCache = new Map();
        const promises = [];
        let p;
        tree.walk((node)=>{
            // Skip SRI, reasons are documented in "minifyJs" module
            if (node.attrs && 'integrity' in node.attrs) {
                return node;
            }
            if (helpers_js.isStyleNode(node) && helpers_js.isCssStyleType(node)) {
                p = processStyleNode(node, processor, minifiedCssCache);
                if (p) {
                    promises.push(p);
                }
            } else if (node.attrs && node.attrs.style) {
                p = processStyleAttr(node, inlineStyleProcessor, minifiedCssCache);
                if (p) {
                    promises.push(p);
                }
            }
            return node;
        });
        return Promise.all(promises).then(()=>tree);
    }
};
function createCssProcessor(postcss, cssnano, cssnanoOptions) {
    return postcss([
        cssnano(cssnanoOptions)
    ]);
}
function getInlineCssnanoOptions(cssnanoOptions) {
    if (!cssnanoOptions || typeof cssnanoOptions !== 'object') {
        return {
            preset: [
                'default',
                inlineCssExcludedPlugins
            ]
        };
    }
    if (Array.isArray(cssnanoOptions.plugins)) {
        return cssnanoOptions;
    }
    if (!('preset' in cssnanoOptions) || cssnanoOptions.preset === undefined) {
        return {
            ...cssnanoOptions,
            preset: [
                'default',
                inlineCssExcludedPlugins
            ]
        };
    }
    if (cssnanoOptions.preset === 'default') {
        return {
            ...cssnanoOptions,
            preset: [
                'default',
                inlineCssExcludedPlugins
            ]
        };
    }
    if (Array.isArray(cssnanoOptions.preset) && cssnanoOptions.preset[0] === 'default') {
        const presetOptions = cssnanoOptions.preset[1];
        return {
            ...cssnanoOptions,
            preset: [
                'default',
                {
                    ...inlineCssExcludedPlugins,
                    ...presetOptions && typeof presetOptions === 'object' ? presetOptions : {}
                }
            ]
        };
    }
    return cssnanoOptions;
}
function processStyleNode(styleNode, processor, minifiedCssCache) {
    let css = helpers_js.extractCssFromStyleNode(styleNode);
    if (!css || css.trim() === '') return;
    // Improve performance by avoiding calling stripCssCdata again and again
    const { strippedCss, isCdataWrapped } = helpers_js.stripCssCdata(css);
    css = strippedCss;
    return processCss(processor, minifiedCssCache, `${isCdataWrapped ? 'style-cdata:' : 'style:'}${css}`, css, isCdataWrapped).then((minifiedCss)=>{
        styleNode.content = [
            helpers_js.wrapCssCdata(minifiedCss, isCdataWrapped)
        ];
    });
}
function processStyleAttr(node, processor, minifiedCssCache) {
    // CSS "color: red;" is invalid. Therefore it should be wrapped inside some selector:
    // a{color: red;}
    const wrapperStart = 'a{';
    const wrapperEnd = '}';
    if (!node.attrs || !node.attrs.style || typeof node.attrs.style !== 'string') {
        return;
    }
    if (node.attrs.style.trim() === '') {
        return;
    }
    const wrappedStyle = wrapperStart + (node.attrs.style || '') + wrapperEnd;
    return processCss(processor, minifiedCssCache, `attr:${wrappedStyle}`, wrappedStyle).then((minifiedCss)=>{
        // Remove wrapperStart at the start and wrapperEnd at the end of minifiedCss
        node.attrs.style = minifiedCss.substring(wrapperStart.length, minifiedCss.length - wrapperEnd.length);
    });
}
function processCss(processor, minifiedCssCache, cacheKey, css, useToString = false) {
    let minifiedCss = minifiedCssCache.get(cacheKey);
    if (!minifiedCss) {
        minifiedCss = processor.process(css, postcssOptions).then((result)=>useToString ? result.toString() : result.css).catch((error)=>{
            minifiedCssCache.delete(cacheKey);
            throw error;
        });
        minifiedCssCache.set(cacheKey, minifiedCss);
    }
    return minifiedCss;
}

exports.default = mod;
exports.getInlineCssnanoOptions = getInlineCssnanoOptions;
