MiniME - Code Quality (Lint)

MiniME checks your code, but won't hurt your feelings.

In additional to minifying and obfuscating your scripts, MiniME can check for

common JavaScript programming errors and suspicious looking code.

Enabling and Disabling Code Warnings

Rather than a complicated set of options to control what's checked, MiniME's code

checking is either on or off. Code checking is on by default, but can be disabled

with the command line option -no-warnings. The -no-warnings switch will disable

code checking for all subsequent files on the command line. Similarly,

the -warnings switch can be used to re-enable code checking for other files. eg:

mm -no-warnings DontCheckMe.js -warnings CheckMe.js

You can also control warnings within your script files with the // warnings: directive.

// warnings:on

Turn warnings on

// warnings:off

Turn warnings off

// warnings:push:on

Save the current warning state before turning warnings on

// warnings:push:off

Save the current warning state before turning warnings off

// warnings:pop

Restore the last saved warning state

So for example:


// warnings:push:off

eval("performEvil()")

// warnings:pop

What does MiniME Check For?

MiniME aims to provide a reasonable set of warnings for common, realistic programming

errors. It tries to be helpful, doesn't suffer from OCD and won't impose weird

coding conventions upon you.

The following explains all the code quality warnings that MiniME can generate.

Unreachable Code

Unreachable code is any code that follows a return, throw, break or continue

statement. For example:


function MyFunction()

{

    return;

    alert("Can't get here");    // Unreachable code

}

Use of debugger Statement

The JavaScript debugger statement shouldn't be used in production code, so MiniME

let's you know if you've accidentally left one in.

Expression Has No Side Effects

Code that has no side effects is redundant and can often indicate an error. For

example:


$this.bind('keydown',function (e) {

    e.preventDefault;       // This is valid, but wrong - should be e.preventDefault()

    return false;

});

Execution Falls Through To Next Case

A common programming error is forgetting to put a break statement between each

case clause in a switch statement. MiniME will warn of this:


switch (x)

{

    case 1:

        alert("I'm falling");

                                // Shouldn't there be a break or return here?



    case 2:

        alert("I'll catch you");

        break;

}

If you intend for execution to fall through, you can suppress this warning with a comment

that says "fall through". eg:


switch (x)

{

    case 1:

        alert("I'm falling");



        // fall through



    case 2:

        alert("I'll catch you");

        break;

}

Trailing Comma in Array or Object Literal

Not all browsers support trailing commas in object and array literals. MiniME warns about this:


var x={"Apples":"Red", "Bananas":"Yellow", };   // Warning

var y=["Apples", "Pears", ];                    // Warning

Missing elements within array literals are allowed:


var z=[1,2,,,,4];       // This is OK

Use of Composite Expression

Composite expressions are expressions involving the comma operator. These are generally confusing

and usually not required so MiniME lets you know:


// This is confusing:

prevOffsetParent = offsetParent, offsetParent = elem.offsetParent;

Since composite expressions are useful in for loops, MiniME doesn't warn in that case.


// This is OK

for (x=0, y=0; x<10; x++, y++)

{

}

Unnecessary Semicolon

JavaScript will tolerate extra semicolons but these can often be a sign of some other problem.

MiniME generates a warning for cases like this:


while (SomeCondition());        // Extra semicolon

If you really want an empty loop, do this instead:


while (SomeCondition()) {}

Missing Semicolon

JavaScript will tolerate missing semicolons but MiniME will warn you:


var x="Hello"           // Warning

var y="World";          // No warning

Assignment as Condition of Flow Control Statement

MiniME warns of assignments in the condition part of a flow control statement. eg:


if (x=y)        // Really? Shouldn't this be x==y

{

    DoSomething();

}

To disable this assignment, use an extra set of parentheses (which MiniME will remove during

minification anyway):


if ((x=y))      // Oh! You really mean assignment.  OK.

{

    DoSomething();

}

Symbol Has Multiple Declarations

JavaScript tolerates a variable or function being declared multiple times, but this can easily

lead to a single variable where you intended two. MiniME warns about this.


function DoSomething()

{

    var i=23;



    // and then later in the same function



    var i=42;       // Symbol declared multiple times

}

Use of new Array() with One Argument Creates a Sized Array

Calling new Array() with a single argument like this:


var x=new Array(10);

creates a sized array - in this case an array of 10 elements.

Calling new Array() with zero or more than one argument creates an array with

initialized elements. So this:


var x=new Array(10,20)

creates an array with two elements 10 and 20.

This is fundamentally unintuitive so MiniME generates a warning whenever is sees

new Array with a single argument. If you really want to create a sized array

you can suppress the warning with a directive.


// warnings:push:off

var x=new Array(10);

// warnings:pop

Note that during minification MiniME will replace calls to new Array() with an array literal []

and new Object() with an object literal {}. Calls to new Array with one argument are left alone.

Symbol Used Outside Declaring Pseudo Scope

Although JavaScript allows braced statement blocks, these don't actually open a new

symbol scope. This is unlike most other C-style programming languages and is easy forget

or be unaware of.

MiniME lets you code as if JavaScript does support scope in statement blocks and warns you if you use

a symbol outside the so-called "scope" (aka "pseudo-scope") in which it was declared.

The following would yield this warning:


if (HappyToday())

{

    var msg="Hello World";

}   

else

{

    var msg="Ugh";

}

alert(msg);

To fix the warning, declare the variable outside the inner scope like this:


var msg;

if (HappyToday())

{

    msg="Hello World";

}   

else

{

    msg="Ugh";

}

alert(msg);

MiniME considers the variables declared in a for statement to belong to the scope of

the statement and warns if they're used after the loop.


for (var i=0; i<10; i++)

{

    // Do something

}



alert(i);       // Sorry, `i` was in the pseudo-scope of the for-loop.

Similary, to resolve this move the variable declaration:


var i;

for (i=0; i<10; i++)

{

    // Do something

}



alert(i);       // Now MiniME is happy.

Note that although this approach does provide a model closer to what most programmers

are used to, it's not perfect. In particular, variables declared in an inner scope

may have an unexpected initial value.

eg:


if (SomeCondition())

{

    var x=23;

}



if (SomeOtherCondition())

{

    var x;

    alert(x);       // x may have the value 23 from above, not undefined.

}

MiniME provides no warning in this case.

Use of with and Use of eval

The with and eval statements are generally considered bad form - some even say "evil".

These statements not only prevent MiniME from being able to effectively obfuscate any containing

scopes they can also adversely affect the performance of the just-in-time compiling JavaScript

engines in most modern browsers.

If you must use these, try to move them to a function at the global scope to allow obfuscation

of more deeply nested scopes.

Symbol Hides Previous Declaration

Re-using symbols is generally considered bad practice. MiniME will warn about the following:


function DoSomething(x)

{

    for (var x=0; x<10; x++)    // New variable x hides parameter x

    {   

        alert(x);               // Which one?

    }

}

Assignment to Undeclared Variable

Assigning to an undeclared variable creates a new global variable which is generally not the intent.

To prevent this warning, instead of this:


global_variable=42;

declare the variable:


var global_variable=42;

or, use a MiniME directive like this:


// public:global_variable

global_variable=42;