Before I join the language debate I’d like to share something entirely different. My take on optimizing your css files for production.
Making css files production ready is surprisingly simple. I use the following setup in my applications.
The css compressor (just removes whitespace in this configuration).
public class CssMinifier
{
static readonly Regex whitespaceRegex = new Regex(@"\s+", RegexOptions.Compiled | RegexOptions.Multiline | RegexOptions.ECMAScript);
// You can uncomment the following line if you're not using css hacks that rely on comment notation.
//static readonly Regex commentsRegex = new Regex(@"\/\*(.*?)\*\/ ", RegexOptions.Compiled | RegexOptions.Multiline | RegexOptions.ECMAScript);
static readonly Regex addLineBreaksRegex = new Regex(@"\} ", RegexOptions.Compiled | RegexOptions.Multiline | RegexOptions.ECMAScript);
static readonly Regex removeLastBreakRegex = new Regex(@"\n$", RegexOptions.Compiled | RegexOptions.Multiline | RegexOptions.ECMAScript);
static readonly Regex trimInsideLeftBracketsRegex = new Regex(@" \{ ", RegexOptions.Compiled | RegexOptions.Multiline | RegexOptions.ECMAScript);
static readonly Regex trimInsideRightBracketsRegex = new Regex(@"; \}", RegexOptions.Compiled | RegexOptions.Multiline | RegexOptions.ECMAScript);
public static string Compress(string source)
{
source = whitespaceRegex.Replace(source, " ");
// You can uncomment the following line if you're not using css hacks that rely on comment notation.
//source = commentsRegex.Replace(source, string.Empty);
source = addLineBreaksRegex.Replace(source, "}\r\n");
source = removeLastBreakRegex.Replace(source, string.Empty);
source = trimInsideLeftBracketsRegex.Replace(source, " {");
source = trimInsideRightBracketsRegex.Replace(source, "}");
return source;
}
}
(\cf4=”" <\cf1="" list\cf0="" new\cf0="" csslib="\par" >=”" .mappath(folder);\par=”" fileoperations\cf0=”" path=”\cf4″ folder)\par=”" target,=”" stringbuilder\cf0=”" readfolder(\cf4=”" void\cf0=”" static\cf0=”" private\cf0=”" );\par=”" ?\\r\\n?\cf0=”" target.append(\cf5=”" target.append(readfile(fpath));\par=”" fpath);\par=”" \\r\\n\\r\\n?\cf0=”" *=”" \{0\}=”" ?=”" target.appendformat(\cf5=”" fpath)\par=”" readpath(\cf4=”" ??\cf0=”" #endif\par=”" ??\cf1=”" .readalltext(path);\par=”" file\cf0=”" system.io.\cf4=”" #else\par=”" fileoperations.readfile(path);\par=”" return=”" ??\cf7=”" mode.\par=”" production=”" in=”" is=”" app=”" the=”" when=”" disk=”" from=”" read=”" file=”" cache=”" \cf6=”" !debug=”" #if\cf0=”" .mappath(relativepath);\par=”" relativepath)\par=”" readfile(\cf1=”" context.response.write(sb.tostring());\par=”" context.response.write(cssminifier.compress(sb.tostring()));\par=”" context.response.cache.setvaliduntilexpires(true);\par=”" context.response.cache.setexpires(datetime.now.adddays(1));\par=”" context.response.cache.setcacheability(httpcacheability.public);\par=”" mode\par=”" release=”" handler=”" this=”" of=”" results=”" caching=”" start=”" ;\par=”" css?\cf0=”" ?text=”" context.response.contenttype=”\cf5″ cssbase);\par=”" readfolder(sb,=”" ))\par=”" ?application?\cf0=”" (context.request.url.absoluteuri.contains(\cf5=”" ();\par=”" sb=”\cf1″ ];\par=”" ?windows?\cf0=”" windowstheme=”context.Request.QueryString[\cf5" context.response.clear();\par="" context)\par="" httpcontext\cf0="" processrequest(\cf4="" common="" ~="">I will typically have a FileOperations helper class that takes care of common file system things.
public class StylesHandler :IHttpHandler
{
private static string windowsTheme;
private const string CSSBASE = "~/common/css";
public void ProcessRequest(HttpContext context)
{
context.Response.Clear();
windowsTheme = context.Request.QueryString["windows"];
StringBuilder sb = new StringBuilder();
if (context.Request.Url.AbsoluteUri.Contains("application"))
ReadFolder(sb, CSSBASE);
context.Response.ContentType = "text/css";
#if !DEBUG //start caching the results of this handler when in release mode
context.Response.Cache.SetCacheability(HttpCacheability.Public);
context.Response.Cache.SetExpires(DateTime.Now.AddDays(1));
context.Response.Cache.SetValidUntilExpires(true);
context.Response.Write(CssMinifier.Compress(sb.ToString()));
#else
context.Response.Write(sb.ToString());
#endif
}
private static string ReadFile(string relativePath)
{
string path = FileOperations.MapPath(relativePath);
#if !DEBUG //Cache the file read from disk when the app is in production mode.
return FileOperations.ReadFile(path);
#else
return System.IO.File.ReadAllText(path);
#endif
}
private static void ReadPath(StringBuilder target, string fPath)
{
target.AppendFormat("/* {0} */\r\n\r\n", fPath);
target.Append(ReadFile(fPath));
target.Append("\r\n");
}
private static void ReadFolder(StringBuilder target, string folder)
{
string path = FileOperations.MapPath(folder);
List<string> csslib =
new List<string>(Directory.GetFiles(path, "*.css", SearchOption.TopDirectoryOnly));
foreach (string s in csslib)
{
ReadPath(target, s);
}
if(!string.IsNullOrEmpty(windowsTheme))
{
ReadPath(target, string.Format("{0}/window_themes/{1}.css", CSSBASE, windowsTheme));
}
}
public bool IsReusable
{
get { return true; }
}
}





