Bundling and minification using RequireJS.NET Compressor

Download

To install RequireJS.NET Compressor, run the following command in the Package Manager Console:
PM> Install-Package RequireJsNet.Compressor

Introduction

Script concatenation and minification is realized by processing the input (related to bundles) you provide through the config and establishing what files should and should not be in a bundle.

Using the classical compressor means that you also have to provide a manual override of all the paths included in bundles so that require.js can load the right version.

Using the autoCompressor, you only have to point it to your application's entry point(s) (easily done using a folder, generally ~/Scripts/Controllers/). It will detect most types of require and define calls by parsing your javascript and generating a tree to determine what the right scripts are for that bundle. In some cases, static analysis might not be enough to determine what module you're trying to load (for example, doing something like require(['jquery-' + lang])), so you can control what scripts go in your bundle using the exclude/include properties of the autoBundle.

The auto compressor will also generate proper require modules out of your shimmed script, whereas with the classic compressor you had to do that manually.

In order to know what files to load at runtime, the autoCompressor will automatically generate another configuration file that will be able to tell us at runtime what bundle should be loaded when. To find them, assuming your configuration file is named RequireJS.json, after a build using the autoCompressor you should find a file named RequireJS.override.json. You can also look at this file to debug possible dependency tree generation issues.

You have to keep in mind that if you've set the LoadOverrides property to true when rendering the require config, if an overload file is present, it will be loaded. If you're trying out the autoCompressor locally, you might want to set that property to false when in debug configuration, for example.

Auto Compressor Bundling configuration

Having a bundle for each MVC area in your project is probably the best approach if your JavaScript code has different dependencies per area. For example, if you are developing a CMS site, the Admin area certainly uses more JS components (WYSIWYG editor, file upload, etc) than the Public area, so it's logical to include in the Public bundle only the JS components referenced by the code-behind code of the pages that compose the Public area.

Bundling per area example:

{
    "paths": {
        "jquery": "jquery-1.10.2",
        "jquery-validate": "jquery.validate",
        "jquery-validate-unobtrusive": "jquery.validate.unobtrusive",
        "bootstrap": "bootstrap",
        "file-upload": "jquery.fileupload",
        "html-editor": "tinymce"
    },
    "shim": {
        "jquery-validate": {
            "deps": [ "jquery" ]
        },
        "jquery-validate-unobtrusive": {
            "deps": [ "jquery", "jquery-validate" ]
        },
        "bootstrap": {
            "deps": [ "jquery" ]
        },
        "file-upload": {
            "deps": [ "jquery" ]
        },
        "html-editor": { 
            "deps":  ["jquery"]
        }
    },
    "autoBundles": {
        "public-app": {
            "outputPath": "Scripts/Bundles/",
            "include": [
                {
                    "directory": "Controllers/Root"
                }
            ]
        },
        "admin-app": {
            "outputPath": "Scripts/Bundles/",
            "include": [
                {
                    "directory": "Controllers/Admin"
                }
            ]
        }
    }
}

The above configuration will generate two files in ~Scripts/Bundles, public-app.js and admin-app.js, if the modules from ~Scripts/Controllers/Root does not require the file-upload or html-editor then these components will be bundled only in the admin-app.js file.

Run compressor from Visual Studio at build

By default when you install the Compressor package any RequireJS.json files with be assigned to RequireJsNetConfig build action and will trigger the compressor task at build. After each build it will create a folder named bundle inside Scripts where all your defined bundles will be compiled.

If you have multiple config files that you want to compile then you need to set for each one the build action to RequireJsNetConfig like this:

Run compressor from MsBuild on a build server

For the bundling to take place at build, create a folder named .build in your ASP.NET project root and add a file named Compressor.targets with the following content:

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <UsingTask TaskName="RequireCompressorTask" AssemblyFile="...\packages\RequireJsNet.Compressor.2.2.3\tools\RequireJsNet.Compressor.dll" />
  <ItemGroup>
    <Configs Include="$(_PackageTempDir)\RequireJS.json" />
    <Content Include="RequireJS.json">
      <Visible>false</Visible>
    </Content>
  </ItemGroup>
  <Target Name="RequireJsNetCompressor" AfterTargets="CopyAllFilesToSingleFolderForMsdeploy">
    <RequireCompressorTask 
      LoggingType="Debug" 
      RequireConfigs="@(Configs)" 
      AutoCompressor="true" 
      EncodingType="UTF8" 
      ProjectPath="$(_PackageTempDir)" 
      PackagePath="$(_PackageTempDir)\Scripts\" />
  </Target>
</Project>

The RequireCompressorTask can receive the following paramters:

LoggingType - to be passed to the YUI compressor, can be none, info or debug

CompressionType - to be passed to the YUI compressor, can be none or standard

EncodingType - to be passed to the YUI compressor, text encoding for the minifier, can have one of the following values: ascii, bigendianunicode, unicode, utf32, utf-32, utf7, utf-7, utf8, utf-8, default

AutoCompressor - whether to run the autoCompressor or the classic one

PackagePath - the output directory for packages, it will default to the value of ProjectPath if not specified.

ProjectPath - the directory from which configs/scripts are read

RequireConfigs - the list of require configuration files. Defaults to ~/RequireJS.json

EntryPointOverride - allows you to override the entrypoint used to load scripts, default is ProjectPath + \Scripts\

Add the following line at the end of your .csproj to import the Compressor targets:

<Import Project=".build\Compressor.targets" />

On your build server you will want to avoid running the `RequireJsNetConfig` build action for each file if you will use the above code, in order to disable the build action set the following MsBuild parameter: /P:RequireJsNetCompressorRun=False

When you build or deploy the MVC project, three files will be generated: the two bundles and RequireJS.override.json. Because these files will be generated on each build, there is no need to keep them on the source repository, in order to exclude them from git you can add the following lines to .gitignore file.

# Exclude RequireJS.NET Compressor build files
*.override.config
*.override.json
*/Scripts/Bundles/
Load bundles

In order to load the bundles made by the Compressor you'll have to set the RequireRendererConfiguration.LoadOverrides property as true, normally you'll want this to happen on the production environment, for this you can use the HttpContext.Current.IsDebuggingEnabled flag like this:

@Html.RenderRequireJsSetup(new RequireRendererConfiguration
{
    RequireJsUrl = Url.Content("~/Scripts/Components/RequireJS/require.js"),
    BaseUrl = Url.Content("~/Scripts/"),
    ConfigurationFiles = new[] { "~/RequireJS.json" },
    EntryPointRoot = "~/Scripts/",
	
    // whether we should load overrides or not, used for autoBundles
    LoadOverrides = !HttpContext.Current.IsDebuggingEnabled
})
More info

For more information on RequireJS.NET Compressor features please read the wiki and try out the demo projects. If you have suggestion or any kind of feedback regarding RequireJS.NET project please submit an issue here.