Przeglądaj źródła

extracted rules minifying code into rules-minifier.js

Gildas 7 lat temu
rodzic
commit
4cd0518b2c

+ 1 - 0
extension/core/bg/bg.js

@@ -37,6 +37,7 @@ singlefile.core = (() => {
 		"/extension/ui/content/ui.js",
 		"/lib/single-file/base64.js",
 		"/lib/single-file/uglifycss.js",
+		"/lib/single-file/rules-minifier.js",
 		"/lib/single-file/htmlnano.js",
 		"/lib/single-file/parse-srcset.js",
 		"/lib/single-file/single-file-core.js",

+ 86 - 0
lib/single-file/rules-minifier.js

@@ -0,0 +1,86 @@
+/*
+ * Copyright 2018 Gildas Lormeau
+ * contact : gildas.lormeau <at> gmail.com
+ * 
+ * This file is part of SingleFile.
+ *
+ *   SingleFile is free software: you can redistribute it and/or modify
+ *   it under the terms of the GNU Lesser General Public License as published by
+ *   the Free Software Foundation, either version 3 of the License, or
+ *   (at your option) any later version.
+ *
+ *   SingleFile is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public License
+ *   along with SingleFile.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+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 = {};
+			doc.querySelectorAll("style").forEach(style => {
+				const cssRules = [];
+				if (style.sheet) {
+					processRules(doc, style.sheet.cssRules, cssRules, rulesCache);
+					const stylesheetContent = cssRules.join("");
+					style.textContent = stylesheetContent;
+				}
+			});
+		}
+	};
+
+	function processRules(doc, rules, cssRules, cache) {
+		if (rules) {
+			Array.from(rules).forEach(rule => {
+				if (rule.media) {
+					cssRules.push("@media " + Array.prototype.join.call(rule.media, ",") + " {");
+					processRules(doc, rule.cssRules, cssRules, cache);
+					cssRules.push("}");
+				} else if (rule.selectorText) {
+					const selector = getFilteredSelector(rule.selectorText);
+					if (selector) {
+						try {
+							if (cache[selector] || doc.querySelector(selector)) {
+								cssRules.push(rule.cssText);
+								cache[selector] = true;
+							}
+						} catch (error) {
+							cssRules.push(rule.cssText);
+						}
+					}
+				} else {
+					cssRules.push(rule.cssText);
+				}
+			});
+		}
+	}
+
+	function getFilteredSelector(selector) {
+		if (selector.match(REGEXP_PSEUDO_CLASSES)) {
+			let selectors = selector.split(/\s*,\s*/g);
+			selector = selectors.map(selector => {
+				const simpleSelectors = selector.split(/\s*[ >~+]\s*/g);
+				const separators = selector.match(/\s*[ >~+]\s*/g);
+				return simpleSelectors.map((selector, selectorIndex) => {
+					while (selector.match(REGEXP_PSEUDO_CLASSES)) {
+						selector = selector.replace(REGEXP_PSEUDO_CLASSES, "").trim();
+					}
+					selector = selector.replace(/:?:[^(]+\(\)/g, "");
+					if (selector == "") {
+						selector = "*";
+					}
+					return selector + (separators && separators[selectorIndex] ? separators[selectorIndex] : "");
+				}).join("");
+			}).join(",");
+		}
+		return selector;
+	}
+
+})();

+ 3 - 2
lib/single-file/single-file-browser.js

@@ -18,7 +18,7 @@
  *   along with SingleFile.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-/* global SingleFileCore, base64, DOMParser, TextDecoder, fetch, superFetch, parseSrcset, uglifycss, htmlnano */
+/* global SingleFileCore, base64, DOMParser, TextDecoder, fetch, superFetch, parseSrcset, uglifycss, htmlnano, rulesMinifier */
 
 this.SingleFile = this.SingleFile || (() => {
 
@@ -98,7 +98,8 @@ this.SingleFile = this.SingleFile || (() => {
 				htmlnano: {
 					process: doc => htmlnano.process(doc),
 					postProcess: doc => htmlnano.postProcess(doc),
-				}
+				},
+				rulesMinifier: doc => rulesMinifier.process(doc)
 			};
 		}
 	}

+ 3 - 58
lib/single-file/single-file-core.js

@@ -358,16 +358,7 @@ this.SingleFileCore = this.SingleFileCore || (() => {
 		}
 
 		removeUnusedCSSRules() {
-			const doc = this.doc;
-			const rulesCache = {};
-			doc.querySelectorAll("style").forEach(style => {
-				const cssRules = [];
-				if (style.sheet) {
-					DomProcessorHelper.processRules(this.doc, style.sheet.cssRules, cssRules, rulesCache);
-					const stylesheetContent = cssRules.join("");
-					style.textContent = this.options.compressCSS ? this.dom.uglifycss(stylesheetContent) : stylesheetContent;
-				}
-			});
+			this.dom.rulesMinifier(this.doc);
 		}
 
 		removeHiddenElements() {
@@ -438,7 +429,7 @@ this.SingleFileCore = this.SingleFileCore || (() => {
 		async inlineStylesheets(initialization) {
 			await Promise.all(Array.from(this.doc.querySelectorAll("style")).map(async styleElement => {
 				const stylesheetContent = initialization ? await DomProcessorHelper.resolveImportURLs(styleElement.textContent, this.baseURI, { maxResourceSize: this.options.maxResourceSize, maxResourceSizeEnabled: this.options.maxResourceSizeEnabled }) : await DomProcessorHelper.processStylesheet(styleElement.textContent, this.baseURI);
-				styleElement.textContent = this.options.compressCSS && !this.options.removeUnusedCSSRules ? this.dom.uglifycss(stylesheetContent) : stylesheetContent;
+				styleElement.textContent = !initialization && this.options.compressCSS ? this.dom.uglifycss(stylesheetContent) : stylesheetContent;
 			}));
 		}
 
@@ -529,7 +520,7 @@ this.SingleFileCore = this.SingleFileCore || (() => {
 			await Promise.all(Array.from(this.doc.querySelectorAll("link[rel*=stylesheet]")).map(async linkElement => {
 				const stylesheetContent = await DomProcessorHelper.resolveLinkStylesheetURLs(linkElement.href, this.baseURI, linkElement.media, { maxResourceSize: this.options.maxResourceSize, maxResourceSizeEnabled: this.options.maxResourceSizeEnabled });
 				const styleElement = this.doc.createElement("style");
-				styleElement.textContent = this.options.compressCSS && !this.options.removeUnusedCSSRules ? this.dom.uglifycss(stylesheetContent) : stylesheetContent;
+				styleElement.textContent = stylesheetContent;
 				linkElement.parentElement.replaceChild(styleElement, linkElement);
 			}));
 		}
@@ -679,31 +670,6 @@ this.SingleFileCore = this.SingleFileCore || (() => {
 			}));
 		}
 
-		static processRules(doc, rules, cssRules, cache) {
-			if (rules) {
-				Array.from(rules).forEach(rule => {
-					if (rule.media) {
-						cssRules.push("@media " + Array.prototype.join.call(rule.media, ",") + " {");
-						DomProcessorHelper.processRules(doc, rule.cssRules, cssRules, cache);
-						cssRules.push("}");
-					} else if (rule.selectorText) {
-						const selector = DomUtil.getFilteredSelector(rule.selectorText);
-						if (selector) {
-							try {
-								if (cache[selector] || doc.querySelector(selector)) {
-									cssRules.push(rule.cssText);
-									cache[selector] = true;
-								}
-							} catch (error) {
-								cssRules.push(rule.cssText);
-							}
-						}
-					} else {
-						cssRules.push(rule.cssText);
-					}
-				});
-			}
-		}
 	}
 
 	// -------
@@ -723,7 +689,6 @@ this.SingleFileCore = this.SingleFileCore || (() => {
 	const REGEXP_IMPORT_SIMPLE_QUOTES_FN = /@import\s*'([^']*)'\s*([^;]*)/i;
 	const REGEXP_IMPORT_DOUBLE_QUOTES_FN = /@import\s*"([^"]*)"\s*([^;]*)/i;
 	const REGEXP_IMPORT_NO_QUOTES_FN = /@import\s*([^;]*)\s*([^;]*)/i;
-	const REGEXP_PSEUDO_CLASSES = /::after|::before|::first-line|::first-letter|:focus|:focus-within|:hover|:link|:visited|:active/gi;
 
 	class DomUtil {
 		static normalizeURL(url) {
@@ -792,26 +757,6 @@ this.SingleFileCore = this.SingleFileCore || (() => {
 			});
 		}
 
-		static getFilteredSelector(selector) {
-			if (selector.match(REGEXP_PSEUDO_CLASSES)) {
-				let selectors = selector.split(/\s*,\s*/g);
-				selector = selectors.map(selector => {
-					const simpleSelectors = selector.split(/\s*[ >~+]\s*/g);
-					const separators = selector.match(/\s*[ >~+]\s*/g);
-					return simpleSelectors.map((selector, selectorIndex) => {
-						while (selector.match(REGEXP_PSEUDO_CLASSES)) {
-							selector = selector.replace(REGEXP_PSEUDO_CLASSES, "").trim();
-						}
-						selector = selector.replace(/:?:[^(]+\(\)/g, "");
-						if (selector == "") {
-							selector = "*";
-						}
-						return selector + (separators && separators[selectorIndex] ? separators[selectorIndex] : "");
-					}).join("");
-				}).join(",");
-			}
-			return selector;
-		}
 	}
 
 	return { getClass };