Browse Source

refactored code

Gildas 7 years ago
parent
commit
7f3f5c466b
1 changed files with 66 additions and 57 deletions
  1. 66 57
      lib/single-file/css-fonts-minifier.js

+ 66 - 57
lib/single-file/css-fonts-minifier.js

@@ -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 => {