Adding minification and obfuscation of your Javascript and CSS to your ASP.net build process using Yahoo YUI Compressor


I’ve just finished integrating Yahoo’s YUI Compressor into the MSBuild process for a new web probject I’m working on, and I thought I would document the process as there doesn;t seem to be a start to finish guide about anywhere.

To start with, let me just explain the structure I use for new Solutions;

C:\_Dev\MyNewWebsite\
C:\_Dev\MyNewWebsite\Artwork\
C:\_Dev\MyNewWebsite\BuildTools\
C:\_Dev\MyNewWebsite\Docs\
C:\_Dev\MyNewWebsite\Source\
C:\_Dev\MyNewWebsite\Source\MyNewWebsite.sln
C:\_Dev\MyNewWebsite\Source\MyNewWebsite.Web\
C:\_Dev\MyNewWebsite\Source\MyNewWebsite.Web\Controls\
C:\_Dev\MyNewWebsite\Source\MyNewWebsite.Web\Images\
C:\_Dev\MyNewWebsite\Source\MyNewWebsite.Web\Scripts\
C:\_Dev\MyNewWebsite\Source\MyNewWebsite.Web\Services\
C:\_Dev\MyNewWebsite\Source\MyNewWebsite.Web\Styles\
C:\_Dev\MyNewWebsite\Source\MyNewWebsite.Web\MyNewWebsiteWeb.csproj
C:\_Dev\MyNewWebsite\Source\MyNewWebsite.Entities\
C:\_Dev\MyNewWebsite\Source\MyNewWebsite.Entities\MyNewWebsiteEntities.csproj
C:\_Dev\MyNewWebsite\Source\MyNewWebsite.Interfaces\
C:\_Dev\MyNewWebsite\Source\MyNewWebsite.Interfaces\MyNewWebsiteInterfaces.csproj
C:\_Dev\MyNewWebsite\ThirdParty\

Firstly, download the dot net port of the YUI compressor from codeplex. I then extracted this into a new folder in the BuildTools folder. (there should be 2 dll files and one txt licence file.)

I then created a new MSBuild file called build-minify.xml, in the MyNewWebsite.Web project folder. That file looks like this;

<?xml version="1.0" encoding="utf-8"?>
<!-- http://yuicompressor.codeplex.com/wikipage?title=Sample%20MSBuild.xml%20File&amp;amp;amp;amp;amp;ProjectName=yuicompressor -->
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <UsingTask
         TaskName="CompressorTask"
         AssemblyFile="..\..\BuildTools\Yahoo.Yui.Compressor-v1.5.0.0\Yahoo.Yui.Compressor.dll" />

  <ItemGroup>
    <CssFiles Include="$(SourceLocation)Styles\Fonts.css"/>
    <CssFiles Include="$(SourceLocation)Styles\Site.css"/>

    <CssIE Include="$(SourceLocation)Styles\IE.css"/>
    <CssIE6 Include="$(SourceLocation)Styles\IE6.css"/>

    <JsAll Include="$(SourceLocation)Scripts\jquery-1.6.1.min.js" />
    <JsAll Include="$(SourceLocation)Scripts\jquery-ui-1.8.14.custom.min.js" />
    <JsAll Include="$(SourceLocation)Scripts\json2.js" />
    <JsAll Include="$(SourceLocation)Scripts\jqModal-r14.js" />
    <JsAll Include="$(SourceLocation)Scripts\jquery.tipsy.js" />
    <JsAll Include="$(SourceLocation)Scripts\jquery.scrollTo-1.4.2.min.js" />
    <JsAll Include="$(SourceLocation)Scripts\jquery.localscroll-1.2.7.min.js" />
    <JsAll Include="$(SourceLocation)Scripts\chinook-alert.js" />
    <JsAll Include="$(SourceLocation)Scripts\chinook-validation.js" />
    <JsAll Include="$(SourceLocation)Scripts\chinook-rollovers.js" />
    <JsAll Include="$(SourceLocation)Scripts\chinook-custom-accordion.js" />
    <JsAll Include="$(SourceLocation)Scripts\chinook-vehicle-lookup.js" />
    <JsAll Include="$(SourceLocation)Scripts\chinook-sandbox.js" />
    <JsAll Include="$(SourceLocation)Scripts\chinook-finance-calculator.js" />
    <JsAll Include="$(SourceLocation)Scripts\chinook-apply-now.js" />
    
  </ItemGroup>

  <Target Name="Minimize">
    <CompressorTask
              CssFiles="@(CssFiles)"
              DeleteCssFiles="false"
              CssOutputFile="Styles/Site.rel.css"
              CssCompressionType="YuiStockCompression"
              DisableOptimizations="Nope"
              EncodingType="Default"
              LineBreakPosition="-1"
              LoggingType="ALittleBit"
              ThreadCulture="en-gb"
              IsEvalIgnored="false"
            />

    <CompressorTask
              CssFiles="@(CssIE)"
              DeleteCssFiles="false"
              CssOutputFile="Styles/IE.rel.css"
              CssCompressionType="YuiStockCompression"
              DisableOptimizations="Nope"
              EncodingType="Default"
              LineBreakPosition="-1"
              LoggingType="ALittleBit"
              ThreadCulture="en-gb"
              IsEvalIgnored="false"
            />

    <CompressorTask
              CssFiles="@(CssIE6)"
              DeleteCssFiles="false"
              CssOutputFile="Styles/IE6.rel.css"
              CssCompressionType="YuiStockCompression"
              DisableOptimizations="Nope"
              EncodingType="Default"
              LineBreakPosition="-1"
              LoggingType="ALittleBit"
              ThreadCulture="en-gb"
              IsEvalIgnored="false"
            />
    
    <CompressorTask
      JavaScriptFiles="@(JsAll)"
      ObfuscateJavaScript="true"
      PreserveAllSemicolons="true"
      DisableOptimizations="false"
      EncodingType="Default"
      DeleteJavaScriptFiles="false"
      LineBreakPosition="-1"
      JavaScriptOutputFile="Scripts/chinook.rel.js"
      LoggingType="ALittleBit"
      ThreadCulture="en-gb"
      IsEvalIgnored="false"
		/>
  </Target>
</Project>

You can create whatever ItemGroups you’d like- then just create a compressor task for each. For a rundown of the settings, check out the link in the comment at the top of the XML there. In the above file I had decided to compress all of my Javascript into a single file called chinook.rel.js, and my fonts and site css file rolled into one file, keeping the ie6 css separate, but still minifying it down.

The next step is to add it to the normal build. I opened the solution in Visual Studio, right clicked my web project and selected properties. Go to “Build Events” and add the following post-build event;

$(MSBuildBinPath)\msbuild.exe  $(ProjectDir)build-minify.xml /target:Minimize

Now you can test it out by building your project. The new minified files are dumped out, in my case to the Scripts folder. Now that the files are there, I need a way of telling my release version of the website to use these instead of the raw, human-readable versions; but I still want to continue using the human-readable versions for development.

To achieve this I use pre-processor directives. These are most commonly seen in the c# code behind files, but can also be used in the aspx files. I added the following lines at the bottom of my master page;

    <% #if (!DEBUG) %>
    <script src="Scripts/chinook.rel.js" type="text/javascript"></script>
    <% #else %>
    <script src="Scripts/jquery-1.6.1.min.js" type="text/javascript"></script>    
    <script src="Scripts/jqModal-r14.js" type="text/javascript"></script>
    <script src="Scripts/chinook-alert.js" type="text/javascript"></script>
    <script src="Scripts/jquery-ui-1.8.14.custom.min.js" type="text/javascript"></script>
    <script src="Scripts/chinook-vehicle-lookup.js" type="text/javascript"></script>
    <script src="Scripts/chinook-sandbox.js" type="text/javascript"></script>
    <script src="Scripts/chinook-finance-calculator.js" type="text/javascript"></script>
    <script src="Scripts/jquery.scrollTo-1.4.2.min.js" type="text/javascript"></script>
    <script src="Scripts/jquery.localscroll-1.2.7.min.js" type="text/javascript"></script>
    <script src="Scripts/jquery.tipsy.js" type="text/javascript"></script>
    <script src="Scripts/json2.js" type="text/javascript"></script>
    <script src="Scripts/chinook-validation.js" type="text/javascript"></script>
    <script src="Scripts/chinook-apply-now.js" type="text/javascript"></script>
    <script src="Scripts/chinook-rollovers.js" type="text/javascript"></script>
    <script type="text/javascript" src="Scripts/chinook-custom-accordion.js"></script>
    <% #endif %>

So if the project is NOT running in debug mode, I import my full chinook.rel.js include which has everything already included, otherwise I manually include all the human readable, separate files.

And that’s pretty much it. Make sure you thoroughly retest your site with your combined java script file, as sometimes minification can cause issues with some code.

  1. #1 by Mir on October 5, 2011 - 14:49

    Not working! I get this error:

    The “CompressorTask” task could not be loaded from the assembly D:\projects\…..\Web\bin\Yahoo.Yui.Compressor.dll. Confirm that the declaration is correct, that the assembly and all its dependencies are available, and that the task contains a public class that implements Microsoft.Build.Framework.ITask.

  2. #2 by shawson on October 10, 2011 - 13:55

    Hmm! It does definitely work as this is the exact build process I use myself, so it must be a configuration issue. Is the path definitely correct to your yahoo.yui.compressor.dll?

(will not be published)