"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports["default"] = removeUnusedCss; var _helpers = require("../helpers"); var _uncss = _interopRequireDefault(require("uncss")); var _purgecss = _interopRequireDefault(require("purgecss")); var _posthtmlRender = _interopRequireDefault(require("posthtml-render")); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); } function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && Symbol.iterator in Object(iter)) return Array.from(iter); } function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); } function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; } function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } // These options must be set and shouldn't be overriden to ensure uncss doesn't look at linked stylesheets. var uncssOptions = { ignoreSheets: [/\s*/], stylesheets: [] }; function processStyleNodeUnCSS(html, styleNode, uncssOptions) { var css = (0, _helpers.extractCssFromStyleNode)(styleNode); return runUncss(html, css, uncssOptions).then(function (css) { // uncss may have left some style tags empty if (css.trim().length === 0) { styleNode.tag = false; styleNode.content = []; return; } styleNode.content = [css]; }); } function runUncss(html, css, userOptions) { if (_typeof(userOptions) !== 'object') { userOptions = {}; } var options = Object.assign({}, userOptions, uncssOptions); return new Promise(function (resolve, reject) { options.raw = css; (0, _uncss["default"])(html, options, function (error, output) { if (error) { reject(error); return; } resolve(output); }); }); } var purgeFromHtml = function purgeFromHtml(tree) { // content is not used as we can directly used the parsed HTML, // making the process faster var selectors = []; tree.walk(function (node) { var classes = node.attrs && node.attrs["class"] && node.attrs["class"].split(' ') || []; var ids = node.attrs && node.attrs.id && node.attrs.id.split(' ') || []; selectors.push.apply(selectors, _toConsumableArray(classes).concat(_toConsumableArray(ids))); node.tag && selectors.push(node.tag); return node; }); return function () { return selectors; }; }; function processStyleNodePurgeCSS(tree, styleNode, purgecssOptions) { var css = (0, _helpers.extractCssFromStyleNode)(styleNode); return runPurgecss(tree, css, purgecssOptions).then(function (css) { if (css.trim().length === 0) { styleNode.tag = false; styleNode.content = []; return; } styleNode.content = [css]; }); } function runPurgecss(tree, css, userOptions) { if (_typeof(userOptions) !== 'object') { userOptions = {}; } var options = Object.assign({}, userOptions, { content: [{ raw: tree, extension: 'html' }], css: [{ raw: css, extension: 'css' }], extractors: [{ extractor: purgeFromHtml(tree), extensions: ['html'] }] }); return new _purgecss["default"]().purge(options).then(function (result) { return result[0].css; }); } /** Remove unused CSS */ function removeUnusedCss(tree, options, userOptions) { var promises = []; var html = userOptions.tool !== 'purgeCSS' && (0, _posthtmlRender["default"])(tree); tree.walk(function (node) { if ((0, _helpers.isStyleNode)(node)) { if (userOptions.tool === 'purgeCSS') { promises.push(processStyleNodePurgeCSS(tree, node, userOptions)); } else { promises.push(processStyleNodeUnCSS(html, node, userOptions)); } } return node; }); return Promise.all(promises).then(function () { return tree; }); }