Minify JavaScript and CSS files when publishing Web Applications from Visual Studio

One of the best practices to speed up a web site or web application is to minify JavaScript and CSS files; which as a secondary effect, not only reduces the size of those files but also makes them harder to read (something helpful to prevent the stealing of your ideas and efforts).

Intro

You may think that actually this is not very helpful since using external JavaScript and CSS files in the real world generally produces faster pages because they will be cached by the browser. However, if their size is big, you may improve the first request load time by reducing their size.

Minification is the practice of removing unnecessary characters from code to reduce its size thereby improving load times. When code is minified all comments are removed, as well as unneeded white space characters (like newlines or tabs). This improves response time performance because the size of the downloaded file is reduced.

There are many tools to minify JavaScript and CSS files, like for example:

I’m a Microsoft .NET professional, so in this and future post, I will going to focus only on Microsoft’s Ajax Minifier tool.

The proper time to minify

It is not advisable to minify your web application’s JavaScript and CSS files during developing time; it should be done on deployment or publish (for testing or any other purpose) time.

But then, how to minify those files without affecting the original source code in the solution when deploying it?

The answer is quite easy: we must use MSBuild in the web application project file to configure the Microsoft Ajax Minifier to be executed as a task when publishing in ‘Release’ mode.

The steps to minify

The first step is to unload the web application project by right-clicking on it and choose the option ‘Unload Project’. Once unloaded, right-click it again and choose the ‘Edit <web_app_project_name>.csproj’ option, which will open the XML editor of the Visual Studio with the project’s configuration.

What I usually do, is to go to the end of the file and then add the following line just before the closing Project tag:

<UsingTask TaskName="AjaxMin" AssemblyFile="$(SolutionDir)Lib\AjaxMinTask.dll" />
<PropertyGroup>
    <ResGenDependsOn>
        CompressJsAndCss;
        $(ResGenDependsOn)
    </ResGenDependsOn>
</PropertyGroup>

Please note the PropertyGroup tag. It describes the property activity to perform and its dependency with the generation of resources. This is very important to specify.

I prefer the UsingTask tag instead of the Import tag because I work with a lot of people who may have installed the Microsoft Ajax Minifier in different versions or locations that me (or not having it installed at all). So we distribute the solution’s dlls inside a Lib subfolder, that is also under source control. With this mechanism, every body uses the same dlls in the same version and referenced from the same location.

It is very important to take into account that you will need to have the AjaxMinTask.dll and also the AjaxMin.dll in the same location.

The next step is to create a custom target with the following lines:


<Target Name="CompressJsAndCss" AfterTargets="CopyAllFilesToSingleFolderForPackage" Condition="'$(Configuration)'=='Release'">
...
</Target>

The new target is called CompressJsAndCss and will be executed when publishing a web application just after the CopyAllFilesToSingleFolderForPackage target, which is responsible of copying (or materialize in-memory files) the project files into your obj folder (specifically the folder specified by _PackageTempDir) in preparation for a publish.

Next, we need to gather the files suitable to be minified, in this case, JavaScript and CSS files. To do so, just add the following lines to the custom target:


<ItemGroup>
      <JS Include="$(_PackageTempDir)\**\*.js" Exclude="$(_PackageTempDir)\**\*.min.js" />
      <CSS Include="$(_PackageTempDir)\**\*.css" Exclude="$(_PackageTempDir)\**\*.min.css" />
</ItemGroup>

As you can see, we’re creating a set of all available JavaScript and CSS files in the _PackageTempDir folder (where the CopyAllFilesToSingleFolderForPackage put the files for the publication) but excluding those with extension .min.js or .min.css which are already minified (usually jQuery files).

Finally, we must call the Microsoft Ajax Minifier task in order to minify the gathered files with the following lines:


 <AjaxMin JsKnownGlobalNames="jQuery,$" JsSourceFiles="@(JS)" JsSourceExtensionPattern="\.js$" JsTargetExtension=".js"
          CssSourceFiles="@(CSS)" CssSourceExtensionPattern="\.css$" CssTargetExtension=".css"/>

Note that we’re informing the Microsoft Ajax Minifier to respect the ‘jQuery’ and ‘$’ literals on the JavaScript files since they are well know global names that must not be modified in the minify process.

At the end, we should have the following added configuration:


<!-- Minify all JavaScript and CSS files that are present on this web application when publish in release. -->
  <UsingTask TaskName="AjaxMin" AssemblyFile="$(SolutionDir)Lib\AjaxMinTask.dll" />

  <!-- This target will run after the files are copied to PackageTmp folder (usually 'obj\Release\Package\PackageTmp') -->
  <Target Name="CompressJsAndCss" AfterTargets="CopyAllFilesToSingleFolderForPackage" Condition="'$(Configuration)'=='Release'">
    <ItemGroup>
      <JS Include="$(_PackageTempDir)\**\*.js" Exclude="$(_PackageTempDir)\**\*.min.js" />
      <CSS Include="$(_PackageTempDir)\**\*.css" Exclude="$(_PackageTempDir)\**\*.min.css" />
    </ItemGroup>
    <Message Text="Compressing JavaScript and CSS files..." Importance="high" />
    <AjaxMin JsKnownGlobalNames="jQuery,$" JsSourceFiles="@(JS)" JsSourceExtensionPattern="\.js$" JsTargetExtension=".js"
             CssSourceFiles="@(CSS)" CssSourceExtensionPattern="\.css$" CssTargetExtension=".css"/>
  </Target>

So what is left to do is to save the csproj file, close it and right-click on it from the Solution Explorer and choose the ‘Reload Project’ option in order to load it again in the solution.

Now when we go to publish the web application (and in ‘Release’ mode) our custom target will be evaluated just after the CopyAllFilesToSingleFolderForPackage target, and all JavaScript and CSS files will be minified without affecting the original ones.

References

Anuncios
Entrada anterior
Deja un comentario

11 comentarios

  1. Pedro

     /  mayo 23, 2012

    Gracias, Rodrigo. Es exactamente lo que andaba buscando. La clave para mí fue:

    AfterTargets=”CopyAllFilesToSingleFolderForPackage”

    y

    $(_PackageTempDir)

    Responder
    • Hola Pedro,

      Me alegra que te haya sido de utilidad. Estoy trabajando en otra publicación (que espero pronto tener lista) sobre como “minificar” JavaScript y CSS en librerías de controles para proyectos web. Es decir, para escenarios diferentes a despliegues sino más bien asociados a la creación de frameworks.

      Responder
      • Pedro

         /  mayo 23, 2012

        Hola, Rodrigo:

        La verdad es que es algo costoso encontrar documentación de calidad al respecto en Internet. ¡Sobre todo para alguien como yo que lleva 24h trabajando con MSBuild!

        Respecto al nuevo tutorial que mencionas, nosotros solucionamos la “minificación” de .css y .js embebidos en bibliotecas de controles de la siguiente manera:


        MinifyJavaScriptAndCSS;
        $(ResGenDependsOn)


        Minified: @(EmbeddedJavaScriptResource)” Importance=”high”/>
        Minified: @(EmbeddedCssResource)” Importance=”high”/>


  2. Pedro

     /  mayo 23, 2012


    MinifyJavaScriptAndCSS;
    $(ResGenDependsOn)


    Minified: @(EmbeddedJavaScriptResource)” Importance=”high”/>
    Minified: @(EmbeddedCssResource)” Importance=”high”/>


    Responder
    • Pedro

       /  mayo 23, 2012

      Lo siento, no puedo pegar el código. Queda truncado 😦

      Responder
      • No te preocupes Pedro.

        Lo que voy a hacer es enviarte un mail contándote como lo he implementado para que compares con tu código y puedas decidir con calma cual te conviene mejor. Te lo voy a enviar a la cuenta con la que haz hecho los comentarios.

        Un saludo,

        R.

  3. Pedro

     /  mayo 25, 2012

    ¡Eres un crack! Gracias.

    Responder
  4. Hi Rodrigo, do you have any idea how to do this for a VS publishing profile instead of a .csproj file? The syntax is the same, but I just can’t make it work. It’s probably because I should set the “CompressJsAndCss” target to run after some other target…

    Responder
  1. Luuuukke.NET | Minify css and jss files automatically with Visual Studio Web Application Projects
  2. Visual Studio 2012 Run AjaxMin in release mode for web deploy package error | Zielke Answers

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s