danielwertheim

danielwertheim


notes from a passionate developer

Share


Sections


Tags


Disclaimer

This is a personal blog. The opinions expressed here represent my own and not those of my employer, nor current or previous. All content is published "as is", without warranty of any kind and I don't take any responsibility and can't be liable for any claims, damages or other liabilities that might be caused by the content.

Customizing the minification of JavaScript in ASP.Net MVC4, allowing reserved words

A friend of mine just posted a question on StackOverflow, regarding having reserved member names in code, which doesn’t play well with the minifier. I opened up object explorer and started to look over the API in WebGrease and the System.Web.Optimazation framework. Just took a few minutes and put together some prototype code that will replace the reserved member name in the code, from e.g “delete” to “fooDelete”. It just doesn’t change the member names, but it will also update the usages of them as well. Note! Really not sure why you would like to do this though. Reserved is usually reserved by a meaning. Also note. I didn’t put any love into the code. It should just work. It’s a StackOverflow question, right?

First lets define a custom bundle that we will use only for the code that we want to apply our changes to. You would use this class when registrering your bundles instead of ScriptBundle.

public class CustomBundle : ScriptBundle
{
    public CustomBundle(string virtualPath) 
        : base(virtualPath)
    {
        this.Builder = new CustomBuilder();
    }
    public CustomBundle(string virtualPath, string cdnPath) 
        : base(virtualPath, cdnPath) {
        this.Builder = new CustomBuilder();
    }
}

Now lets define a custom builder that does the work. The key is the usage of JSParser found in WebGrease.

public class CustomBuilder : IBundleBuilder {
    public string BuildBundleContent(
        Bundle bundle, BundleContext context, IEnumerable<FileInfo> files)
    {
        var content = new StringBuilder();
        foreach (var fileInfo in files)
        {
            var parser = new Microsoft.Ajax.Utilities.JSParser(Read(fileInfo));

            //The magic happens here
            parser.Settings.AddRenamePair("delete", "fooDelete");

            content.Append(parser.Parse(parser.Settings).ToCode());
            content.Append(";");
        }

        return content.ToString();
    }

    private string Read(FileInfo file)
    {
        using(var r = file.OpenText())
        {
            return r.ReadToEnd();
        }
    }
}

That’s it. And again. Not much love in this code. But I tested to minify with it, and it will take care of:

var myObj = {delete: function(){
    ... ... ...
}};

var myOtherObj = { test: function(){
    myObj.delete();
}};

and turn it to:

var myObj = {fooDelete: function(){
    ... ... ...
}};

var myOtherObj = { test: function(){
    myObj.fooDelete();
}};

Any use of the real delete will not be affected, which is exactly what we want. Also note, the result of the code above only kicks in when it’s minified. If you want it always, you’ll have to tweak it.

As an alternative solution you perhaps should use the minifier instead. That will look something like this:

public class CustomBuilder : IBundleBuilder
{
    public string BuildBundleContent(Bundle bundle, BundleContext context, IEnumerable<FileInfo> files)
    {
        var settings = new CodeSettings();
        settings.AddRenamePair("delete", "fooDelete");

        var content = new StringBuilder();
        foreach (var fileInfo in files)
        {
            var minifier = new Microsoft.Ajax.Utilities.Minifier();
            content.Append(minifier.MinifyJavaScript(Read(fileInfo), settings));
            content.Append(";");
        }

        return content.ToString();
    }

    private string Read(FileInfo file)
    {
        using (var r = file.OpenText())
        {
            return r.ReadToEnd();
        }
    }
}

I also have to say something negative about the bundler. When it failed, it failed in silence, just injecting a comment in the generated JS and then didn’t minify the JavaScript. Come on. That should fail with a big fat BOOOOOM!

//Daniel

View Comments