SquishIt – The Friendly ASP.NET JavaScript and CSS Squisher

Please don’t post support questions into the comments on this post! If you have questions, please post them to the SquishIt Google Group.

I’ve received more feedback via e-mail on SquishIt than on pretty much any other post or project I’ve ever worked on in the past. I appreciate it! Most all of the feedback has been extremely positive, with people thanking me for creating such a great tool. Well, I don’t know how great it is, but people seem to like it!

Anyways, the one complain that I keep hearing over and over again is that I need to create a better guide to using it. And I agree, even though SquishIt is a breeze to use, I still need to provide a better "getting started" document.

But before I start rambling, let’s get on with the tutorial….


Getting Setup With SquishIt

As a side note, there are quite a few more options that SquishIt provides to tweak the squishing experience, but this post just covers the basic options for squishing your CSS and JavaScript.

The first thing that you need to do is head over to the SquishIt download page on GitHub and grab the zip file for the latest build of SquishIt. If you want, you could also grab the source and build it from there. Your choice!

The next step is to grab a current ASP.NET or ASP.NET MVC project. You could also create a new one, like I am going to do for this post. I’m first going to create a folder for my project, and inside of that folder I am going to create my usual “src” and “lib” folders, like this:

project folder

In the “src” folder I just have my project source, and in my “lib” folder is where I am going to drop any third party libraries that my project uses. Inside of my “lib” folder I am going to create a “SquishIt” folder:

image

And then put the contents of the zip file I downloaded from GitHub in there:

image

Now we are ready to get started in our website!

Getting Your JavaScript And CSS Setup

Now that we have the SquishIt binaries in the site, we can go over into Visual Studio and start getting everything wired up. The first thing we need to do is to get some CSS and JavaScript into our project. I’m going to pull in the uncompressed CSS from the 960 Grid System project, and then an unminified copy of jQuery and jQuery UI:

image

Note: Here I am using a Web Forms project, because I want to show that even though this was originally designed with ASP.NET MVC in mind, it works just the same in Web Forms!

Let’s first integrate the CSS and JavaScript into the website in the normal fashion, this will let those who are migrating their sites do it a bit easier.

First we will add tags to reference both the CSS and JavaScript in the page:

<head runat="server">
    <title></title>
    <link type="text/css" rel="stylesheet" href="/css/reset.css" />
    <link type="text/css" rel="stylesheet" href="/css/text.css" />
    <link type="text/css" rel="stylesheet" href="/css/960.css" />
</head>
<body>
    <form id="form1" runat="server">
    </form>
    <script type="text/javascript" src="/js/jquery-1.4.2.js"></script>
    <script type="text/javascript" src="/js/jquery-ui-1.8.1.js"></script>
</body>

Next, we will look in the Chrome developer tools to see just how much CSS and JavaScript that we are passing across the wire:

image

So, 11KB of CSS and 560KB of JavaScript, that is a ton! Let’s go ahead and see if we can do something about that.

Integrating SquishIt Into Your Website

The next thing we need to do is add a reference to the SquishIt.Framework.dll from the web project:

image

After we have created a reference, now we are ready to squish some Css and JavaScript! The first thing you are going to want to do is to import the SquishIt namespace at the top of the page:

<%@ Import Namespace="SquishIt.Framework" %>

Now we can create a Bundle. A Bundle is a single group of files that will be compressed into a single file. In this first example we are going to create a CSS bundle:

<%= Bundle.Css()
        .Add("~/css/reset.css")
        .Add("~/css/text.css")
        .Add("~/css/960.css")
        .Render("~/css/combined_#.css")
%>

In this code we are starting with the Bundle class, and then calling the "Css" method which lets us start a CSS bundle. Next we just call the "Add" method over and over with each file we want to add. Notice that I have put the tildes (~) on the front of each path, SquishIt will automatically expand app relative paths in websites. And finally we call the "Render" method which writes the bundle out to a particular file.

Also notice that we have the "_#" in the output file name. This will cause bundler to render a hash of the file contents into the filename. This allows us to invalidate the file if a change is made to one of our CSS files. If you don’t include that in there, then the hash is appended as a querystring parameter. This will also work for invalidating the file, but could cause some caching proxies not to cache the file. On the flip side though, if you keep changing files a lot, you could build up a bunch of combined CSS files in your folder since the file name keeps changing. You’ll have to manually clean them out.

Now that we have squished our css, we can do the same thing for the JavaScript:

<%= Bundle.JavaScript()
        .Add("~/js/jquery-1.4.2.js")
        .Add("~/js/jquery-ui-1.8.1.js")
        .Render("~/js/combined_#.js")
%>

Looks very similar, right? We just do the exact same thing.

WAIT YOU’RE NOT DONE YET!

I have one little catch that you need to know about. If you are like me, then you probably have your web.config set to debug="true" most of the time. And if you do, then when you run it, you are going to see the references to the JavaScript and CSS files spit out normally and they won’t be compressed. This is because trying to debug and develop with compressed CSS and JavaScript is a HUGE pain in the butt. In fact, it is practically impossible. So when you are operating in debug mode in your website, SquishIt keeps the files uncompressed and separated.

If you set to debug="false" you will get the combined and compressed JavaScript and CSS, or you can call the "ForceRelease" method like this:

<%= Bundle.Css()
        .Add("~/css/reset.css")
        .Add("~/css/text.css")
        .Add("~/css/960.css")
        .ForceRelease()
        .Render("~/css/combined_#.css")
%>

Great! Just remember since SquishIt caches everything, if you make a change like this, you’ll need to restart your site by poking the web.config or some other method.

Now you’re ready to squish some CSS and JavaScript!

When we run the website, we will now see only a single CSS and JavaScript reference, and when we look in the Chrome developer tools to see how much JavaScript and CSS we are passing, we get a much better picture:

image

Phew. We got almost a 50% reduction in size! PLUS, just as importantly, we got three less http requests. Not bad for 5 minutes of work! If we had a production site, which might have dozens of JavaScript files, this could be an enormous time saver.

Finally

I hope you see how easy it is to integrate SquishIt into your website. In only a few minutes you can be up and running with all of your JavaScript and CSS being combined and compressed. It makes your life much easier, and your users will thank you!

Please don’t post support questions into the comments on this post! If you have questions, please post them to the SquishIt Google Group.

Be Sociable, Share!

146 comments

  1. @Justin: yes I did pull latest sources from http://github.com/valmont/SquishIt (linked from this site somewhere) and tried quckyl adding utf-8 encoding to the file writer, to no avail.

    However, later on I noticed there is another repository (presumably yours) at
    http://github.com/jetheredge/SquishIt
    This one unfortunately still has the sasme problem.

    I can have a look at it, but any hints or even fixes will be appreciated.

    Test case could be a simple js with a line:
    alert(‘test ČŽŠĐćčžšđć’);

  2. @stefan Try switching from the default YUI compressor over to JsMin. I believe the call is "WithCompressor()". I believe the issues around i18n are in the YUI compressor.

  3. @stefan Oh, and yes, I am going to get some tests in there soon for this. Thanks for the code.

  4. JsMin has other problems with my js files ("JSMIN unterminated string literal: 10"). I’ve added exception handling to MinifyJavaScript() so that at least it also says which is the problematic file.

    Another improvement i have in there is to not re-minify files whose names end with ".min.js" (for jquery, plugins…)

    code below, feel free to include it.

    [quote]private StringBuilder MinifyJavaScript(List<string> files, IJavaScriptCompressor minifier)
    {
    var outputJavaScript = new StringBuilder();
    foreach (string file in files)
    {
    try
    {
    string content = ReadFile(file);
    if (file.EndsWith(".min.js"))
    {
    outputJavaScript.Append(content);
    }
    else
    {
    outputJavaScript.Append(minifier.CompressContent(content));
    }
    }
    catch (Exception e)
    {
    throw new Exception(string.Format("Error processing {0}", file), e);
    }

    }
    return outputJavaScript;
    }
    [/quote]

  5. I lazy load many of my scripts. Is there a way to use SquishIt in Application Start to generate a compressed script that can then be referenced as a local javascript file later on?

    thanks for your hard work!

  6. How about minimizing also inline javascript on the fly? Eg:
    [quote]<squishit:minimize runat="server" compressor="JsMin">
    alert("Hello, <%=Html.Encode(Model.firstname)%>!");
    </squishit:minimize>[/quote]
    (with a more complex script of course :))

    Btw, congrats to your soccer team :D !

  7. I noticed in the web config you have a trust level of "full" – for most live deployments that’s not really an option. Do you have more granular solution?

  8. @stefan Interesting idea. I’ll look into it.

    @Paul We have a few issues with some of the libraries we are using not supporting medium trust. Once we get a few of those worked out we will support medium trust.

  9. I like @stephan’s idea.

  10. @Justin: I’m using YUI compressor with no problems fro UTF-8 scripts after defining the encoding in FileWriter.cs:
    public FileWriter(string file)
    {
    sw = new StreamWriter(file, false, System.Text.Encoding.UTF8);
    }

    and in YuiMinifier.cs:
    public string CompressContent(string content)
    {
    return JavaScriptCompressor.Compress(content, true, true, false, false, -1, Encoding.UTF8, CultureInfo.InvariantCulture, false);
    }

    (full patch is in your mailbox)

    greets,
    Stefan

  11. Have you tested this in a website with multiple worker processes? For example, using the web garden feature in IIS.

    My gut feeling is there needs to be an ICacheProvider so an out-of-process cache can be used.

    BTW-> The ease in which this can integrate into an existing site is amazing. Thanks!

  12. Justin – absolutely brilliant piece of code. Had it setup, tested and live in under 30 minutes. Works great and brought my request load down significantly. Extra points for handling cache invalidation!

  13. Great solution, thumbs up !

    For anyone having this error :

    [ERROR] identifier is a reserved word

    Make sure there is no debugger keyword in your javascript file.

    Good Job again.

    Thanks u sir

  14. Justin, it’s great and simple in use. Thanks u.
    I have one questions.
    What should I do if I use ‘conditional comments’ with css. For example:
    <!–[if IE 7]>
    <link href="css/ie7.css" rel="stylesheet" type="text/css" />
    <![endif]–>

    Thanks

  15. @yuri Just Make a second bundle for the ie7 stylesheet and put it in between your conditional comments.

  16. Why does LESS get rendered as: /css/main.les in debug mode?

  17. @Pickels Odd, not sure why. I’ll check into it.

  18. Okay, I apologise now for being a complete newb. I am a front end dev with no .NET knowledge so please bear with me :P

    I followed the instructions above but when I try to load the website (locally in debug mode) the "<%= Bundle.Css().." code causes the site to redirect to a custom error page. If I remove the "<%= Bundle.Css()…" code it loads the site fine.

    The Bundle code appears to have been hooked up correctly as I get the intellisense etc.

    There are no build errors and if I put a breakpoint at Session_Start in Global.asax.cs or at Page_Load in the master page, it doesn’t hit it.

    Any idea why I’m having problems? Or does it sound like it may be something custom at our end that’s causing it to fall over?

  19. Hm, I wrote a comment and it disappeared. That sucks. Anyways, if I can get this working I’ll be mega chuffed. It looks to be the simplest solution I’ve found so far.

    Oh and +1 for the @import support. Juicer does this and it is amazing! It also allows for /* depends */ comments in JS which would be a great feature for SquishIt as well. In case you want to check it out: http://cjohansen.no/en/javascript/juicer_a_css_and_javascript_packaging_tool

  20. @Jenna Hmm, without knowing the error it is hard to say. Could you paste in the exact code you are using to put SquishIt in the page? Or shoot me an e-mail through my contact form.

  21. Was just given a link to Squishit, I’ve previously used IncludeCombiner http://github.com/petemounce/includecombiner , has anyone does a comparison on both?

  22. Oopsie, I’m an idiot. I mistakenly added a semi colon after the Render(). Removing that fixed the issue…. doh :P

    Thank you for replying so quickly!

  23. i fond a problem in the Bundle.Css after everyting is done and a new file is created someting importent is missing the boundle changed background-image: url(‘../../Resources/paper-background.gif’); to background-image: url(‘/Resources/paper-background.gif’); as you can see the "../../" is gone and when linked to the page no images is found becuse it needs the "../../" any solution for this ?

  24. The CSS bundler rewrite paths relative to the output file. It appears that either there is a bug, or your original path was wrong.

    Can you put the hierarchy of the css files and images relative to the output css file?

  25. Im only using the script with one external css
    based on the user agent

    ~/stilmallar/paper/paperChrome.css
    ~/stilmallar/paper/paperSafari.css
    ~/stilmallar/paper/paperStandard.css

    the output file is named paperStandard-min.css

    The image in the orginal css
    img1 ../../Resources/paper-background.gif
    img2 ../../Resources/oscarclean-kontakt.png
    img3 ../../Resources/paper_02.gif
    etc

    the css is located in
    stilmallar/formular/FormStandard.css
    stilmallar/paper/paperStandard.css

    the images is located in Resources/images/some image

    the map stilmallar and Resources is in the root

    do you need any more specific info?

  26. Hmm, that looks like it is rewriting correctly then. The image urls in the css file as relative to the css file itself. So you have two css files that are two directory levels down and you are writing out a file in the root. If your image paths were ../../image.png when your css files were two directory levels down, then when you write the output css two directories up, those paths will change to just /image.png.

    As to why you aren’t seeing them in your html, I’m still not sure.

  27. I found SquishIt this week, and it’s been a huge help to me. I just have a question about the best way to use it. I currently have seven .aspx pages (expecting to add more in the future), each using a combination of javascript files. As you mention earlier, it would be best to just have them all combined into one file so no matter which page loads it will all be cached. That’s fine, but if I add a new javascript file to my project I don’t really want to have to remember to go add the reference to each .aspx page individually.

    I have a warm-up page that is triggered on each iisreset/app pool recycle/etc, so I was thinking it would be nice if I could just do the squish there and then just have the other pages reference the combined file. Is this advisable, and if so what would be the best way to accomplish that so if the javascript changes then the other pages will be assured to reference the correct combined file?

    Thank you.

  28. @robert You can do what is called a named bundle. Just put the code to create your bundle in your Global.asax, or in a warmup page as you said, and called "AsNamed("NameOfBundle", "Outputfile.whatever");

    Then later on, just called Bundle.Css().RenderNamed("NameOfBundle")

    Will that work for you?

  29. I see. I had misunderstood the usage of AsNamed and RenderNamed while reading the other comments. Thanks, that appears to be perfect.

  30. Hey Justin,
    Saw 0.6 was released. I noticed that in your commit notes you replaced Yahoo minifier with Microsoft’s. Do we still need those Yahoo dll’s for something?

  31. @Paul Can you post your question over at the SquishIt Google group. I’d like to get these types of questions out of the comments.

    The Google group can be found here: http://groups.google.com/group/squishit

  32. Just wanted to say thanks for the very useful library. We’ve put it into production on a few sites and the usefulness to time invested ratio is very high. Kudos!

  33. Hello, I’m having some issues putting the website into production with the library.

    i followed all the steps outline, and works fine locally, but when we try it live, it tells me the following error

    [quote]Description: An error occurred during the compilation of a resource required to service this request. Please review the following specific error details and modify your source code appropriately.

    Compiler Error Message: CS0246: The type or namespace name ‘SquishIt’ could not be found (are you missing a using directive or an assembly reference?)
    [/quote]

    I see that vs creates the .refresh file for the dll, and im sure we uploaded that into the server.

    I’m thinking of adding it into the assemblies section of the webconfig, im not sure if that would be correct, since if i do

    [quote]sn -T SquishIt.Framework.dll[/quote]

    it tells me

    [quote]SquishIt.Framework.dll does not represent a strongly named assembly[/quote]

    can i add it without a public key? or am I’m going in a wrong path here? please help :)

  34. @proz It looks like the file is not in the bin folder of the website. You are getting an error because it can’t find the dll.

    Please direct any additional questions to the Google group: http://groups.google.com/group/squishit

  35. biswanath chinta

    @Justin, suggestions, more like what do think of these ?

    As a best practice the app’s wont have write permission to the directory on the web server. To overcome this, can we store the bundled files in the memory ?

    I think the bundled files will be hardly more than 1MB. The other advantage will be serving the files from the memory might be a little faster ( may be very little ) than serving from the disk.

  36. @biswanth Well, first I don’t think it is necessarily a bad thing for the library to have write permissions into a css or js folder, but that isn’t really important.

    In the dev version I have done some work to enable storing the compressed files in memory. I am working on a project now where we are rendering the files from MVC actions. If you pull the dev version then you’ll see that you can create a bundle with the method "AsCached" and then you can later render that bundle out with "RenderCached" passing it the same name.

    The new version of SquishIt with this feature should hopefully be out in the next few weeks.

  37. Will this verision have a option to enable memory ore disk storing in my host they clear the memory every minute… and then this wont work good?

  38. @robert I can store in memory or on disk.

  39. Using SquishIt with Ext.NET

    This is not a problem with SquishIt but rather with Ext.NET, but since Ext.NET tend to be quite slow in replying, I thought I’d post this on here in the hope that someone has resolved this issue.

    I’m using Ext.NET 1.0, ASP.NET MVC 2.0 and .NET 4.0.

    When I try to run SquishIt with Ext.NET I get the following error message:

    "The Controls collection cannot be modified because the control contains code blocks (i.e. <% … %>)."

    From my testing, it is caused by:

    <ext:ResourceManager ID="SiteRM" runat="server"></ext:ResourceManager>
    and a fuller code snippet is:

    <%@ Master Language="C#" Inherits="System.Web.Mvc.ViewMasterPage" %>
    <%@ Import Namespace="SquishIt.Framework" %>
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"&gt;
    <html xmlns="http://www.w3.org/1999/xhtml"&gt;
    <head runat="server">
    <title>
    <asp:ContentPlaceHolder ID="TitleContent" runat="server" />
    </title>
    <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />

    <%: Bundle.Css()
    .Add("~/Content/style.css")
    .Add("~/Content/Theme/Original.css")
    .Render("~/Content/combined_#.css")
    %>

    <ext:ResourcePlaceHolder ID="GlobalResourcePlaceHolder" runat="server" />
    <asp:ContentPlaceHolder ID="HeadContent" runat="server" />
    </head>
    <body>
    <ext:ResourceManager ID="SiteRM" runat="server">
    </ext:ResourceManager>
    </body>

    Any help would be appreciated

    TIA

    David

  40. @David This is always the case with ASP.NET. If you have controls in your header then you won’t be able to write a string directly into the response. To test that, put SquishIt temporarily outside of your head tag. Check out this post http://www.west-wind.com/weblog/posts/5758.aspx on Rick Strahl’s blog for a few possible solutions.

    Also, please post additional questions in the SquishIt Google group: http://groups.google.com/group/squishit

  41. Hi Justin,

    Thanks for getting back to me. I’ll make sure to put any questions in the right group. Now, having said that, by commenting out

    <ext:ResourceManager ID="SiteRM" runat="server">
    </ext:ResourceManager>

    Squishit does work. :)

  42. @David Yep, because it is trying to manipulate your head tag. It is a common problem with ASP.NET that doesn’t really have a great workaround. You can try putting the SquishIt reference in a user control and then placing that user control in the head. That might work, I haven’t dealt with this issue in a long time. :-)

  43. Hi Justin,

    Thanks for a great component – just what I was looking for!

    I have a question though – how can I compress files from embedded resources within a DLL? I notice there’s an "AddEmbeddedResource" method, but it doesn’t appear to do anything. Please can you give me an example of the syntax for specifying the location of the embedded resource?

    Cheers.

  44. Hi Justin,

    First, thanks so much for putting this out. Amazing work.

    Quick question. Does SquishIt support conditional CSS? For example, if I only want a CSS file to be included if the browser is IE6. Thanks in advance for your reply.

  45. i want to know
    1> is file merging and compression is done for each unique visitor’s request or its done just for the first time(first request) and for later requests generated file is used.
    2> Is is possible to include a folder by using wild card like "*".
    3> Is there a tutorials where few more things are specified the just getting started thing.

  46. Suggestions

    1> if possible generate files on build up.
    2> suppose somewhere in our project we have this may in a partial.ascx(for mvc) or userControl.ascx(for webform)

    <some html>
    html
    </some html>

    <squish type="css" packageName="p1" order="1">
    some css
    </squish>

    <squish type="js" packageName="p1" order="1">
    some js
    </squish>

    –this way we can test individual components easily

    –in some some other file we do: same package different order
    <squish type="js" packageName="p1" order="2">
    some js
    </squish>

    On dedug = true css/js inside <squish> tags should be injected simply b/w "style" and "script" tags respectively
    on debug=false
    now may be on build or on runtime files are generated and may be referenced as below

    now some where else on the project may be on a view(for mvc) or on a page(for web form)
    we do like this, which includes all
    <%= Squish.Js().GetPackage("p1") %>// this looks inside whole project for scripts inside packageName=1, merge them according to order tag, minify them and then add a link for it
    <%= Squish.Css().GetPackage("p1") %>

  47. Thank you Justin, this is a great tool!

  48. for any of you want to integrate the squishit to auto-build process in command line, in order to let program to run successfully in the command line, you need to get the source code and change following line:
    BundleBase.cs:
    protected string
    ResolveAppRelativePathToFileSystem(string file)
    {
    from:
    return @"C:\" + file.Replace("/", "\\");

    to:
    return System.IO.Path.Combine( System.IO.Directory.GetCurrentDirectory() , file.Replace("/", "\\"));

    Regards

  49. 10/10 for your work on this Justin. Integrated beautifully in to my current project and the improvement is great.

    Fantastic Effort.

    Cheers,
    James

Leave a Reply

Your email address will not be published. Required fields are marked *