Thursday, June 30, 2011

YUI Compessor .Net - My MSBuild

For integrating JavaScript or CSS compression in to your .net projects YUI Compressor for .Net is a great find. It is both easy to integrate and simple to start the build process.

To integrate I simply unzipped the download (MSBuild folder containing EcmaScript.NET.modified.dll, MSBuild.xml, New BSD License.txt, and Yahoo.Yui.Compressor.dll) into a solution folder in my project.

One thing I didn't like about the default MSBuild.xml was that it assumes you want all javascript or css files to end up in a single, respective output.  For instance, minifying/compressing one or more scripts into a single script for deployment.  However, in my case I need each script to be seperated.  If you work with Microsoft Dynamics CRM you know what I mean... a form script for each customized entity. What I end up doing is 

  1. Add my script to my project (in source control).  For example: ( myscript.js)
  2. Add a second script in the same folder.  For example: (myscript.min.js)
  3. Selecting the second script in visual studio, go to File, Source Control, Exclude from source control.
What this does is lets my second script appear in my project (it is now included in the vsproj file list) but keep a second copy out of source safe.  Then we can take a look at my modified MSBuild.xml (original msbuild.xml).  What I did here was just use the simple properties of MSBuild.xml to allow me to build a single input to a single output file from the command line, and make it re-usable so I don't have to create another MSBuild.xml for every one of these!


<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/MsBuild/2003">
  <UsingTask
     TaskName="CompressorTask"
     AssemblyFile="..\MSBuild\Yahoo.Yui.Compressor.dll" />

  <PropertyGroup>
    <LineBreakPosition Condition=" '$(LineBreakPosition)'=='' ">-1</LineBreakPosition>
  </PropertyGroup>

  <Target Name="MyTaskTarget">   
    <ItemGroup>   
      <JavaScriptFiles Include="$(JavaScriptInputFile)"/>
    </ItemGroup>
    <CompressorTask
       CssFiles="@(CssFiles)"
       DeleteCssFiles="false"
       CssOutputFile="$(CssOutputFile)"
       CssCompressionType="YuiStockCompression"
       JavaScriptFiles="@(JavaScriptFiles)"
       ObfuscateJavaScript="True"
       PreserveAllSemicolons="False"
       DisableOptimizations="Nope"
       EncodingType="Default"
       DeleteJavaScriptFiles="false"
       LineBreakPosition="$(LineBreakPosition)"
       JavaScriptOutputFile="$(JavaScriptOutputFile)"
       LoggingType="ALittleBit"
       ThreadCulture="en-us"
       IsEvalIgnored="false"
           />
  </Target>
</Project>

Changes to the original:

  1. Commented out JavaScriptOutputFile in //PropertyGroup
  2. Added LineBreakPosition in //PropertyGroup with my default value (-1 is off)
  3. //ItemGroup/JavaScriptFiles only includes the property $(JavaScriptInputFile)
  4. In //CompressorTask, LineBreakPosition now references the property $(LineBreakPosition) instead of the static value -1.
  5. Note that comments have been removed to keep the post sane, see the original above.
In my project pre-build I then build single scripts like so:   (in this example MSBuild.xml is in the MSBuild folder of the project I'm building)


$(MSBuildBinPath)\msbuild.exe "$(ProjectDir)MSBuild\MSBuild.xml"
     /p:JavaScriptInputFile=$(ProjectDir)_script\Foobar\foobar1_library.js
     /p:JavaScriptOutputFile=$(ProjectDir)_script\FooBar\foobar1_library.min.js

$(MSBuildBinPath)\msbuild.exe "$(ProjectDir)MSBuild\MSBuild.xml"
     /p:JavaScriptInputFile=$(ProjectDir)_script\foobar_ui.js
     /p:JavaScriptOutputFile=$(ProjectDir)_script\foobar_ui.min.js

In this example MSBuild.xml is in a folder (MSBuild) at the same level as each of my projects.  I keep line breaks as I'm trying to find a bug in the script.  Additionally, the script is being copied to two other files in other projects.  Because this wasn't an automated process before, each of these scripts was in source control.  I've removed the files from source control so we only have one copy in there, but.. the files will be read-only on other developers machines until they do a clean get from source control.  Thus, I remove the read-only attribute so I don't have problems updating those copies.


$(MSBuildBinPath)\msbuild.exe "$(ProjectDir)..\MSBuild\MSBuild.xml"
     /p:JavaScriptInputFile=$(ProjectDir)my_big_library.js
     /p:JavaScriptOutputFile=$(ProjectDir)EmbeddedResources\my_big_library.min.js
     /p:LineBreakPosition=0

attrib -R $(ProjectDir)..\OtherProject1\_static\my_big_library.min.js

copy $(ProjectDir)EmbeddedResources\my_big_library.min.js
     $(ProjectDir)..\OtherProject1\_static\my_big_library.min.js

attrib -R $(ProjectDir)..\OtherProject2\Scripts\my_big_library.min.js

copy $(ProjectDir)EmbeddedResources\my_big_library.min.js
     $(ProjectDir)..\OtherProject2\Scripts\my_big_library.min.js

No comments: