- Visual Studio 11 Beta
- ASP.NET MVC 4 Beta
- Single Page Application
- Bundles (new combining/minification stuff)
- Web API
- Twitter Bootstrap 2.0
- Modernizr
- MvcScaffolding
- etc…
The Problems
Everything was going well, adding NuGet packages left and right (click, click, click!)I added my JsTrace MVC NuGet package and started to get JavaScript errors.It turns out that I had 2 problems with the Bundles feature:
- It doesn't add anything to the Bundles automatically except the original scripts in the Template.
- It automatically uses minified files by looking for "min" or not "debug" JavaScript or CSS files.
Fix #1 - Add my own Bundles
Grr! To fix #1, I had to add my own Bundle to the Global.asax:Bundle tomsBundle = new Bundle("~/core/js"); tomsBundle.AddDirectory("~/Scripts", "Trace*", false, true); tomsBundle.AddDirectory("~/Scripts/Core", "Foo.*", false, true); BundleTable.Bundles.Add(tomsBundle);OK, problem solved.
Fix #1a - Bootstrap Bundles
While I was there, I decided to make an extension method to add the Twitter.Bootstrap stuff to the BundleTable:public static class BootstrapExtensionMethods { public static void EnableBootstrapBundle( this BundleCollection bundles) { var bootstrapCss = new Bundle("~/bootstrap/css", new CssMinify()); bootstrapCss.AddDirectory("~/Content", "bootstrap*", false, true); bundles.Add(bootstrapCss); var bootstrapJs = new Bundle("~/bootstrap/js", new JsMinify()); bootstrapJs.AddFile("~/Scripts/bootstrap.js"); bundles.Add(bootstrapJs); } }So, add a one-liner to the Global.asax:
BundleTable.Bundles.EnableBootstrapBundle();
Fix #2 - Supersize Me!
Now, for Problem #2 – I want to have big, fat un-minimized debug versions of my files while I'm developing. So, after digging around with Google and ILSpy, I came across the implementation of JsMinify and noticed that it uses a property called "EnableInstrumentation" to determine whether or not it should minify the files. Eureka!Of course, they don't actually provide you with a simple way to set that flag. So, I realized I had to create my own implementation of 2 interfaces:
- IBundleBuilder - has the responsibility for iterating over a collection of files and combining them
- IBundleTransform – has the responsibility of "transforming" (i.e. minifying) the bundles (default implementations being JsMinify and CssMinify)
- NonMinifyingBundleBuilder – reverses Microsoft's logic and replaces the minified files with their debug counterparts.
- InstrumentedBundleTransform – sets the "EnableInstrumentation" flag to true while processing.
BundleTable.Bundles.DisableMinify();This is probably best wrapped in an "#if DEBUG" so your release code still gets all minified.
In fact, that's what I do in the NuGet package I created out of this!
NOTE: this is a beta (since it relies on Microsoft.Web.Optimization 1.0.0-beta), so you will have to install the NuGet package with the NuGet Package Console like this:
PM> Install-Package McKearney.BundlingTweaks
-Pre
Where in the Global.asax.cs file does the bundle registration code belong?
ReplyDeleteNever mind, I figured it out. However, I've discovered a new problem. In bootstrap.css you have:
ReplyDelete...
background-image: url("images/glyphicons-halflings.png");
...
When the browser attempts to resolve this, the path is:
/bootstrap/images/glyphicons-halflings.png
which results in a 404. So, how do you resolve this with the bundles, WITHOUT editing the .css? Thanks!
OK. Problems fixed. The issue resides in how the bundles are created. I changed the code to this:
ReplyDelete//bootstrap
var bootstrapCss = new Bundle("~/Content/css", new CssMinify());
bootstrapCss.AddFile("~/Content/bootstrap.css");
bootstrapCss.AddFile("~/Content/bootstrap-responsive.css");
BundleTable.Bundles.Add(bootstrapCss);
var bootstrapJs = new Bundle("~/Content/js", new JsMinify());
bootstrapJs.AddFile("~/Scripts/bootstrap.js");
BundleTable.Bundles.Add(bootstrapJs);
and in my HTML:
<script src="@BundleTable.Bundles.ResolveBundleUrl("~/Scripts/js")" type="text/javascript"></script>
<link href="@BundleTable.Bundles.ResolveBundleUrl("~/Content/css")" rel="stylesheet" type="text/css" />
<script src="@BundleTable.Bundles.ResolveBundleUrl("~/Content/js")" type="text/javascript"></script>
It's important that jQuery is added BEFORE the bootstrap JS. Also, in order for bootstrap-responsive.css to function properly, the CSS file must be added AFTER the base bootstrap.css code. Adding all the bootstrap CSS files using AddDirectory creates a bundle with the CSS out of order.
Just got back from vacation. Thanks for finding and fixing this stuff. I will try to incorporate the changes very soon.
ReplyDeleteTom
One other note. The Bootstrap fluid example has some additional CSS in the <head> tag:
Delete<style>
body
{
padding-top: 60px;
padding-bottom: 40px;
}
.sidebar-nav
{
padding: 9px 0;
}
</style>
that goes BETWEEN the bootstrap.css and bootstrap-responsive.css tags. I just put that into a file, bootstrap-tweak.css and sandwiched it between the AddFile methods:
bootstrapCss.AddFile("~/Content/bootstrap.css");
bootstrapCss.AddFile("~/Content/bootstrap-tweak.css");
bootstrapCss.AddFile("~/Content/bootstrap-responsive.css");
Would you prefer to just give me a pull request with your fixes? :)
DeleteSure. Where is your project hosted? Link?
ReplyDeleteThe code's here..
ReplyDeletehttp://bundling.codeplex.com/
Let me know if you have any problems with it.
Tom