|
|
@@ -57,9 +57,6 @@ this.fontsMinifier = this.fontsMinifier || (() => {
|
|
|
|
|
|
return {
|
|
|
removeUnusedFonts: doc => {
|
|
|
- const declaredFonts = new Set();
|
|
|
- const fontsDetails = new Map();
|
|
|
- const usedFonts = [];
|
|
|
const stats = {
|
|
|
rules: {
|
|
|
processed: 0,
|
|
|
@@ -70,41 +67,40 @@ this.fontsMinifier = this.fontsMinifier || (() => {
|
|
|
discarded: 0
|
|
|
}
|
|
|
};
|
|
|
- let pseudoElementsContent = Array.from(doc.querySelectorAll("style")).map(style => {
|
|
|
- if (style.sheet) {
|
|
|
- return getPseudoElementsContent(doc, style.sheet.cssRules);
|
|
|
- }
|
|
|
- }).join("");
|
|
|
+ const fontsInfo = { declared: new Set(), used: [] };
|
|
|
+ let pseudoElementsContent = "";
|
|
|
doc.querySelectorAll("style").forEach(style => {
|
|
|
if (style.sheet) {
|
|
|
stats.rules.processed += style.sheet.cssRules.length;
|
|
|
stats.rules.discarded += style.sheet.cssRules.length;
|
|
|
- processRules(doc, style.sheet.cssRules, fontsDetails, declaredFonts, usedFonts, pseudoElementsContent, false);
|
|
|
+ getFontsInfo(doc, style.sheet.cssRules, fontsInfo);
|
|
|
+ pseudoElementsContent += getPseudoElementsContent(doc, style.sheet.cssRules);
|
|
|
}
|
|
|
});
|
|
|
doc.querySelectorAll("[style]").forEach(element => {
|
|
|
if (element.style.fontFamily) {
|
|
|
const fontFamilyNames = element.style.fontFamily.split(",").map(fontFamilyName => removeQuotes(fontFamilyName));
|
|
|
- usedFonts.push(fontFamilyNames);
|
|
|
+ fontsInfo.used.push(fontFamilyNames);
|
|
|
}
|
|
|
});
|
|
|
- const variableFound = usedFonts.find(fontNames => fontNames.find(fontName => fontName.startsWith("var(--")));
|
|
|
+ const variableFound = fontsInfo.used.find(fontNames => fontNames.find(fontName => fontName.startsWith("var(--")));
|
|
|
let unusedFonts;
|
|
|
if (variableFound) {
|
|
|
unusedFonts = [];
|
|
|
} else {
|
|
|
const filteredUsedFonts = new Set();
|
|
|
- usedFonts.forEach(fontNames => fontNames.forEach(fontName => {
|
|
|
- if (declaredFonts.has(fontName)) {
|
|
|
+ fontsInfo.used.forEach(fontNames => fontNames.forEach(fontName => {
|
|
|
+ if (fontsInfo.declared.has(fontName)) {
|
|
|
filteredUsedFonts.add(fontName);
|
|
|
}
|
|
|
}));
|
|
|
- unusedFonts = Array.from(declaredFonts).filter(fontFamilyName => !filteredUsedFonts.has(fontFamilyName));
|
|
|
+ unusedFonts = Array.from(fontsInfo.declared).filter(familyName => !filteredUsedFonts.has(familyName));
|
|
|
}
|
|
|
+ const docContent = doc.body.innerText + pseudoElementsContent;
|
|
|
doc.querySelectorAll("style").forEach(style => {
|
|
|
if (style.sheet) {
|
|
|
stats.fonts.discarded += style.sheet.cssRules.length;
|
|
|
- style.textContent = deleteUnusedFonts(doc, style.sheet.cssRules, unusedFonts);
|
|
|
+ style.textContent = filterUnusedFonts(doc, style.sheet.cssRules, unusedFonts, docContent);
|
|
|
stats.fonts.discarded -= style.sheet.cssRules.length;
|
|
|
stats.rules.discarded -= style.sheet.cssRules.length;
|
|
|
}
|
|
|
@@ -112,9 +108,7 @@ this.fontsMinifier = this.fontsMinifier || (() => {
|
|
|
return stats;
|
|
|
},
|
|
|
removeAlternativeFonts: doc => {
|
|
|
- const declaredFonts = new Set();
|
|
|
const fontsDetails = new Map();
|
|
|
- const usedFonts = [];
|
|
|
const stats = {
|
|
|
rules: {
|
|
|
processed: 0,
|
|
|
@@ -125,12 +119,11 @@ this.fontsMinifier = this.fontsMinifier || (() => {
|
|
|
discarded: 0
|
|
|
}
|
|
|
};
|
|
|
- let pseudoElementsContent = "";
|
|
|
doc.querySelectorAll("style").forEach(style => {
|
|
|
if (style.sheet) {
|
|
|
stats.rules.processed += style.sheet.cssRules.length;
|
|
|
stats.rules.discarded += style.sheet.cssRules.length;
|
|
|
- processRules(doc, style.sheet.cssRules, fontsDetails, declaredFonts, usedFonts, pseudoElementsContent, true);
|
|
|
+ getFontsDetails(doc, style.sheet.cssRules, fontsDetails);
|
|
|
}
|
|
|
});
|
|
|
doc.querySelectorAll("style").forEach(style => {
|
|
|
@@ -142,51 +135,46 @@ this.fontsMinifier = this.fontsMinifier || (() => {
|
|
|
}
|
|
|
};
|
|
|
|
|
|
- function getPseudoElementsContent(doc, rules) {
|
|
|
- if (rules) {
|
|
|
- return Array.from(rules).map(rule => {
|
|
|
- if (rule.type == CSSRule.MEDIA_RULE) {
|
|
|
- return getPseudoElementsContent(doc, rule.cssRules);
|
|
|
- } else if (rule.type == CSSRule.STYLE_RULE && testPseudoElements(rule.selectorText)) {
|
|
|
- let content = rule.style.getPropertyValue("content");
|
|
|
- content = content && removeQuotes(content);
|
|
|
- return content;
|
|
|
- }
|
|
|
- }).join("");
|
|
|
- } else {
|
|
|
- return "";
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- function processRules(doc, rules, fontsDetails, declaredFonts, usedFonts, pseudoElementsContent, secondPass) {
|
|
|
+ function getFontsInfo(doc, rules, fontsInfo) {
|
|
|
if (rules) {
|
|
|
Array.from(rules).forEach(rule => {
|
|
|
if (rule.type == CSSRule.MEDIA_RULE) {
|
|
|
- processRules(doc, rule.cssRules, fontsDetails, declaredFonts, usedFonts, pseudoElementsContent, secondPass);
|
|
|
+ getFontsInfo(doc, rule.cssRules, fontsInfo);
|
|
|
} else if (rule.type == CSSRule.STYLE_RULE) {
|
|
|
if (rule.style && rule.style.fontFamily) {
|
|
|
const fontFamilyNames = rule.style.fontFamily.split(",").map(fontFamilyName => removeQuotes(fontFamilyName));
|
|
|
- usedFonts.push(fontFamilyNames);
|
|
|
+ fontsInfo.used.push(fontFamilyNames);
|
|
|
}
|
|
|
} else {
|
|
|
if (rule.type == CSSRule.FONT_FACE_RULE && rule.style) {
|
|
|
const fontFamilyName = removeQuotes(rule.style.getPropertyValue("font-family"));
|
|
|
if (fontFamilyName) {
|
|
|
- declaredFonts.add(fontFamilyName);
|
|
|
+ fontsInfo.declared.add(fontFamilyName);
|
|
|
}
|
|
|
- if (secondPass || testUnicodeRange(doc.body.innerText + pseudoElementsContent, rule)) {
|
|
|
- const fontKey = getFontKey(rule.style);
|
|
|
- let fontInfo = fontsDetails.get(fontKey);
|
|
|
- if (!fontInfo) {
|
|
|
- fontInfo = [];
|
|
|
- fontsDetails.set(fontKey, fontInfo);
|
|
|
- }
|
|
|
- const src = rule.style.getPropertyValue("src");
|
|
|
- if (src) {
|
|
|
- const fontSources = src.match(REGEXP_URL_FUNCTION);
|
|
|
- if (fontSources) {
|
|
|
- fontSources.forEach(source => fontInfo.unshift(source));
|
|
|
- }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ function getFontsDetails(doc, rules, fontsDetails) {
|
|
|
+ if (rules) {
|
|
|
+ Array.from(rules).forEach(rule => {
|
|
|
+ if (rule.type == CSSRule.MEDIA_RULE) {
|
|
|
+ getFontsDetails(doc, rule.cssRules, fontsDetails);
|
|
|
+ } else {
|
|
|
+ if (rule.type == CSSRule.FONT_FACE_RULE && rule.style) {
|
|
|
+ const fontKey = getFontKey(rule.style);
|
|
|
+ let fontInfo = fontsDetails.get(fontKey);
|
|
|
+ if (!fontInfo) {
|
|
|
+ fontInfo = [];
|
|
|
+ fontsDetails.set(fontKey, fontInfo);
|
|
|
+ }
|
|
|
+ const src = rule.style.getPropertyValue("src");
|
|
|
+ if (src) {
|
|
|
+ const fontSources = src.match(REGEXP_URL_FUNCTION);
|
|
|
+ if (fontSources) {
|
|
|
+ fontSources.forEach(source => fontInfo.unshift(source));
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
@@ -290,16 +278,22 @@ this.fontsMinifier = this.fontsMinifier || (() => {
|
|
|
return cssText;
|
|
|
}
|
|
|
|
|
|
- function deleteUnusedFonts(doc, rules, unusedFonts) {
|
|
|
+ function filterUnusedFonts(doc, rules, unusedFonts, docContent) {
|
|
|
let stylesheetContent = "";
|
|
|
if (rules) {
|
|
|
Array.from(rules).forEach(rule => {
|
|
|
const fontFamilyName = rule.style && rule.style.getPropertyValue("font-family");
|
|
|
if (rule.media) {
|
|
|
stylesheetContent += "@media " + Array.prototype.join.call(rule.media, ",") + "{";
|
|
|
- stylesheetContent += deleteUnusedFonts(doc, rule.cssRules, unusedFonts);
|
|
|
+ stylesheetContent += filterUnusedFonts(doc, rule.cssRules, unusedFonts, docContent);
|
|
|
stylesheetContent += "}";
|
|
|
- } else if (rule.type != CSSRule.FONT_FACE_RULE || (rule.type == CSSRule.FONT_FACE_RULE && rule.style && fontFamilyName && !unusedFonts.includes(removeQuotes(fontFamilyName)))) {
|
|
|
+ } else if (rule.type == CSSRule.FONT_FACE_RULE) {
|
|
|
+ if (rule.style && fontFamilyName && !unusedFonts.includes(removeQuotes(fontFamilyName))) {
|
|
|
+ if (testUnicodeRange(docContent, rule.style.getPropertyValue("unicode-range"))) {
|
|
|
+ stylesheetContent += rule.cssText;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
stylesheetContent += rule.cssText;
|
|
|
}
|
|
|
});
|
|
|
@@ -307,8 +301,23 @@ this.fontsMinifier = this.fontsMinifier || (() => {
|
|
|
return stylesheetContent;
|
|
|
}
|
|
|
|
|
|
- function testUnicodeRange(docContent, rule) {
|
|
|
- const unicodeRange = rule.style.getPropertyValue("unicode-range");
|
|
|
+ function getPseudoElementsContent(doc, rules) {
|
|
|
+ if (rules) {
|
|
|
+ return Array.from(rules).map(rule => {
|
|
|
+ if (rule.type == CSSRule.MEDIA_RULE) {
|
|
|
+ return getPseudoElementsContent(doc, rule.cssRules);
|
|
|
+ } else if (rule.type == CSSRule.STYLE_RULE && testPseudoElements(rule.selectorText)) {
|
|
|
+ let content = rule.style.getPropertyValue("content");
|
|
|
+ content = content && removeQuotes(content);
|
|
|
+ return content;
|
|
|
+ }
|
|
|
+ }).join("");
|
|
|
+ } else {
|
|
|
+ return "";
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ function testUnicodeRange(docContent, unicodeRange) {
|
|
|
if (unicodeRange) {
|
|
|
const unicodeRanges = unicodeRange.split(REGEXP_COMMA);
|
|
|
const result = unicodeRanges.filter(rangeValue => {
|