Friday, September 17, 2010

Include Javascript and CSS Compression in Visual Studio

For a frontend developer it is a must to compress his javascript and css files, before deploying a website to a productive environment. In this post I'll demonstrate how you can automate this process in Visual Studio.

First of all download the YUI Compressor for .Net. (3.5)

Extract the zip file and copy the to files into a folder in your visual studio solution. I have a folder called "Libraries" for third part dll files.

YUI Dlls


Create a file "MSBuildCompress.xml" in a folder you want. I put this file in my "_Tools" folder.

Custom Compress Msbuild Task


Put the following xml in your newly created xml file.

<?xml version="1.0" encoding="utf-8"?>
<!-- Author Baris Bikmaz -->
<!-- MSBUILD Task to compress css and js files with the yui compressor -->
<Project xmlns="http://schemas.microsoft.com/developer/MsBuild/2003">
    <UsingTask
        TaskName="CompressorTask"
        AssemblyFile="../Libraries/Yui/Yahoo.Yui.Compressor.dll" />


    <PropertyGroup>
        <CssOutputFile Condition=" '$(CssOutputFile)'=='' ">$(OutputFolder)\css\main.css</CssOutputFile>
<CssDebugFile>$(OutputFolder)\css\main.debug.css</CssDebugFile>
        <JavaScriptOutputFile Condition=" '$(JavaScriptOutputFile)'=='' ">$(OutputFolder)\js\main.js</JavaScriptOutputFile>
    </PropertyGroup>
    
    <Target Name="CompressAndMergeFiles">
        <ItemGroup>
            <JavaScriptFiles Include="$(OutputFolder)\js\*.*" Exclude="$(OutputFolder)\js\main.*js"/>
            <CssFiles Include="$(OutputFolder)\css\*.*" Exclude="$(OutputFolder)\css\main.*css" />
        </ItemGroup>

<Message Text="========== Compressing Css and Js Files in $(OutputFolder) ==========" Importance="high" />

        <CompressorTask
            CssFiles="@(CssFiles)"
            DeleteCssFiles="false"
            CssOutputFile="$(CssOutputFile)"
            CssCompressionType="YuiStockCompression"
            JavaScriptFiles="@(JavaScriptFiles)"
            ObfuscateJavaScript="True"
            PreserveAllSemicolons="False"
            DisableOptimizations="Nope"
            EncodingType="Default"
            DeleteJavaScriptFiles="false"
            LineBreakPosition="-1"
            JavaScriptOutputFile="$(JavaScriptOutputFile)"
            LoggingType="ALittleBit"
            ThreadCulture="en-au"
            IsEvalIgnored="false"
            />

<Message Text="========== Creating debug file ==========" Importance="high" />
<ReadLinesFromFile File="%(CssFiles.Identity)">
<Output TaskParameter="Lines" ItemName="lines"/>
</ReadLinesFromFile>
<WriteLinesToFile File="$(CssDebugFile)" Lines="@(Lines)" Overwrite="true" />
    </Target>
</Project>

1. Check here that the path to the yui library is correct:


<UsingTask TaskName="CompressorTask"
 AssemblyFile="../Libraries/Yui/Yahoo.Yui.Compressor.dll" />

2. The propertygroup defines the files and their location which are generated by the compressor


<PropertyGroup>
 <CssOutputFile Condition=" '$(CssOutputFile)'=='' ">$(OutputFolder)\css\main.css</CssOutputFile>
 <CssDebugFile>$(OutputFolder)\css\main.debug.css</CssDebugFile>
 <JavaScriptOutputFile Condition=" '$(JavaScriptOutputFile)'=='' ">$(OutputFolder)\js\main.js</JavaScriptOutputFile>
</PropertyGroup>


3. The ItemGroup defines the files which should be compressed by the YUI compressor. I exclude all the files which will be generated because all css files, compressed and uncompressed are in the same folder. You can change this if you want.


<ItemGroup>
  <JavaScriptFiles Include="$(OutputFolder)\js\*.*" Exclude="$(OutputFolder)\js\main.*"/>
  <CssFiles Include="$(OutputFolder)\css\*.*" Exclude="$(OutputFolder)\css\main.*" />
</ItemGroup>

4. The CompressorTask does the compression. The files to compress and the the output files are given ase arguments.

<CompressorTask
            CssFiles="@(CssFiles)"
            DeleteCssFiles="false"
            CssOutputFile="$(CssOutputFile)"
            CssCompressionType="YuiStockCompression"
            JavaScriptFiles="@(JavaScriptFiles)"
            ObfuscateJavaScript="True"
            PreserveAllSemicolons="False"
            DisableOptimizations="Nope"
            EncodingType="Default"
            DeleteJavaScriptFiles="false"
            LineBreakPosition="-1"
            JavaScriptOutputFile="$(JavaScriptOutputFile)"
            LoggingType="ALittleBit"
            ThreadCulture="en-au"
            IsEvalIgnored="false"
            />

5. The last step merges the css files and creates a main.debug.css which is not compressed. This is useful in debug enviroments, where the frontend developer want to examine the css with firebug to find the location of a style.

<ReadLinesFromFile File="%(CssFiles.Identity)">
<Output TaskParameter="Lines" ItemName="lines"/>
</ReadLinesFromFile>
<WriteLinesToFile File="$(CssDebugFile)" Lines="@(Lines)" Overwrite="true" />


6.) Now put some css and js in to your css and js folder. When you look at the ItemGroups XML Node you will see that my css and js are under the same folder, like in the image below.


7. Now test the configuration. Open a dos console and type the command:


"c:\WINDOWS\Microsoft.NET\Framework\v3.5\msbuild.exe" "C:\MyProject\_Tools\MSBuildCompress.xml" /p:OutputFolder="C:\MyProject\MasterTemplate\12\Template\Layouts\MyProject"

The parameter "OutputFolder" is used in the .xml file to define the folder where the css and js folders are. I have following structure in my project. If you have another stucture you have to adapt the xml file.
If the command is successfull, you can copy it. The next step is to put this command into visual studio.

Open the properties of the project that contains the js and css files. Click on build Events and put the following text into the text field (Pre-Build Event)

Pre-build event

No comments: