Are you using Cassette?

20 במרץ 2012

ASP.NET 4.5 Developer Preview introduces a new capability to manage scripts bundling and minification. You can read about it here.


However, the current implementation lacks a very important aspect of scripts management which is managing the dependencies. As you know, when referencing a single script file you also need to reference all its dependencies.


Wouldn't it be nice if someone automatically injects the right dependencies? Enter Cassette world …


Cassette is an HttpModule which seats inside ASP.NET pipeline and monitors requests for scripts and CSS. Cassette can automatically bundle and minify your code and even more interesting it can parse each JavaScript/CSS file and understand the list of dependencies. When analysis is completed Cassette renders a flat list of references with the correct order. Cool, right?


In the following section I would like to describe the way you can start using Cassette. Of course, you can skip this introduction and get the formal Cassette documentation from here.


Let's assume we are dealing with a very basic application. The default view Home/Index.cshtml displays a simple button and when the user clicks it a jQuery UI dialog appears. Starter code can be downloaded from here


To accomplish this basic application I added some script references were needed. Typically, this means adding a script references inside the layout file (for jQuery and jQuery UI) and one more inside the Home/Index.cshtml view.


The layout file looks like this:

<head>
    <meta charset="utf-8" />
    <title>@ViewBag.Title</title>
   
    <link href="@Url.Content("~/Content/themes/base/jquery.ui.all.css")" rel="stylesheet" type="text/css" />
    <link href="@Url.Content("~/Content/Site.css")" rel="stylesheet" type="text/css" />

    <script src="@Url.Content("~/Scripts/jquery-1.5.1.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/jquery-ui-1.8.11.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/modernizr-1.7.js")" type="text/javascript"></script
>
</
head
>


And Home/Index.cshtml view:

<script src="@Url.Content("~/Scripts/App/Home/Index.js")" type="text/javascript"></script>

<h2>@ViewBag.Message</h2
>
<
p>
    To learn more about ASP.NET MVC visit <a href="http://asp.net/mvc" title="ASP.NET MVC Website">http://asp.net/mvc</a>.

    <input type="button" id="buttonSayHello" value="Say Hello" 
/>
</
p
>

To summarize, we have a view which uses a script file which uses jQuery and jQuery UI libraries.


Now, lets see how Cassette can help us managing all these dependencies.


Open NuGet Manager UI and install a package named "Cassette.Web"


Now we need to fix scripts, CSS and views.


Open ~/Views/Shared/_Layout.cshtml and fix it as below:

<head>
    <meta charset="utf-8" />
    <title>@ViewBag.Title</title>

    @Bundles.RenderStylesheets()
    @Bundles.RenderScripts()

    @{
        Bundles.Reference("~/Content/themes/base/jquery.ui.all.css");
        Bundles.Reference("~/Content/Site.css");
    }
</head
>


Fix ~/Views/Home/Index.cshtml:

@{
    Bundles.Reference("~/Scripts/App/Home/Index.js");
}

<h2>@ViewBag.Message</h2
>
<
p>
    To learn more about ASP.NET MVC visit <a href="http://asp.net/mvc" title="ASP.NET MVC Website">http://asp.net/mvc</a>.

    <input type="button" id="buttonSayHello" value="Say Hello" 
/>
</
p
>


Fix ~/Scripts/App/Home/Index.js to hold a VS doc reference to jQuery UI script. Cassette understands this comment as a script dependency:

/// <reference path="../../Libs/jquery-ui-1.8.11.js" />

(function () {
    $(function () {
        $("#buttonSayHello").click(function () {
            var dlg = $("<div>Hello World</div>");
            dlg.dialog({
                modal: true
            });
        });
    });
})();


Please note that we are referencing the non minified version of jQuery UI. The reason is that Cassette supports its own built-in minification.


Fix jQuery UI script to reference jQuery Core script:

/// <reference path="jquery-1.5.1.js" />

Last thing, we need to fix jQuery CSS files. Currently, those CSS are using the @import keyword. To utilize Cassette support for CSS it is recommended that you switch those commands to Cassette syntax. For example, add the following lines to the beginning of jquery.ui.all.css file and remove the @import lines:

/*
@reference "jquery.ui.base.css";
@reference "jquery.ui.theme.css";
*/


You should fix jquery.ui.base.css in the same way:

/*
@reference "jquery.ui.core.css";
@reference "jquery.ui.resizable.css";
@reference "jquery.ui.selectable.css"
@reference "jquery.ui.accordion.css";

@reference "jquery.ui.autocomplete.css";
@reference "jquery.ui.button.css";
@reference "jquery.ui.dialog.css";
@reference "jquery.ui.slider.css";
@reference "jquery.ui.tabs.css";
@reference "jquery.ui.datepicker.css";
@reference "jquery.ui.progressbar.css";
*/


That’s it !!! After fixing views, scripts and CSSs you are ready to execute the application and monitor Cassette magic.


Look at the HTML source and notice how Cassette injects all the required scripts and CSS references. In addition, Cassette uses a hash value of the script content to guard against browser caching. Cool.

<link href="/_cassette/asset/Content/themes/base/jquery.ui.core.css?9d38d2bbe70eb75c4119ab72510a117cfb49b913" type="text/css" rel="stylesheet"/>
<
link href="/_cassette/asset/Content/themes/base/jquery.ui.resizable.css?4860357b34ca9d209a61784a0839e55a9ac6372c" type="text/css" rel
="stylesheet"/>
<
link href="/_cassette/asset/Content/themes/base/jquery.ui.selectable.css?e381398c50e0566a49408576e0a87df9217f16d8" type="text/css" rel
="stylesheet"/>
<
link href="/_cassette/asset/Content/themes/base/jquery.ui.accordion.css?73fb7966f0b3a2bf686deb7c8dec0edd42894eb7" type="text/css" rel
="stylesheet"/>
<
link href="/_cassette/asset/Content/themes/base/jquery.ui.autocomplete.css?10ac974fd724c7dd45359c494c8e63d4fac5c226" type="text/css" rel
="stylesheet"/>
<
link href="/_cassette/asset/Content/themes/base/jquery.ui.button.css?26b11c4e2b69746007bb9e9d819b54f3368597b0" type="text/css" rel
="stylesheet"/>
<
link href="/_cassette/asset/Content/themes/base/jquery.ui.dialog.css?a4c49ffd917f8f26b225abfe26bef4c7ea334cbd" type="text/css" rel
="stylesheet"/>
<
link href="/_cassette/asset/Content/themes/base/jquery.ui.slider.css?6880af0ef985e536612b67e7b51c41ae074dd303" type="text/css" rel
="stylesheet"/>
<
link href="/_cassette/asset/Content/themes/base/jquery.ui.tabs.css?1bb52f3a3d42c8337947b6c43a1cdbb365364841" type="text/css" rel
="stylesheet"/>
<
link href="/_cassette/asset/Content/themes/base/jquery.ui.datepicker.css?b40203fd3aaf03fecdb5de53ee73e0d174291909" type="text/css" rel
="stylesheet"/>
<
link href="/_cassette/asset/Content/themes/base/jquery.ui.progressbar.css?8e462a4c8734825474b5fd073965319af166abc8" type="text/css" rel
="stylesheet"/>
<
link href="/_cassette/asset/Content/themes/base/jquery.ui.base.css?5ece017066850b7e1ac6cfeae595740e324bc18d" type="text/css" rel
="stylesheet"/>
<
link href="/_cassette/asset/Content/themes/base/jquery.ui.theme.css?42d2c7901f9c0bd5d0a5b3a68191f82837f8658f" type="text/css" rel
="stylesheet"/>
<
link href="/_cassette/asset/Content/themes/base/jquery.ui.all.css?1ce06a5255860062cfdb27f241c2ed1c70a64a81" type="text/css" rel
="stylesheet"/>
<
link href="/_cassette/asset/Content/Site.css?46fe61a9c663b0bb427604fb6bd30608930274ac" type="text/css" rel="stylesheet"/>
   
<script src="/_cassette/asset/Scripts/Libs/jquery-1.5.1.js?1e9619c4943d9fc8dbb73191d4179b6dd75ab25a" type="text/javascript"></script
>
<
script src="/_cassette/asset/Scripts/Libs/jquery-ui-1.8.11.js?be8e64d1d5e7005b31d42f79c3de3e7d08f2bc47" type="text/javascript"></script
>
<
script src="/_cassette/asset/Scripts/App/Home/Index.js?00b16e534e25dfa101f194748034c8b0fbda2d01" type="text/javascript"></script
>

Please note how Cassette resolves the dependencies between Index.js and jQuery scripts.


We can do even more with Cassette. Open ~/CassetteConfiguration.cs and change it:

public class CassetteConfiguration : ICassetteConfiguration
{
    public void Configure(BundleCollection bundles, CassetteSettings settings)
    {
        bundles.Add<ScriptBundle>("~/Scripts/App");
        bundles.Add<ScriptBundle>("~/Scripts/Libs");

        bundles.Add<StylesheetBundle>("~/Content/Site.css");
        bundles.Add<StylesheetBundle>("~/Content/themes/base");
    }
}


Execute application and look at the HTML source:

<link href="/_cassette/asset/Content/themes/base/jquery.ui.accordion.css?73fb7966f0b3a2bf686deb7c8dec0edd42894eb7" type="text/css" rel="stylesheet"/>
<
link href="/_cassette/asset/Content/themes/base/jquery.ui.autocomplete.css?10ac974fd724c7dd45359c494c8e63d4fac5c226" type="text/css" rel
="stylesheet"/>
<
link href="/_cassette/asset/Content/themes/base/jquery.ui.button.css?26b11c4e2b69746007bb9e9d819b54f3368597b0" type="text/css" rel
="stylesheet"/>
<
link href="/_cassette/asset/Content/themes/base/jquery.ui.core.css?9d38d2bbe70eb75c4119ab72510a117cfb49b913" type="text/css" rel
="stylesheet"/>
<
link href="/_cassette/asset/Content/themes/base/jquery.ui.datepicker.css?b40203fd3aaf03fecdb5de53ee73e0d174291909" type="text/css" rel
="stylesheet"/>
<
link href="/_cassette/asset/Content/themes/base/jquery.ui.dialog.css?a4c49ffd917f8f26b225abfe26bef4c7ea334cbd" type="text/css" rel
="stylesheet"/>
<
link href="/_cassette/asset/Content/themes/base/jquery.ui.progressbar.css?8e462a4c8734825474b5fd073965319af166abc8" type="text/css" rel
="stylesheet"/>
<
link href="/_cassette/asset/Content/themes/base/jquery.ui.resizable.css?4860357b34ca9d209a61784a0839e55a9ac6372c" type="text/css" rel
="stylesheet"/>
<
link href="/_cassette/asset/Content/themes/base/jquery.ui.selectable.css?e381398c50e0566a49408576e0a87df9217f16d8" type="text/css" rel
="stylesheet"/>
<
link href="/_cassette/asset/Content/themes/base/jquery.ui.slider.css?6880af0ef985e536612b67e7b51c41ae074dd303" type="text/css" rel
="stylesheet"/>
<
link href="/_cassette/asset/Content/themes/base/jquery.ui.tabs.css?1bb52f3a3d42c8337947b6c43a1cdbb365364841" type="text/css" rel
="stylesheet"/>
<
link href="/_cassette/asset/Content/themes/base/jquery.ui.base.css?5ece017066850b7e1ac6cfeae595740e324bc18d" type="text/css" rel
="stylesheet"/>
<
link href="/_cassette/asset/Content/themes/base/jquery.ui.theme.css?42d2c7901f9c0bd5d0a5b3a68191f82837f8658f" type="text/css" rel
="stylesheet"/>
<
link href="/_cassette/asset/Content/themes/base/jquery.ui.all.css?1ce06a5255860062cfdb27f241c2ed1c70a64a81" type="text/css" rel
="stylesheet"/>
<
link href="/_cassette/asset/Content/Site.css?46fe61a9c663b0bb427604fb6bd30608930274ac" type="text/css" rel="stylesheet"/>
   
<script src="/_cassette/asset/Scripts/Libs/jquery-1.5.1.js?1e9619c4943d9fc8dbb73191d4179b6dd75ab25a" type="text/javascript"></script
>
<
script src="/_cassette/asset/Scripts/Libs/jquery-ui-1.8.11.js?be8e64d1d5e7005b31d42f79c3de3e7d08f2bc47" type="text/javascript"></script
>
<
script src="/_cassette/asset/Scripts/Libs/jquery.unobtrusive-ajax.js?ab8e4edddbe5721804e6eeb588684d7a31c10c25" type="text/javascript"></script
>
<
script src="/_cassette/asset/Scripts/Libs/jquery.validate.js?378f121fe0d02e2d59c64094e47e107e453f56a9" type="text/javascript"></script
>
<
script src="/_cassette/asset/Scripts/Libs/jquery.validate.unobtrusive.js?57492833508444cc10554b9dd6240c313334641c" type="text/javascript"></script
>
<
script src="/_cassette/asset/Scripts/Libs/MicrosoftAjax.debug.js?e68b3852c1d342f7515ae93f743fd925b84abf6a" type="text/javascript"></script
>
<
script src="/_cassette/asset/Scripts/Libs/MicrosoftMvcAjax.debug.js?56e5c96216e716945637882bde9c2503209d5948" type="text/javascript"></script
>
<
script src="/_cassette/asset/Scripts/Libs/MicrosoftMvcValidation.debug.js?1dc04a87db830ff07c2e962aa505b575853ac282" type="text/javascript"></script
>
<
script src="/_cassette/asset/Scripts/Libs/modernizr-1.7.js?aa20a6d214a44351de222630fa5c2f6bd5850867" type="text/javascript"></script
>
<
script src="/_cassette/asset/Scripts/App/Home/Index.js?00b16e534e25dfa101f194748034c8b0fbda2d01" type="text/javascript"></script
>

As you can see, Cassette is including all files found under the specified directory. Why is that?


In our sample we defined a bundle for the ~/Scripts/App directory and also defined a reference for the jquery-ui-1.8.11.js script which is part of that bundle. Cassette rule is very simple regarding this case. When refering to a script found under a bundle Cassette ensures that the whole bundle is referenced. This means that all scripts/CSSs under the bundle are injected.


This is reasonable. Bundling means “get me all files” and Cassette does that for your. When switching to production you will see only one reference to the bundle itself.


Now, lets switch to production. Open ~/Web.config and change debug setting to false:

<compilation debug="false" targetFramework="4.0">

Execute application and look at the HTML source. Cassette enforces the bundling configuration and of course minifies the content:

<link href="/_cassette/stylesheetbundle/Content/themes/base_ba0e60ffa8a7b377d7725e11bc30a12ccac64c98" type="text/css" rel="stylesheet"/>
<
link href="/_cassette/stylesheetbundle/Content/Site.css_46fe61a9c663b0bb427604fb6bd30608930274ac" type="text/css" rel="stylesheet"/>

<script src="/_cassette/scriptbundle/Scripts/Libs_a452bb8b8567293fda9273cd0b929d0927be6cd1" type="text/javascript"></script
>
<
script src="/_cassette/scriptbundle/Scripts/App_00b16e534e25dfa101f194748034c8b0fbda2d01" type="text/javascript"></script
>

When developing the web site you don’t want minification and bundling. This is exactly what Cassette does when respecting the “debug” setting inside web.config


Full fixed sample with Cassette configuration can be downloaded from here


I realy love Cassette. What do you think?

Add comment
facebook linkedin twitter email

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

2 comments

  1. Per25 במרץ 2012 ב 19:54

    Thank you very much for sharing this information! Helped me alot!

    Reply
  2. Longo5 במרץ 2013 ב 19:01

    I think these were about to expire however,
    which means you ought to do it today. >>Here you just need to discuss your preferences and design of the cards.
    Try to find out stores where you can easily use these coupons.

    Reply