NUglify Documentation

repository·master·Indexed 19 days ago

https://github.com/jbest84/nuglify

A .NET library and CLI tool for the minification and compression of CSS, JavaScript, and HTML. A fork of Microsoft Ajax Minifier, it provides features such as identifier renaming, dead code elimination, global optimization, and an HTML compressor for .NET applications without a Node.js environment.

Tokens
9K
Snippets
31
Records
39
Agent score
16%

What's inside NUglify

  1. Maximize minification gains using Namespaces

    master

    To get the smallest possible file size, avoid defining functions and variables in the global scope. Global names are never renamed by the minifier.

    Best Practice: The Namespace Pattern Instead of global functions, define a single global object to act as a namespace. Define your public API as methods/properties on this object, and keep all other logic as local functions or variables within that object's scope. Local items will be renamed to short identifiers (e.g., single characters), significantly reducing file size.

    Implementation Example:

    var MyScope = new function()
    {
        var my = this;
        var requestObject = null;
        my.Status = 0;
        my.Start = function(url)
        {
            if (my.Status == 0) { startRequest(url); }
            else { alert("request processing"); }
        };
        // ... other methods ...
    
        function startRequest(url) { /* local helper */ }
        function cancelRequest() { /* local helper */ }
    };

    In this pattern, MyScope.Start and MyScope.Status remain unchanged for external access, but requestObject, startRequest, and cancelRequest are all eligible for aggressive renaming.

    var MyScope = new function()
    {
        var my = this;
        var requestObject = null;
        my.Status = 0;
        my.Start = function(url)
        {
            if (my.Status == 0)
            {
                startRequest(url);
            }
            else
            {
                alert("request processing");
            }
        }
        my.Stop = function()
        {
            if (my.Status != 0)
            {
                cancelRequest();
            }
        }
    
    function startRequest(url)
        {
        // kick off the request
        my.Status = 1;
        }
    
    function cancelRequest()
        {
        // cancel a pending request
        my.Status = 0;
        }
    };
  2. How NUglify minifies JavaScript

    master

    NUglify performs minification by parsing source code into a JavaScript syntax parse tree (based on Microsoft ROTOR sources). It then manipulates the tree and walks it to output the minimum amount of code required to reproduce a comparable parse tree.

    By default, NUglify performs several levels of optimization:

    • Basic Minification: Removes comments, whitespace, and unnecessary semicolons or curly braces.
    • Identifier Renaming: Renames local variables and functions to shorter names to reduce file size.
    • Dead Code Elimination: Removes unused or unnecessary code and unreachable statements.
    • Global Optimization: Analyzes global variables and object properties to provide shortcuts (e.g., replacing repeated window access with a single-letter variable).
  3. Avoid Ambiguous Catch Identifiers in JavaScript

    master

    Using the same name for a catch parameter and a variable defined outside the try/catch block can cause cross-browser behavior differences, especially when variable renaming is enabled.

    In Internet Explorer, the catch parameter is treated as a variable defined within the containing function scope. In all other browsers, the catch parameter is only defined within the catch block itself.

    Example of problematic code:

    var e = "outer";
    
    function foo()
    {
        try
        {
            // do something that might error
        }
        catch (e)
        {
            // handle the error
        }
        alert(e);
    }

    In non-IE browsers, alert(e) will always show "outer". In IE, it will show the error object or undefined.

    NUglify will throw an error if it detects this ambiguity: Possible coding error: Ambiguous catch identifier 'e'. Cross-browser behavior difference.

  4. Avoid ambiguous JavaScript patterns for cross-browser compatibility

    master

    NUglify flags certain patterns that behave differently in Internet Explorer (IE) compared to modern browsers. To ensure consistent behavior, avoid the following:

    Ambiguous Try/Catch Variables

    In IE, the error variable in a catch block is defined in the containing scope, whereas other browsers scope it only to the catch block. This causes collisions if the error variable name matches a variable in the outer scope. Solution: Ensure the catch variable name does not collide with variables in the containing scope.

    Ambiguous Named Function Expressions

    In IE, the name of a named function expression is defined in the containing scope. This can cause recursion to fail if the function name collides with a variable in the outer scope. Solution: Avoid using function expression names that match variables in the outer scope.

    // BAD: Collision with outer scope 'e'
    try {
        a = foo;
    } catch (e) {
        // error handling
    }
    alert(e);
    
    // GOOD: Unique catch variable
    try {
        a = e / 0;
    } catch (err) {
        // error handling
    }
    alert(e);
  5. Understand CSS comment-based hacks

    master

    Developers often use specific comment patterns to target or hide rules from certain browsers. If the CssParser.CommentMode is set to CssComment.Hacks, NUglify will preserve these patterns:

    • Mac IE: /* (content) \*/ (ends with an escaped asterisk).
    • Netscape 4 / Opera 5: /* */*//*/ (specific sequence).
    • Netscape 4: /* */*/.
    • IE6 Property Hiding: property /* (content) */:value.
    • IE5.5 Property Hiding: property:/* (content) */value.
    • Empty Comments: /** or /* */ (assumed to be hacks).
  6. Understand the Error Output format

    master

    NUglify (via Microsoft Ajax Minifier) produces error and warning messages in a specific format compatible with MSBuild and Visual Studio. This allows build systems to parse errors and enables developers to double-click errors in IDEs to jump to the source location.

    Format: *origin*: [*subcategory*] *category* *code*: *text*

    • origin: Required. Indicates where the error occurred. For tool errors, it is ajaxmin.exe. For source file errors, it is path(line,columnstart-columnend).
    • subcategory: Optional. Often used to indicate severity.
    • category: Required. Either error or warning.
    • code: Required. A non-localized code (no spaces) starting with AM, JS, or CSS (e.g., JS1016, AM-IO).
    • text: Optional. A human-readable description of the error.
    // Examples of error strings:
    bar.js(2,5-10): run-time error JS1010: Expected identifier: while
    a.js(3,4-9): coding error JS1206: Did you intend to write an assignment here: a = 4
    a.js(2,5-13): code warning JS1137: 'abstract' is a new reserved word...
    foo.js(9,56-57): performance warning JS1135: Variable 'i' has not been declared: i
    ajaxmin.exe: error AM-USAGE: Invalid switch: -9
  7. JavaScript Minification: Scope Analysis and Unreachable Code

    master

    NUglify performs scope chain analysis to identify and remove unreachable code. This includes local functions that are never called, recursive functions that are never called, and chains of functions that are only called by other unreachable functions.

    Important Constraints:

    • Global Functions: These are never removed because they may be called by external modules.
    • Function Expressions: These are assumed to be referenced by their containing scope and are treated accordingly.
  8. How local variable and function renaming works

    master

    NUglify renames local functions and variables to shorter names to reduce size, while leaving global functions untouched.

    Renaming Logic:

    1. Scope Awareness: Function scope chains are respected; global and outer variable references are preserved.
    2. Naming Order: Within a local scope, names are generated using lower-case letters, then upper-case, then combinations.
    3. Frequency Optimization: Variables with the most references are renamed first to maximize byte savings.
    4. Unused Parameter Removal: If a function defines arguments that are never referenced, they are removed from the end of the argument list. Note that if an unused parameter is followed by a used one, it is kept to preserve the correct parameter order.
    // Original
    function DivideTwoNumbers(numerator, denominator, unusedparameter) {
        return numerator / denominator;
    }
    
    // Minified
    function a(a,b){return a/b}
  9. Understand JavaScript minification optimizations

    master

    NUglify applies several logic-based optimizations to reduce file size:

    • String Delimiters: Automatically chooses between single (') and double (") quotes based on which requires fewer escape characters.
    • Variable Declarations: Combines adjacent var statements into a single comma-delimited statement.
    • Constructor Calls: Removes empty parameter lists from new operators (e.g., new Image() becomes new Image).
    • Scope Flattening: Removes nested blocks that do not add semantic value to the function scope.
    • Logical Reductions:
      • Converts if (obj.method) { obj.method(); } into the shorthand obj.method&&obj.method().
      • Converts if (a <= b) {} else { alert("x") } into if(a<b)alert("x") (not-ing the condition).
    • For-loop Optimization: Moves var statements into the for loop initializer when possible.
  10. Use Conditional Compilation Comments in JavaScript

    master

    NUglify supports a subset of conditional compilation comments (@cc_on, @if, and @set). To ensure they are retained in the output, use them at the statement level.

    Supported Patterns:

    1. Statement-level comments:
    var ie = false;
    /*@cc_on
    ie = true;
    @*/
    1. Variable initializers (Special Case): You can specify the initializer of a variable within the comment. Only the equals-sign and the initializer must be inside the comment.
    var ie/*@cc_on = 1 @*/;
    1. Variable references within expressions: Comments inside expressions are only retained if they contain a single conditional-compilation variable reference. If they contain operators or multiple references, they are ignored.

    Example of retained reference:

    //@set @fourteen = 14
    var fourteen = /*@fourteen @*/;

    Example of ignored comment (contains operator):

    var isMSIE = /*@cc_on!@*/0;
  11. Use the NUglify CLI to minify files

    master

    You can run NUglify from the command line. The tool automatically detects whether to use JavaScript or CSS minification based on the file extension of the input file. You can also explicitly specify the mode using the --JS or --CSS switches.

    Basic Minification

    To minify a file and output the result to the standard output stream:

    ajaxmin inputfile.js

    Save to an Output File

    To save the minified code to a specific file, use the --OUT option:

    ajaxmin inputfile.js --out outputfile.js

    Overwrite Existing Files

    By default, NUglify will throw an error if the output file already exists. To force an overwrite, use the --CLOBBER switch:

    ajaxmin inputfile.js --out outputfile.js --clobber
  12. Best practices for JavaScript minification

    master

    To achieve maximum minification, follow these coding patterns:

    1. Use Namespaces: Wrap your code within a namespace. Expose only necessary functions and variables globally. Declare all helper functions and state variables as local to your namespace object.
    2. Avoid Constructor Calls: Use object and array literals instead of the new operator. NUglify automatically performs these substitutions unless the -NEW:KEEP parameter is specified.
    3. Shortcut Global Objects: Create local references for frequently used global objects like window and document to allow the minifier to rename them to single characters.
    4. Shortcut DOM Methods: Create local wrapper functions for frequently used DOM methods (e.g., document.getElementById) so they can be renamed to single characters.
    // Use literals instead of constructors
    var o = {}; 
    var a = [];
    
    // Shortcut global objects
    var w = window;
    var d = document;
    
    // Shortcut DOM methods
    function GetElById(id) {
        document.getElementById(id);
    }