General CSS Path Transform for ASP.NET Bundling
CSS path transform problems can arise with bundling and minification, especially with large volumes of externally-based ones. Read on for an ASP.NET-based solution!
Join the DZone community and get the full member experience.
Join For FreeIn a couple of ASP.NET projects, I have had CSS path transform problems with bundling and minification. CSS files are coming in from external agencies and from different open-source projects and the number of files is so big that it is problematic to go through all of these after updates. I found a path transform solution that works for me in most cases and it is described here.
What Does CssRewriteUrlTransform Do, Exactly?
It seems there is something up with CssRewriteUrlTransform class. From MSDN, we can read the following about it: 'Rewrites urls to be absolute so assets will still be found after bundling.' We can find out about it from the Process() method page: Example: bundle.Include(“~/content/some.css”)
will transform url(images/1.jpg) => url(/content/images/1.jpg)
.
If all CSS is under our control, then we should be good to go with CssRewriteUrlTransform class. As I found out, it doesn’t work in all cases. Especially if you have to bundle a big number of CSS files provided by some agency.
I have some web applications where tens of CSS files are bundled and minified. The files come in as they are: some paths are relative to CSS files and some paths are already absolute, meaning that after publishing the web application to some subfolder (some customers need it) some of the files in the design are not showing up anymore. It is unthinkable that after every CSS update I would have to manually go through all these files and format paths, like the CssRewriteUrlTransform class expects.
Custom Bundle Transform for CSS Paths
After some digging around on the internet, I found some code samples and I ended up with the following path transforming class:
public class StyleRelativePathTransform : IBundleTransform
{
private static Regex pattern = new Regex(@"url\s*\(\s*([""']?)([^:)]+)\1\s*\)", RegexOptions.IgnoreCase); public void Process(BundleContext context, BundleResponse response)
{
response.Content = string.Empty; foreach (BundleFile file in response.Files)
{
using (var reader = new StreamReader(file.VirtualFile.Open()))
{
var contents = reader.ReadToEnd();
var matches = pattern.Matches(contents);if(matches.Count == 0)
{
continue;
} var directoryPath = VirtualPathUtility.GetDirectory(file.VirtualFile.VirtualPath);
foreach (Match match in matches)
{
var fileRelativePath = match.Groups[2].Value;
var fileVirtualPath = VirtualPathUtility.Combine(directoryPath, fileRelativePath);
var quote = match.Groups[1].Value;
var replace = String.Format("url({0}{1}{0})", quote, VirtualPathUtility.ToAbsolute(fileVirtualPath)); contents = contents.Replace(match.Groups[0].Value, replace);
} response.Content = String.Format("{0}\r\n{1}", response.Content, contents);
}
}
}
}
It is able to handle URLs in CSS files perfectly in my case. An additional change is needed for bundling and minification. Let’s open BundleConfig class in the App_Start folder and the make styles section of the RegisterBundles method look similar to the following code:
var styles = new StyleBundle("~/styles")
.Include("~/content/pages/meetings.min.css", new CssRewriteUrlTransform())
.Include("~/content/pages/login.min.css", new CssRewriteUrlTransform())
.Include("~/content/pages/error.min.css", new CssRewriteUrlTransform())
.Include("~/content/Site.css", new CssRewriteUrlTransform());
styles.Transforms.Insert(0, new StyleRelativePathTransform());
styles.Orderer = new AsIsBundleOrderer();
bundles.Add(styles);
AsIsBundeOrderer
is another class I found on the web and it keeps CSS files in order, so I added these to bundle. By default, they are reordered for reasons unknown to me.
public class AsIsBundleOrderer : IBundleOrderer
{
public virtual IEnumerable<FileInfo> OrderFiles(BundleContext context, IEnumerable<FileInfo> files)
{
return files;
}public IEnumerable<BundleFile> OrderFiles(BundleContext context, IEnumerable<BundleFile> files)
{
return files;
}
}
After applying the transform class and orderer given in this blog post, my issues with the long list of externally provided CSS files were solved.
Published at DZone with permission of Gunnar Peipman, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments