Parcourir la source

moved regexps in upper scope

Gildas il y a 7 ans
Parent
commit
fc9c48acdf
1 fichiers modifiés avec 147 ajouts et 162 suppressions
  1. 147 162
      lib/single-file/css-minifier.js

+ 147 - 162
lib/single-file/css-minifier.js

@@ -62,91 +62,9 @@ this.uglifycss = this.uglifycss || (() => {
 		output: ""
 	};
 
-	/**
-	 * convertRelativeUrls converts relative urls and replaces them with tokens
-	 * before we start compressing. It must be called *after* extractDataUrls
-	 *
-	 * @param {string} css - CSS content
-	 * @param {options} options - UglifyCSS Options
-	 * @param {string[]} preservedTokens - Global array of tokens to preserve
-	 *
-	 * @return {string} Processed css
-	 */
-
-	function convertRelativeUrls(css, options, preservedTokens) {
-
-		const pattern = /(url\s*\()\s*(["']?)/g;
-		const maxIndex = css.length - 1;
-		const sb = [];
-
-		let appendIndex = 0, match;
-
-		// Since we need to account for non-base64 data urls, we need to handle
-		// ' and ) being part of the data string. Hence switching to indexOf,
-		// to determine whether or not we have matching string terminators and
-		// handling sb appends directly, instead of using matcher.append* methods.
-
-		while ((match = pattern.exec(css)) !== null) {
-
-			const startIndex = match.index + match[1].length;  // 'url('.length()
-			let terminator = match[2];         // ', " or empty (not quoted)
-
-			if (terminator.length === 0) {
-				terminator = ")";
-			}
-
-			let foundTerminator = false, endIndex = pattern.lastIndex - 1;
-
-			while (foundTerminator === false && endIndex + 1 <= maxIndex) {
-				endIndex = css.indexOf(terminator, endIndex + 1);
-
-				// endIndex == 0 doesn't really apply here
-				if ((endIndex > 0) && (css.charAt(endIndex - 1) !== "\\")) {
-					foundTerminator = true;
-					if (")" != terminator) {
-						endIndex = css.indexOf(")", endIndex);
-					}
-				}
-			}
-
-			// Enough searching, start moving stuff over to the buffer
-			sb.push(css.substring(appendIndex, match.index));
-
-			if (foundTerminator) {
-
-				let token = css.substring(startIndex, endIndex).replace(/(^\s*|\s*$)/g, "");
-				if (token.slice(0, 19) !== ___PRESERVED_TOKEN_) {
-
-					if (terminator === "'" || terminator === "\"") {
-						token = token.slice(1, -1);
-					} else if (terminator === ")") {
-						terminator = "";
-					}
-
-					const url = terminator + token + terminator;
-
-					preservedTokens.push(url);
-
-					const preserver = "url(" + ___PRESERVED_TOKEN_ + (preservedTokens.length - 1) + "___)";
-					sb.push(preserver);
-
-				} else {
-					sb.push(`url(${token})`);
-				}
-
-				appendIndex = endIndex + 1;
-
-			} else {
-				// No end terminator found, re-add the whole match. Should we throw/warn here?
-				sb.push(css.substring(match.index, pattern.lastIndex));
-				appendIndex = pattern.lastIndex;
-			}
-		}
-
-		sb.push(css.substring(appendIndex));
-
-		return sb.join("");
-	}
+	const REGEXP_DATA_URI = /url\(\s*(["']?)data:/g;
+	const REGEXP_WHITE_SPACES = /\s+/g;
+	const REGEXP_NEW_LINE = /\n/g;
 
 	/**
 	 * extractDataUrls replaces all data urls with tokens before we start
@@ -162,7 +80,7 @@ this.uglifycss = this.uglifycss || (() => {
 	function extractDataUrls(css, preservedTokens) {
 
 		// Leave data urls alone to increase parse performance.
-		const pattern = /url\(\s*(["']?)data:/g;
+		const pattern = REGEXP_DATA_URI;
 		const maxIndex = css.length - 1;
 		const sb = [];
 
@@ -204,11 +122,11 @@ this.uglifycss = this.uglifycss || (() => {
 				let token = css.substring(startIndex, endIndex);
 				const parts = token.split(",");
 				if (parts.length > 1 && parts[0].slice(-7) == ";base64") {
-					token = token.replace(/\s+/g, "");
+					token = token.replace(REGEXP_WHITE_SPACES, "");
 				} else {
-					token = token.replace(/\n/g, " ");
-					token = token.replace(/\s+/g, " ");
-					token = token.replace(/(^\s+|\s+$)/g, "");
+					token = token.replace(REGEXP_NEW_LINE, " ");
+					token = token.replace(REGEXP_WHITE_SPACES, " ");
+					token = token.replace(REGEXP_PRESERVE_HSLA1, "");
 				}
 
 				preservedTokens.push(token);
@@ -229,6 +147,8 @@ this.uglifycss = this.uglifycss || (() => {
 		return sb.join("");
 	}
 
+	const REGEXP_HEX_COLORS = /(=\s*?["']?)?#([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])(\}|[^0-9a-f{][^{]*?\})/gi;
+
 	/**
 	 * compressHexColors compresses hex color values of the form #AABBCC to #ABC.
 	 *
@@ -249,7 +169,7 @@ this.uglifycss = this.uglifycss || (() => {
 
 		// Look for hex colors inside { ... } (to avoid IDs) and which don't have a =, or a " in front of them (to avoid filters)
 
-		const pattern = /(=\s*?["']?)?#([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])(\}|[^0-9a-f{][^{]*?\})/gi;
+		const pattern = REGEXP_HEX_COLORS;
 		const sb = [];
 
 		let index = 0, match;
@@ -284,6 +204,9 @@ this.uglifycss = this.uglifycss || (() => {
 		return sb.join("");
 	}
 
+	const REGEXP_KEYFRAMES = /@[a-z0-9-_]*keyframes\s+[a-z0-9-_]+\s*{/gi;
+	const REGEXP_WHITE_SPACE = /(^\s|\s$)/g;
+
 	/** keyframes preserves 0 followed by unit in keyframes steps
 	 *
 	 * @param {string} content - CSS content
@@ -294,12 +217,12 @@ this.uglifycss = this.uglifycss || (() => {
 
 	function keyframes(content, preservedTokens) {
 
-		const pattern = /@[a-z0-9-_]*keyframes\s+[a-z0-9-_]+\s*{/gi;
+		const pattern = REGEXP_KEYFRAMES;
 
 		let index = 0, buffer;
 
 		const preserve = (part, i) => {
-			part = part.replace(/(^\s|\s$)/g, "");
+			part = part.replace(REGEXP_WHITE_SPACE, "");
 			if (part.charAt(0) === "0") {
 				preservedTokens.push(part);
 				buffer[i] = ___PRESERVED_TOKEN_ + (preservedTokens.length - 1) + "___";
@@ -329,7 +252,7 @@ this.uglifycss = this.uglifycss || (() => {
 				if (ch === "{") {
 
 					if (level === 0) {
-						buffers.push(buffer.replace(/(^\s|\s$)/g, ""));
+						buffers.push(buffer.replace(REGEXP_WHITE_SPACE, ""));
 
 					} else if (level === 1) {
 
@@ -337,7 +260,7 @@ this.uglifycss = this.uglifycss || (() => {
 
 						buffer.forEach(preserve);
 
-						buffers.push(buffer.join(",").replace(/(^\s|\s$)/g, ""));
+						buffers.push(buffer.join(",").replace(REGEXP_WHITE_SPACE, ""));
 					}
 
 					buffer = "";
@@ -346,7 +269,7 @@ this.uglifycss = this.uglifycss || (() => {
 				} else if (ch === "}") {
 
 					if (level === 2) {
-						buffers.push("{" + buffer.replace(/(^\s|\s$)/g, "") + "}");
+						buffers.push("{" + buffer.replace(REGEXP_WHITE_SPACE, "") + "}");
 						buffer = "";
 
 					} else if (level === 1) {
@@ -426,6 +349,69 @@ this.uglifycss = this.uglifycss || (() => {
 	 * @return {string} Uglified result
 	 */
 
+	// const REGEXP_EMPTY_RULES = /[^};{/]+\{\}/g;
+	const REGEXP_PRESERVE_STRING = /("([^\\"]|\\.|\\)*")|('([^\\']|\\.|\\)*')/g;
+	const REGEXP_MINIFY_ALPHA = /progid:DXImageTransform.Microsoft.Alpha\(Opacity=/gi;
+	const REGEXP_PRESERVE_TOKEN1 = /\r\n/g;
+	const REGEXP_PRESERVE_TOKEN2 = /[\r\n]/g;
+	const REGEXP_VARIABLES = /@variables\s*\{\s*([^}]+)\s*\}/g;
+	const REGEXP_VARIABLE = /\s*([a-z0-9-]+)\s*:\s*([^;}]+)\s*/gi;
+	const REGEXP_VARIABLE_VALUE = /var\s*\(\s*([^)]+)\s*\)/g;
+	const REGEXP_PRESERVE_CALC = /calc\(([^;}]*)\)/g;
+	const REGEXP_TRIM = /(^\s*|\s*$)/g;
+	const REGEXP_PRESERVE_CALC2 = /\( /g;
+	const REGEXP_PRESERVE_CALC3 = / \)/g;
+	const REGEXP_PRESERVE_MATRIX = /\s*filter:\s*progid:DXImageTransform.Microsoft.Matrix\(([^)]+)\);/g;
+	const REGEXP_REMOVE_SPACES = /(^|\})(([^{:])+:)+([^{]*{)/g;
+	const REGEXP_REMOVE_SPACES2 = /\s+([!{};:>+()\],])/g;
+	const REGEXP_RESTORE_SPACE_IMPORTANT = /!important/g;
+	const REGEXP_PSEUDOCLASSCOLON = /___PSEUDOCLASSCOLON___/g;
+	const REGEXP_COLUMN = /:/g;
+	const REGEXP_PRESERVE_ZERO_UNIT = /\s*(animation|animation-delay|animation-duration|transition|transition-delay|transition-duration):\s*([^;}]+)/gi;
+	const REGEXP_PRESERVE_ZERO_UNIT1 = /(^|\D)0?\.?0(m?s)/gi;
+	const REGEXP_PRESERVE_FLEX = /\s*(flex|flex-basis):\s*([^;}]+)/gi;
+	const REGEXP_SPACES = /\s+/;
+	const REGEXP_PRESERVE_HSLA = /(hsla?)\(([^)]+)\)/g;
+	const REGEXP_PRESERVE_HSLA1 = /(^\s+|\s+$)/g;
+	const REGEXP_RETAIN_SPACE_IE6 = /:first-(line|letter)(\{|,)/gi;
+	const REGEXP_CHARSET = /^(.*)(@charset)( "[^"]*";)/gi;
+	const REGEXP_REMOVE_SECOND_CHARSET = /^((\s*)(@charset)( [^;]+;\s*))+/gi;
+	const REGEXP_LOWERCASE_DIRECTIVES = /@(font-face|import|(?:-(?:atsc|khtml|moz|ms|o|wap|webkit)-)?keyframe|media|page|namespace)/gi;
+	const REGEXP_LOWERCASE_PSEUDO_ELEMENTS = /:(active|after|before|checked|disabled|empty|enabled|first-(?:child|of-type)|focus|hover|last-(?:child|of-type)|link|only-(?:child|of-type)|root|:selection|target|visited)/gi;
+	const REGEXP_CHARSET2 = /^(.*)(@charset "[^"]*";)/g;
+	const REGEXP_CHARSET3 = /^(\s*@charset [^;]+;\s*)+/g;
+	const REGEXP_LOWERCASE_FUNCTIONS = /:(lang|not|nth-child|nth-last-child|nth-last-of-type|nth-of-type|(?:-(?:atsc|khtml|moz|ms|o|wap|webkit)-)?any)\(/gi;
+	const REGEXP_LOWERCASE_FUNCTIONS2 = /([:,( ]\s*)(attr|color-stop|from|rgba|to|url|(?:-(?:atsc|khtml|moz|ms|o|wap|webkit)-)?(?:calc|max|min|(?:repeating-)?(?:linear|radial)-gradient)|-webkit-gradient)/gi;
+	const REGEXP_NEWLINE1 = /\s*\/\*/g;
+	const REGEXP_NEWLINE2 = /\*\/\s*/g;
+	const REGEXP_RESTORE_SPACE1 = /\band\(/gi;
+	const REGEXP_RESTORE_SPACE2 = /([^:])not\(/gi;
+	const REGEXP_REMOVE_SPACES3 = /([!{}:;>+([,])\s+/g;
+	const REGEXP_REMOVE_SEMI_COLUMNS = /;+\}/g;
+	const REGEXP_REPLACE_ZERO = /(^|[^.0-9\\])(?:0?\.)?0(?:ex|ch|r?em|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|g?rad|turn|m?s|k?Hz|dpi|dpcm|dppx|%)(?![a-z0-9])/gi;
+	const REGEXP_REPLACE_ZERO_DOT = /([0-9])\.0(ex|ch|r?em|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|g?rad|turn|m?s|k?Hz|dpi|dpcm|dppx|%| |;)/gi;
+	const REGEXP_REPLACE_4_ZEROS = /:0 0 0 0(;|\})/g;
+	const REGEXP_REPLACE_3_ZEROS = /:0 0 0(;|\})/g;
+	const REGEXP_REPLACE_2_ZEROS = /:0 0(;|\})/g;
+	const REGEXP_REPLACE_1_ZERO = /(background-position|transform-origin|webkit-transform-origin|moz-transform-origin|o-transform-origin|ms-transform-origin|box-shadow):0(;|\})/gi;
+	const REGEXP_REPLACE_ZERO_DOT_DECIMAL = /(:|\s)0+\.(\d+)/g;
+	const REGEXP_REPLACE_RGB = /rgb\s*\(\s*([0-9,\s]+)\s*\)/gi;
+	const REGEXP_REPLACE_BORDER_ZERO = /(border|border-top|border-right|border-bottom|border-left|outline|background):none(;|\})/gi;
+	const REGEXP_REPLACE_IE_OPACITY = /progid:DXImageTransform\.Microsoft\.Alpha\(Opacity=/gi;
+	const REGEXP_REPLACE_QUERY_FRACTION = /\(([-A-Za-z]+):([0-9]+)\/([0-9]+)\)/g;
+	const REGEXP_QUERY_FRACTION = /___QUERY_FRACTION___/g;
+	const REGEXP_REPLACE_SEMI_COLUMNS = /;;+/g;
+	const REGEXP_REPLACE_HASH_COLOR = /(:|\s)(#f00)(;|})/g;
+	const REGEXP_PRESERVED_NEWLINE = /___PRESERVED_NEWLINE___/g;
+	const REGEXP_REPLACE_HASH_COLOR_SHORT1 = /(:|\s)(#000080)(;|})/g;
+	const REGEXP_REPLACE_HASH_COLOR_SHORT2 = /(:|\s)(#808080)(;|})/g;
+	const REGEXP_REPLACE_HASH_COLOR_SHORT3 = /(:|\s)(#808000)(;|})/g;
+	const REGEXP_REPLACE_HASH_COLOR_SHORT4 = /(:|\s)(#800080)(;|})/g;
+	const REGEXP_REPLACE_HASH_COLOR_SHORT5 = /(:|\s)(#c0c0c0)(;|})/g;
+	const REGEXP_REPLACE_HASH_COLOR_SHORT6 = /(:|\s)(#008080)(;|})/g;
+	const REGEXP_REPLACE_HASH_COLOR_SHORT7 = /(:|\s)(#ffa500)(;|})/g;
+	const REGEXP_REPLACE_HASH_COLOR_SHORT8 = /(:|\s)(#800000)(;|})/g;
+
 	function processString(content = "", options = defaultOptions) {
 
 		const comments = [];
@@ -434,11 +420,10 @@ this.uglifycss = this.uglifycss || (() => {
 		let pattern;
 
 		content = extractDataUrls(content, preservedTokens);
-		content = convertRelativeUrls(content, options, preservedTokens);
 		content = collectComments(content, comments);
 
 		// preserve strings so their content doesn't get accidentally minified
-		pattern = /("([^\\"]|\\.|\\)*")|('([^\\']|\\.|\\)*')/g;
+		pattern = REGEXP_PRESERVE_STRING;
 		content = content.replace(pattern, token => {
 			const quote = token.substring(0, 1);
 			token = token.slice(1, -1);
@@ -449,7 +434,7 @@ this.uglifycss = this.uglifycss || (() => {
 				}
 			}
 			// minify alpha opacity in filter strings
-			token = token.replace(/progid:DXImageTransform.Microsoft.Alpha\(Opacity=/gi, "alpha(opacity=");
+			token = token.replace(REGEXP_MINIFY_ALPHA, "alpha(opacity=");
 			preservedTokens.push(token);
 			return quote + ___PRESERVED_TOKEN_ + (preservedTokens.length - 1) + "___" + quote;
 		});
@@ -464,9 +449,9 @@ this.uglifycss = this.uglifycss || (() => {
 			// so push to the preserved tokens keeping the !
 			if (token.charAt(0) === "!") {
 				if (options.cuteComments) {
-					preservedTokens.push(token.substring(1).replace(/\r\n/g, "\n"));
+					preservedTokens.push(token.substring(1).replace(REGEXP_PRESERVE_TOKEN1, "\n"));
 				} else if (options.uglyComments) {
-					preservedTokens.push(token.substring(1).replace(/[\r\n]/g, ""));
+					preservedTokens.push(token.substring(1).replace(REGEXP_PRESERVE_TOKEN2, ""));
 				} else {
 					preservedTokens.push(token);
 				}
@@ -507,9 +492,9 @@ this.uglifycss = this.uglifycss || (() => {
 		// parse simple @variables blocks and remove them
 		if (options.expandVars) {
 			const vars = {};
-			pattern = /@variables\s*\{\s*([^}]+)\s*\}/g;
+			pattern = REGEXP_VARIABLES;
 			content = content.replace(pattern, (_, f1) => {
-				pattern = /\s*([a-z0-9-]+)\s*:\s*([^;}]+)\s*/gi;
+				pattern = REGEXP_VARIABLE;
 				f1.replace(pattern, (_, f1, f2) => {
 					if (f1 && f2) {
 						vars[f1] = f2;
@@ -520,30 +505,30 @@ this.uglifycss = this.uglifycss || (() => {
 			});
 
 			// replace var(x) with the value of x
-			pattern = /var\s*\(\s*([^)]+)\s*\)/g;
+			pattern = REGEXP_VARIABLE_VALUE;
 			content = content.replace(pattern, (_, f1) => {
 				return vars[f1] || "none";
 			});
 		}
 
 		// normalize all whitespace strings to single spaces. Easier to work with that way.
-		content = content.replace(/\s+/g, " ");
+		content = content.replace(REGEXP_WHITE_SPACES, " ");
 
 		// preserve formulas in calc() before removing spaces
-		pattern = /calc\(([^;}]*)\)/g;
+		pattern = REGEXP_PRESERVE_CALC;
 		content = content.replace(pattern, (_, f1) => {
 			preservedTokens.push(
 				"calc(" +
-				f1.replace(/(^\s*|\s*$)/g, "")
-					.replace(/\( /g, "(")
-					.replace(/ \)/g, ")") +
+				f1.replace(REGEXP_TRIM, "")
+					.replace(REGEXP_PRESERVE_CALC2, "(")
+					.replace(REGEXP_PRESERVE_CALC3, ")") +
 				")"
 			);
 			return ___PRESERVED_TOKEN_ + (preservedTokens.length - 1) + "___";
 		});
 
 		// preserve matrix
-		pattern = /\s*filter:\s*progid:DXImageTransform.Microsoft.Matrix\(([^)]+)\);/g;
+		pattern = REGEXP_PRESERVE_MATRIX;
 		content = content.replace(pattern, (_, f1) => {
 			preservedTokens.push(f1);
 			return "filter:progid:DXImageTransform.Microsoft.Matrix(" + ___PRESERVED_TOKEN_ + (preservedTokens.length - 1) + "___);";
@@ -552,23 +537,23 @@ this.uglifycss = this.uglifycss || (() => {
 		// remove the spaces before the things that should not have spaces before them.
 		// but, be careful not to turn 'p :link {...}' into 'p:link{...}'
 		// swap out any pseudo-class colons with the token, and then swap back.
-		pattern = /(^|\})(([^{:])+:)+([^{]*{)/g;
-		content = content.replace(pattern, token => token.replace(/:/g, "___PSEUDOCLASSCOLON___"));
+		pattern = REGEXP_REMOVE_SPACES;
+		content = content.replace(pattern, token => token.replace(REGEXP_COLUMN, "___PSEUDOCLASSCOLON___"));
 
 		// remove spaces before the things that should not have spaces before them.
-		content = content.replace(/\s+([!{};:>+()\],])/g, "$1");
+		content = content.replace(REGEXP_REMOVE_SPACES2, "$1");
 
 		// restore spaces for !important
-		content = content.replace(/!important/g, " !important");
+		content = content.replace(REGEXP_RESTORE_SPACE_IMPORTANT, " !important");
 
 		// bring back the colon
-		content = content.replace(/___PSEUDOCLASSCOLON___/g, ":");
+		content = content.replace(REGEXP_PSEUDOCLASSCOLON, ":");
 
 		// preserve 0 followed by a time unit for properties using time units
-		pattern = /\s*(animation|animation-delay|animation-duration|transition|transition-delay|transition-duration):\s*([^;}]+)/gi;
+		pattern = REGEXP_PRESERVE_ZERO_UNIT;
 		content = content.replace(pattern, (_, f1, f2) => {
 
-			f2 = f2.replace(/(^|\D)0?\.?0(m?s)/gi, (_, g1, g2) => {
+			f2 = f2.replace(REGEXP_PRESERVE_ZERO_UNIT1, (_, g1, g2) => {
 				preservedTokens.push("0" + g2);
 				return g1 + ___PRESERVED_TOKEN_ + (preservedTokens.length - 1) + "___";
 			});
@@ -577,9 +562,9 @@ this.uglifycss = this.uglifycss || (() => {
 		});
 
 		// preserve unit for flex-basis within flex and flex-basis (ie10 bug)
-		pattern = /\s*(flex|flex-basis):\s*([^;}]+)/gi;
+		pattern = REGEXP_PRESERVE_FLEX;
 		content = content.replace(pattern, (_, f1, f2) => {
-			let f2b = f2.split(/\s+/);
+			let f2b = f2.split(REGEXP_SPACES);
 			preservedTokens.push(f2b.pop());
 			f2b.push(___PRESERVED_TOKEN_ + (preservedTokens.length - 1) + "___");
 			f2b = f2b.join(" ");
@@ -587,10 +572,10 @@ this.uglifycss = this.uglifycss || (() => {
 		});
 
 		// preserve 0% in hsl and hsla color definitions
-		content = content.replace(/(hsla?)\(([^)]+)\)/g, (_, f1, f2) => {
+		content = content.replace(REGEXP_PRESERVE_HSLA, (_, f1, f2) => {
 			const f0 = [];
 			f2.split(",").forEach(part => {
-				part = part.replace(/(^\s+|\s+$)/g, "");
+				part = part.replace(REGEXP_PRESERVE_HSLA1, "");
 				if (part === "0%") {
 					preservedTokens.push("0%");
 					f0.push(___PRESERVED_TOKEN_ + (preservedTokens.length - 1) + "___");
@@ -605,79 +590,79 @@ this.uglifycss = this.uglifycss || (() => {
 		content = keyframes(content, preservedTokens);
 
 		// retain space for special IE6 cases
-		content = content.replace(/:first-(line|letter)(\{|,)/gi, (_, f1, f2) => ":first-" + f1.toLowerCase() + " " + f2);
+		content = content.replace(REGEXP_RETAIN_SPACE_IE6, (_, f1, f2) => ":first-" + f1.toLowerCase() + " " + f2);
 
 		// newlines before and after the end of a preserved comment
 		if (options.cuteComments) {
-			content = content.replace(/\s*\/\*/g, "___PRESERVED_NEWLINE___/*");
-			content = content.replace(/\*\/\s*/g, "*/___PRESERVED_NEWLINE___");
+			content = content.replace(REGEXP_NEWLINE1, "___PRESERVED_NEWLINE___/*");
+			content = content.replace(REGEXP_NEWLINE2, "*/___PRESERVED_NEWLINE___");
 			// no space after the end of a preserved comment
 		} else {
-			content = content.replace(/\*\/\s*/g, "*/");
+			content = content.replace(REGEXP_NEWLINE2, "*/");
 		}
 
 		// If there are multiple @charset directives, push them to the top of the file.
-		pattern = /^(.*)(@charset)( "[^"]*";)/gi;
+		pattern = REGEXP_CHARSET;
 		content = content.replace(pattern, (_, f1, f2, f3) => f2.toLowerCase() + f3 + f1);
 
 		// When all @charset are at the top, remove the second and after (as they are completely ignored).
-		pattern = /^((\s*)(@charset)( [^;]+;\s*))+/gi;
+		pattern = REGEXP_REMOVE_SECOND_CHARSET;
 		content = content.replace(pattern, (_, __, f2, f3, f4) => f2 + f3.toLowerCase() + f4);
 
 		// lowercase some popular @directives (@charset is done right above)
-		pattern = /@(font-face|import|(?:-(?:atsc|khtml|moz|ms|o|wap|webkit)-)?keyframe|media|page|namespace)/gi;
+		pattern = REGEXP_LOWERCASE_DIRECTIVES;
 		content = content.replace(pattern, (_, f1) => "@" + f1.toLowerCase());
 
 		// lowercase some more common pseudo-elements
-		pattern = /:(active|after|before|checked|disabled|empty|enabled|first-(?:child|of-type)|focus|hover|last-(?:child|of-type)|link|only-(?:child|of-type)|root|:selection|target|visited)/gi;
+		pattern = REGEXP_LOWERCASE_PSEUDO_ELEMENTS;
 		content = content.replace(pattern, (_, f1) => ":" + f1.toLowerCase());
 
 		// if there is a @charset, then only allow one, and push to the top of the file.
-		content = content.replace(/^(.*)(@charset "[^"]*";)/g, "$2$1");
-		content = content.replace(/^(\s*@charset [^;]+;\s*)+/g, "$1");
+		content = content.replace(REGEXP_CHARSET2, "$2$1");
+		content = content.replace(REGEXP_CHARSET3, "$1");
 
 		// lowercase some more common functions
-		pattern = /:(lang|not|nth-child|nth-last-child|nth-last-of-type|nth-of-type|(?:-(?:atsc|khtml|moz|ms|o|wap|webkit)-)?any)\(/gi;
+		pattern = REGEXP_LOWERCASE_FUNCTIONS;
 		content = content.replace(pattern, (_, f1) => ":" + f1.toLowerCase() + "(");
 
 		// lower case some common function that can be values
 		// NOTE: rgb() isn't useful as we replace with #hex later, as well as and() is already done for us right after this
-		pattern = /([:,( ]\s*)(attr|color-stop|from|rgba|to|url|(?:-(?:atsc|khtml|moz|ms|o|wap|webkit)-)?(?:calc|max|min|(?:repeating-)?(?:linear|radial)-gradient)|-webkit-gradient)/gi;
+		pattern = REGEXP_LOWERCASE_FUNCTIONS2;
 		content = content.replace(pattern, (_, f1, f2) => f1 + f2.toLowerCase());
 
 		// put the space back in some cases, to support stuff like
 		// @media screen and (-webkit-min-device-pixel-ratio:0){
-		content = content.replace(/\band\(/gi, "and (");
-		content = content.replace(/([^:])not\(/gi, "$1not (");
+		content = content.replace(REGEXP_RESTORE_SPACE1, "and (");
+		content = content.replace(REGEXP_RESTORE_SPACE2, "$1not (");
 
 		// remove the spaces after the things that should not have spaces after them.
-		content = content.replace(/([!{}:;>+([,])\s+/g, "$1");
+		content = content.replace(REGEXP_REMOVE_SPACES3, "$1");
 
 		// remove unnecessary semicolons
-		content = content.replace(/;+\}/g, "}");
+		content = content.replace(REGEXP_REMOVE_SEMI_COLUMNS, "}");
 
 		// replace 0(px,em,%) with 0.
-		content = content.replace(/(^|[^.0-9\\])(?:0?\.)?0(?:ex|ch|r?em|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|g?rad|turn|m?s|k?Hz|dpi|dpcm|dppx|%)(?![a-z0-9])/gi, "$10");
+		content = content.replace(REGEXP_REPLACE_ZERO, "$10");
 
 		// Replace x.0(px,em,%) with x(px,em,%).
-		content = content.replace(/([0-9])\.0(ex|ch|r?em|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|g?rad|turn|m?s|k?Hz|dpi|dpcm|dppx|%| |;)/gi, "$1$2");
+		content = content.replace(REGEXP_REPLACE_ZERO_DOT, "$1$2");
 
 		// replace 0 0 0 0; with 0.
-		content = content.replace(/:0 0 0 0(;|\})/g, ":0$1");
-		content = content.replace(/:0 0 0(;|\})/g, ":0$1");
-		content = content.replace(/:0 0(;|\})/g, ":0$1");
+		content = content.replace(REGEXP_REPLACE_4_ZEROS, ":0$1");
+		content = content.replace(REGEXP_REPLACE_3_ZEROS, ":0$1");
+		content = content.replace(REGEXP_REPLACE_2_ZEROS, ":0$1");
 
 		// replace background-position:0; with background-position:0 0;
 		// same for transform-origin and box-shadow
-		pattern = /(background-position|transform-origin|webkit-transform-origin|moz-transform-origin|o-transform-origin|ms-transform-origin|box-shadow):0(;|\})/gi;
+		pattern = REGEXP_REPLACE_1_ZERO;
 		content = content.replace(pattern, (_, f1, f2) => f1.toLowerCase() + ":0 0" + f2);
 
 		// replace 0.6 to .6, but only when preceded by : or a white-space
-		content = content.replace(/(:|\s)0+\.(\d+)/g, "$1.$2");
+		content = content.replace(REGEXP_REPLACE_ZERO_DOT_DECIMAL, "$1.$2");
 
 		// shorten colors from rgb(51,102,153) to #336699
 		// this makes it more likely that it'll get further compressed in the next step.
-		pattern = /rgb\s*\(\s*([0-9,\s]+)\s*\)/gi;
+		pattern = REGEXP_REPLACE_RGB;
 		content = content.replace(pattern, (_, f1) => {
 			let rgbcolors = f1.split(","), hexcolor = "#";
 			for (let i = 0; i < rgbcolors.length; i += 1) {
@@ -697,34 +682,34 @@ this.uglifycss = this.uglifycss || (() => {
 		content = compressHexColors(content);
 
 		// Replace #f00 -> red
-		content = content.replace(/(:|\s)(#f00)(;|})/g, "$1red$3");
+		content = content.replace(REGEXP_REPLACE_HASH_COLOR, "$1red$3");
 
 		// Replace other short color keywords
-		content = content.replace(/(:|\s)(#000080)(;|})/g, "$1navy$3");
-		content = content.replace(/(:|\s)(#808080)(;|})/g, "$1gray$3");
-		content = content.replace(/(:|\s)(#808000)(;|})/g, "$1olive$3");
-		content = content.replace(/(:|\s)(#800080)(;|})/g, "$1purple$3");
-		content = content.replace(/(:|\s)(#c0c0c0)(;|})/g, "$1silver$3");
-		content = content.replace(/(:|\s)(#008080)(;|})/g, "$1teal$3");
-		content = content.replace(/(:|\s)(#ffa500)(;|})/g, "$1orange$3");
-		content = content.replace(/(:|\s)(#800000)(;|})/g, "$1maroon$3");
+		content = content.replace(REGEXP_REPLACE_HASH_COLOR_SHORT1, "$1navy$3");
+		content = content.replace(REGEXP_REPLACE_HASH_COLOR_SHORT2, "$1gray$3");
+		content = content.replace(REGEXP_REPLACE_HASH_COLOR_SHORT3, "$1olive$3");
+		content = content.replace(REGEXP_REPLACE_HASH_COLOR_SHORT4, "$1purple$3");
+		content = content.replace(REGEXP_REPLACE_HASH_COLOR_SHORT5, "$1silver$3");
+		content = content.replace(REGEXP_REPLACE_HASH_COLOR_SHORT6, "$1teal$3");
+		content = content.replace(REGEXP_REPLACE_HASH_COLOR_SHORT7, "$1orange$3");
+		content = content.replace(REGEXP_REPLACE_HASH_COLOR_SHORT8, "$1maroon$3");
 
 		// border: none -> border:0
-		pattern = /(border|border-top|border-right|border-bottom|border-left|outline|background):none(;|\})/gi;
+		pattern = REGEXP_REPLACE_BORDER_ZERO;
 		content = content.replace(pattern, (_, f1, f2) => f1.toLowerCase() + ":0" + f2);
 
 		// shorter opacity IE filter
-		content = content.replace(/progid:DXImageTransform\.Microsoft\.Alpha\(Opacity=/gi, "alpha(opacity=");
+		content = content.replace(REGEXP_REPLACE_IE_OPACITY, "alpha(opacity=");
 
 		// Find a fraction that is used for Opera's -o-device-pixel-ratio query
 		// Add token to add the '\' back in later
-		content = content.replace(/\(([-A-Za-z]+):([0-9]+)\/([0-9]+)\)/g, "($1:$2___QUERY_FRACTION___$3)");
+		content = content.replace(REGEXP_REPLACE_QUERY_FRACTION, "($1:$2___QUERY_FRACTION___$3)");
 
 		// remove empty rules.
-		content = content.replace(/[^};{/]+\{\}/g, "");
+		// content = content.replace(REGEXP_EMPTY_RULES, "");
 
 		// Add '\' back to fix Opera -o-device-pixel-ratio query
-		content = content.replace(/___QUERY_FRACTION___/g, "/");
+		content = content.replace(REGEXP_QUERY_FRACTION, "/");
 
 		// some source control tools don't like it when files containing lines longer
 		// than, say 8000 characters, are checked in. The linebreak option is used in
@@ -749,10 +734,10 @@ this.uglifycss = this.uglifycss || (() => {
 
 		// replace multiple semi-colons in a row by a single one
 		// see SF bug #1980989
-		content = content.replace(/;;+/g, ";");
+		content = content.replace(REGEXP_REPLACE_SEMI_COLUMNS, ";");
 
 		// trim the final string (for any leading or trailing white spaces)
-		content = content.replace(/(^\s*|\s*$)/g, "");
+		content = content.replace(REGEXP_TRIM, "");
 
 		// restore preserved tokens
 		for (let i = preservedTokens.length - 1; i >= 0; i--) {
@@ -760,7 +745,7 @@ this.uglifycss = this.uglifycss || (() => {
 		}
 
 		// restore preserved newlines
-		content = content.replace(/___PRESERVED_NEWLINE___/g, "\n");
+		content = content.replace(REGEXP_PRESERVED_NEWLINE, "\n");
 
 		// return
 		return content;