Search code examples
c#asp.netasp.net-optimization

Don't uglify certain file when using Microsoft Web Optimization Framework


I am trying to concat lots of .js files into a single one using Microsoft Web Optimization framework. Everything works, but within those files I have several that are already minified & uglified and there is not need to process them again.

For example I have recaptcha_ajax.js file and it causes following errors when it's appended:

/* Minification failed. Returning unminified contents.
(715,29-36): run-time error JS1019: Can't have 'break' outside of loop: break t
(714,293-300): run-time error JS1019: Can't have 'break' outside of loop: break t
(678,210-217): run-time error JS1019: Can't have 'break' outside of loop: break t
(671,1367-1374): run-time error JS1019: Can't have 'break' outside of loop: break t
(665,280-287): run-time error JS1019: Can't have 'break' outside of loop: break t
 */

I've tried to take take recaptcha_ajax.js out of bundle and reference it directly, but then other errors popup - so, I need that file within the bundle at certain position.

I just need to be able to say - do not minify & uglify recaptcha_ajax.js - just add it to the bundle.

Is there a way to do this? Here is how I see it:

var b = new ScriptBundle("~/bundles/myjsbundle");

b.IncludeDirectory("~/ScriptsMine/", "*.js", true);

// some command like:
// b.DoNotMinifyOrUglify("~/ScriptsMine/recaptcha_ajax.js");

bundles.Add(b);

Solution

  • Bundles transform each file by using a collection of IItemTransform and concatenate result. Then it transform the result by using a collection of IBundleTransform.

    The default script bundle minifies the complete bundle content by using JsMinify (which implements IBundleTransform).

    So to prevent some file from minifying, you have to create your own IBundleBuilder which minifies the bundle file by file by using an IItemTransform.

    public class CustomScriptBundle : Bundle
    {
        public CustomScriptBundle(string virtualPath)
            : this(virtualPath, null)
        {
        }
    
        public CustomScriptBundle(string virtualPath, string cdnPath)
            : base(virtualPath, cdnPath, null)
        {
            this.ConcatenationToken = ";" + Environment.NewLine;
            this.Builder = new CustomBundleBuilder();
        }
    }
    
    
    public class CustomBundleBuilder : IBundleBuilder
    {
        internal static string ConvertToAppRelativePath(string appPath, string fullName)
        {
            return (string.IsNullOrEmpty(appPath) || !fullName.StartsWith(appPath, StringComparison.OrdinalIgnoreCase) ? fullName : fullName.Replace(appPath, "~/")).Replace('\\', '/');
        }
    
        public string BuildBundleContent(Bundle bundle, BundleContext context, IEnumerable<BundleFile> files)
        {
            if (files == null)
                return string.Empty;
            if (context == null)
                throw new ArgumentNullException("context");
            if (bundle == null)
                throw new ArgumentNullException("bundle");
    
            StringBuilder stringBuilder = new StringBuilder();
            foreach (BundleFile bundleFile in files)
            {
                bundleFile.Transforms.Add(new CustomJsMinify());
                stringBuilder.Append(bundleFile.ApplyTransforms());
                stringBuilder.Append(bundle.ConcatenationToken);
            }
    
            return stringBuilder.ToString();
        }
    }
    
    public class CustomJsMinify : IItemTransform
    {
        public string Process(string includedVirtualPath, string input)
        {
            if (includedVirtualPath.EndsWith("min.js", StringComparison.OrdinalIgnoreCase))
            {
                return input;
            }
    
            Minifier minifier = new Minifier();
            var codeSettings = new CodeSettings();
            codeSettings.EvalTreatment = EvalTreatment.MakeImmediateSafe;
            codeSettings.PreserveImportantComments = false;
    
            string str = minifier.MinifyJavaScript(input, codeSettings);
    
            if (minifier.ErrorList.Count > 0)
                return "/* " + string.Concat(minifier.Errors) + " */";
    
            return str;
        }
    }
    

    Then use the CustomScriptBundle instead of ScriptBundle

    public static void RegisterBundles(BundleCollection bundles)
    {
        bundles.Add(new CustomScriptBundle("~/bundles/Sample").Include(
                    "~/Scripts/a.js",
                    "~/Scripts/b.js",
                    "~/Scripts/c.js"));
    }
    

    If you provide a min.js file it will be used instead of minifying it.