Forráskód Böngészése

optimized code for speed

Gildas 7 éve
szülő
commit
ad8399b04d
1 módosított fájl, 68 hozzáadás és 65 törlés
  1. 68 65
      lib/single-file/css-rules-minifier.js

+ 68 - 65
lib/single-file/css-rules-minifier.js

@@ -24,6 +24,7 @@ this.cssMinifier = this.cssMinifier || (() => {
 
 	const REMOVED_PSEUDO_CLASSES = [":focus", ":focus-within", ":hover", ":link", ":visited", ":active"];
 	const REMOVED_PSEUDO_ELEMENTS = ["::after", "::before", "::first-line", "::first-letter", "::placeholder", "::-webkit-input-placeholder", "::selection", "::marker", "::cue", "::-webkit-progress-bar", "::-webkit-progress-value", "::-webkit-inner-spin-button", "::-webkit-outer-spin-button", "::-webkit-search-cancel-button", "::-webkit-search-cancel-button"];
+	const FILTERED_PSEUDO = REMOVED_PSEUDO_CLASSES.concat(REMOVED_PSEUDO_ELEMENTS);
 	const IGNORED_SELECTORS = ["::-webkit-scrollbar", "::-webkit-scrollbar-button", "::-webkit-scrollbar-thumb", "::-webkit-scrollbar-track", "::-webkit-scrollbar-track-piece", "::-webkit-scrollbar-corner", "::-webkit-resizer"];
 
 	return {
@@ -39,76 +40,92 @@ this.cssMinifier = this.cssMinifier || (() => {
 					} else {
 						mediaInfo = mediaAllInfo;
 					}
-					processRules(doc, styleElement.sheet.cssRules, mediaInfo, stats);
-					styleElement.textContent = serializeRules(styleElement.sheet.cssRules);
+					const cssRules = styleElement.sheet.cssRules;
+					stats.processed += cssRules.length;
+					stats.discarded += cssRules.length;
+					styleElement.textContent = processRules(doc, cssRules, mediaInfo);
+					stats.discarded -= cssRules.length;
 				}
 			});
 			doc.querySelectorAll("[style]").forEach(element => {
-				processStyle(doc, element.style, mediaAllInfo);
+				let textContent = processStyleAttribute(element.style, mediaAllInfo);
+				if (textContent) {
+					element.setAttribute("style", textContent);
+				} else {
+					element.removeAttribute("style");
+				}
 			});
 			return stats;
 		}
 	};
 
-	function processRules(doc, cssRules, mediaInfo, stats) {
-		stats.processed += cssRules.length;
-		stats.discarded += cssRules.length;
+	function processRules(doc, cssRules, mediaInfo) {
+		let sheetContent = "";
 		Array.from(cssRules).forEach(cssRule => {
 			if (cssRule.type == CSSRule.MEDIA_RULE) {
-				processRules(doc, cssRule.cssRules, mediaInfo.medias.get(cssRule.media), stats);
+				sheetContent += "@media " + Array.from(cssRule.media).join(",") + "{";
+				sheetContent += processRules(doc, cssRule.cssRules, mediaInfo.medias.get(cssRule.media));
+				sheetContent += "}";
 			} else if (cssRule.type == CSSRule.STYLE_RULE) {
 				const ruleInfo = mediaInfo.rules.get(cssRule);
-				if (ruleInfo) {
-					const stylesInfo = parseCss.parseAListOfDeclarations(cssRule.style.cssText);
-					const unusedStyles = stylesInfo.filter(style => !ruleInfo.style.get(style.name));
-					if (unusedStyles.length) {
-						unusedStyles.forEach(style => cssRule.style.removeProperty(style.name));
-					}
-					if (ruleInfo.matchedSelectors.size < ruleInfo.selectorsText.length) {
-						cssRule.selectorText = ruleInfo.selectorsText.filter(selectorText => ruleInfo.matchedSelectors.has(selectorText) || testIgnoredSelector(selectorText)).join(",");
+				if (ruleInfo || testIgnoredSelector(cssRule.selectorText)) {
+					sheetContent += processRuleInfo(cssRule, ruleInfo);
+				}
+			} else {
+				sheetContent += cssRule.cssText;
+			}
+		});
+		return sheetContent;
+	}
+
+	function processRuleInfo(cssRule, ruleInfo) {
+		let selectorText = "", styleCssText = "";
+		if (ruleInfo) {
+			const stylesInfo = parseCss.parseAListOfDeclarations(cssRule.style.cssText);
+			for (let styleIndex = 0; styleIndex < stylesInfo.length; styleIndex++) {
+				const style = stylesInfo[styleIndex];
+				if (ruleInfo.style.get(style.name)) {
+					if (styleCssText) {
+						styleCssText += ";";
 					}
-				} else {
-					if (!testIgnoredSelector(cssRule.selectorText)) {
-						const parent = cssRule.parentRule || cssRule.parentStyleSheet;
-						let indexRule = 0;
-						while (cssRule != parent.cssRules[indexRule] && indexRule < parent.cssRules.length) {
-							indexRule++;
-						}
-						if (cssRule == parent.cssRules[indexRule]) {
-							parent.deleteRule(indexRule);
+					const priority = cssRule.style.getPropertyPriority(style.name);
+					styleCssText += style.name + ":" + cssRule.style.getPropertyValue(style.name) + (priority && ("!" + priority));
+				}
+			}
+			if (ruleInfo.matchedSelectors.size < ruleInfo.selectorsText.length) {
+				for (let selectorTextIndex = 0; selectorTextIndex < ruleInfo.selectorsText.length; selectorTextIndex++) {
+					const ruleSelectorText = ruleInfo.selectorsText[selectorTextIndex];
+					if (ruleInfo.matchedSelectors.has(ruleSelectorText) || testIgnoredSelector(ruleSelectorText)) {
+						if (selectorText) {
+							selectorText += ",";
 						}
+						selectorText += ruleSelectorText;
 					}
 				}
 			}
-		});
-		stats.discarded -= cssRules.length;
+		}
+		return (selectorText || cssRule.selectorText) + "{" + (styleCssText || cssRule.style.cssText) + "}";
 	}
 
-	function processStyle(doc, cssStyle, mediaInfo) {
+	function processStyleAttribute(cssStyle, mediaInfo) {
+		let styleCssText = "";
 		const styleInfo = mediaInfo.styles.get(cssStyle);
 		if (styleInfo) {
 			const stylesInfo = parseCss.parseAListOfDeclarations(cssStyle.cssText);
-			const unusedStyles = stylesInfo.filter(style => !styleInfo.style.get(style.name));
-			if (unusedStyles.length) {
-				unusedStyles.forEach(style => cssStyle.removeProperty(style.name));
+			for (let styleIndex = 0; styleIndex < stylesInfo.length; styleIndex++) {
+				const style = stylesInfo[styleIndex];
+				if (styleInfo.style.get(style.name)) {
+					if (styleCssText) {
+						styleCssText += ";";
+					}
+					const priority = cssStyle.getPropertyPriority(style.name);
+					styleCssText += style.name + ":" + cssStyle.getPropertyValue(style.name) + (priority && ("!" + priority));
+				}
 			}
+			return (styleCssText || cssStyle.cssText);
 		}
 	}
 
-	function serializeRules(rules) {
-		let sheetContent = "";
-		Array.from(rules).forEach(rule => {
-			if (rule.media) {
-				sheetContent += "@media " + Array.from(rule.media).join(",") + "{";
-				sheetContent += serializeRules(rule.cssRules);
-				sheetContent += "}";
-			} else {
-				sheetContent += rule.cssText;
-			}
-		});
-		return sheetContent;
-	}
-
 	function testIgnoredSelector(selectorText) {
 		let indexSelector = 0, found;
 		selectorText = selectorText.toLowerCase();
@@ -121,29 +138,15 @@ this.cssMinifier = this.cssMinifier || (() => {
 		if (!found) {
 			indexSelector = 0;
 			while (indexSelector < IGNORED_SELECTORS.length && !found) {
-				found = testFilterSelector(selectorText);
-				if (!found) {
-					indexSelector++;
+				let indexPseudo = 0;
+				while (indexPseudo < FILTERED_PSEUDO.length && !found) {
+					found = selectorText.includes(FILTERED_PSEUDO[indexPseudo]);
+					if (!found) {
+						indexPseudo++;
+					}
 				}
-			}
-		}
-		return found;
-	}
-
-	function testFilterSelector(selector) {
-		let indexPseudoClass = 0, found;
-		while (indexPseudoClass < REMOVED_PSEUDO_CLASSES.length && !found) {
-			found = selector.includes(REMOVED_PSEUDO_CLASSES[indexPseudoClass]);
-			if (!found) {
-				indexPseudoClass++;
-			}
-		}
-		if (!found) {
-			let indexPseudoElement = 0;
-			while (indexPseudoElement < REMOVED_PSEUDO_ELEMENTS.length && !found) {
-				found = selector.includes(REMOVED_PSEUDO_ELEMENTS[indexPseudoElement]);
 				if (!found) {
-					indexPseudoElement++;
+					indexSelector++;
 				}
 			}
 		}