|
|
@@ -18,13 +18,21 @@
|
|
|
* along with SingleFile. If not, see <http://www.gnu.org/licenses/>.
|
|
|
*/
|
|
|
|
|
|
+/* global CSSRule */
|
|
|
+
|
|
|
this.rulesMinifier = this.rulesMinifier || (() => {
|
|
|
|
|
|
const REGEXP_PSEUDO_CLASSES = /::after|::before|::first-line|::first-letter|:focus|:focus-within|:hover|:link|:visited|:active/gi;
|
|
|
|
|
|
return {
|
|
|
process: doc => {
|
|
|
- const rulesCache = {};
|
|
|
+ const rulesCache = {
|
|
|
+ selectors: {},
|
|
|
+ fonts: {
|
|
|
+ declared: {},
|
|
|
+ used: {}
|
|
|
+ }
|
|
|
+ };
|
|
|
const stats = {
|
|
|
processed: 0,
|
|
|
discarded: 0
|
|
|
@@ -37,6 +45,14 @@ this.rulesMinifier = this.rulesMinifier || (() => {
|
|
|
stats.discarded += processed - style.sheet.cssRules.length;
|
|
|
}
|
|
|
});
|
|
|
+ const unusedFonts = Object.keys(rulesCache.fonts.declared).filter(fontFamily => !Object.keys(rulesCache.fonts.used).includes(fontFamily));
|
|
|
+ doc.querySelectorAll("style").forEach(style => {
|
|
|
+ if (style.sheet) {
|
|
|
+ const processed = style.sheet.cssRules.length;
|
|
|
+ style.textContent = deleteUnusedFonts(doc, style.sheet.cssRules, unusedFonts);
|
|
|
+ stats.discarded += processed - style.sheet.cssRules.length;
|
|
|
+ }
|
|
|
+ });
|
|
|
return stats;
|
|
|
}
|
|
|
};
|
|
|
@@ -53,14 +69,40 @@ this.rulesMinifier = this.rulesMinifier || (() => {
|
|
|
const selector = getFilteredSelector(rule.selectorText);
|
|
|
if (selector) {
|
|
|
try {
|
|
|
- if (cache[selector] || doc.querySelector(selector)) {
|
|
|
+ if (cache.selectors[selector] || doc.querySelector(selector)) {
|
|
|
stylesheetContent += rule.cssText;
|
|
|
- cache[selector] = true;
|
|
|
+ cache.selectors[selector] = true;
|
|
|
+ if (rule.style.fontFamily) {
|
|
|
+ rule.style.fontFamily.split(",").forEach(fontFamily => cache.fonts.used[fontFamily.trim()] = true);
|
|
|
+ }
|
|
|
}
|
|
|
} catch (error) {
|
|
|
stylesheetContent += rule.cssText;
|
|
|
}
|
|
|
}
|
|
|
+ } else {
|
|
|
+ if (rule.type == CSSRule.FONT_FACE_RULE) {
|
|
|
+ cache.fonts.declared[rule.style.fontFamily.trim()] = true;
|
|
|
+ }
|
|
|
+ stylesheetContent += rule.cssText;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+ return stylesheetContent;
|
|
|
+ }
|
|
|
+
|
|
|
+ function deleteUnusedFonts(doc, rules, unusedFonts) {
|
|
|
+ let stylesheetContent = "";
|
|
|
+ if (rules) {
|
|
|
+ Array.from(rules).forEach(rule => {
|
|
|
+ if (rule.media) {
|
|
|
+ stylesheetContent += "@media " + Array.prototype.join.call(rule.media, ",") + " {";
|
|
|
+ stylesheetContent += deleteUnusedFonts(doc, rule.cssRules, unusedFonts);
|
|
|
+ stylesheetContent += "}";
|
|
|
+ } else if (rule.selectorText) {
|
|
|
+ stylesheetContent += rule.cssText;
|
|
|
+ } else if (rule.type == CSSRule.FONT_FACE_RULE && !unusedFonts.includes(rule.style.fontFamily.trim())) {
|
|
|
+ stylesheetContent += rule.cssText;
|
|
|
} else {
|
|
|
stylesheetContent += rule.cssText;
|
|
|
}
|