2011-09-04

JSHint – a JavaScript code quality checker

JSHint is a tool that analyzes JavaScript source code to warn about quality problems. This post describes how to use it.

What it is

JSHint...
  • detects anti-patterns and potential errors. Example: Using an assignment in the condition of an if statement.
        if (x = 3) { ... }
    
  • enforces best practices and coding styles. Example: The names of constructors must be capitalized.
There are also plug-ins for integrating development environments with JSHint. For example, a TextMate bundle that runs JSHint whenever a JavaScript file is saved.

History. JSHint is based on Douglas Crockford’s JSLint whose initial release was in 2002. In 2011, Anton Kovalyov released JSHint and explains why he forked Crockford’s tool:

JSLint served me well for quite some time but in the past few months it has gotten uncomfortably opinionated and hostile towards your code. It is quickly transforming from a tool that helps developers to prevent bugs to a tool that makes sure you write your code like Douglas Crockford.

Installation

  • Install npm, the Node Package Manager
  • Use npm to install node-jshint:
        npm install -g jshint
    
    In most Unix installations, you have to prefix the above command with a sudo (“execute as super-user”), so that everything can be put where it needs to be.
The JSHint GitHub page mentions other platforms that JSHint runs on: Rhino, Windows Scripting Host, Mac OS X JavaScriptCore.

Example

Let’s say we want to check the following file demo.js.
    function foo(x, y) {
        if (x > y) return x;
        bar = x * y
        return
        bar;
    }
This command runs JSHint:
    jshint demo.js --config config.json
The optional --config allows you to configure JSHint’s operation via a custom config file (config.json in the command above). Such a file is in JSON syntax [1] which means that keys must be quoted and that there can be no trailing comma. The settings that can be influenced by a config file are listed later. config.json looks as follows.
    {
        "predef": [ "myModule" ],
        "undef": true,
        "curly": true
    }
node-jshint comes with a much more comprehensive example.

Running JSHint on demo.js with the previously mentioned command produces the following errors:

    demo.js: line 2, col 16, Expected '{' and instead saw 'return'.
    demo.js: line 3, col 5, 'bar' is not defined.
    demo.js: line 3, col 16, Missing semicolon.
    demo.js: line 4, col 5, Line breaking error 'return'.
    demo.js: line 5, col 5, 'bar' is not defined.

    5 errors
Reasons for the reported errors:
  1. The then-clause of the if statement must be a block.
  2. Missing var before bar.
  3. Missing semicolon after the assignment.
  4. The return statement cannot be broken up into two lines [2].
  5. Accessing the undeclared variable bar.

Inline options

Some settings can be controlled by putting comments in the source code.
  • Global variables (setting predef):
        /*global DISQUS: true, jQuery: false */
    
    Consequence: The global variable DISQUS can be read and written, jQuery can only be read.
  • Other settings (see below for a list):
        /*jshint evil: true, boss: true */
    

Config file settings

The default for all boolean settings is always false. [Most of the text in this section has originally been taken from the JSHint source code.]

Controlling how it runs

passfail boolean true if checking should stop after the first error.
maxerr number Stop after how many errors? Default: 50.

Debugging

debug boolean true if debugger statements are allowed. These act like break points and probably shouldn’t be in shipping code.
devel boolean true if logging globals (console, alert, ...) should be considered predefined. In other words: don’t complain when those are used.

Firefox-only

iterator boolean true if the __iterator__ property should be disallowed.
proto boolean true if the __proto__ property should be disallowed.

ECMAScript 5 and standard-compliance

es5 boolean true if ES5 syntax is allowed.
globalstrict boolean true if global "use strict"; is allowed (also sets strict to true).
noarg boolean true if arguments.caller and arguments.callee should be disallowed.
nonstandard boolean true if non-standard – but widely used – globals should be predefined (see below what that means). Currently, those are escape and unescape which are deprecated anyway – it is better to use encodeURIComponent and decodeURIComponent, instead.
standard boolean true if standard ECMAScript globals should be predefined (see below what that means). Examples: Array, Date, JSON.
strict boolean If true, require the "use strict"; for all code (be it file-globally or for each function individually).

Predefined globals

These options control what global identifiers are considered predefined by JSHint. JSHint will let you access a predefined global without having declared it beforehand.

predef string[] an array with the names of custom predefined global variables.
browser boolean true if the standard browser (DOM) globals should be predefined. Examples: document, WebSocket, window.
couch boolean true if CouchDB globals should be predefined.
dojo boolean true if Dojo Toolkit globals should be predefined.
jquery boolean true if jQuery globals should be predefined.
mootools boolean true if MooTools globals should be predefined.
node boolean true if the Node.js environment globals should be predefined. Examples: exports, global, process.
rhino boolean true if the Rhino environment globals should be predefined. Examples: load, print, quit.
wsh boolean true if the Windows Scripting Host environment globals should be predefined. Examples: ActiveXObject, VBArray, WSH.
prototypejs boolean true if Prototype and Scriptaculous globals should be predefined.

Style

asi boolean true if automatic semicolon insertion should be tolerated. If false, you are warned about all missing semicolons.
newcap boolean true if constructor names must be capitalized.
nomen boolean true if leading or trailing underscores should be disallowed in names. Those are often used for “privacy by naming convention” (which some consider bad practice, but there are cons and pros to it).
onevar boolean true if only one var statement per function should be allowed.
plusplus boolean true if increment/decrement should not be allowed. If you use this operator in a statement by itself (as opposed to inside another statement), then there is nothing bad about it.
lastsemic boolean true if the semicolon may be omitted after the last statement inside a one-line block.
sub boolean true if inefficient property access is allowed. For example, obj["prop"] is less efficient than obj.prop. This option never complains about computed values inside square brackets.
trailing boolean true if trailing whitespace is forbidden.
white boolean true if strict whitespace and indentation rules apply.

Correctness

bitwise boolean true if bitwise operators should not be allowed (which are likely typos).
boss boolean true if advanced usage of assignments should be allowed. Example: An assignment inside the condition of an if statement (which is likely a mistyped equals operator).
curly boolean true if blocks (in curly braces) should be required where one can also put single statements (as part of if statements, loops, etc.).
eqeqeq boolean true if === should be required.
eqnull boolean true if == null comparisons should be tolerated.
evil boolean true if eval should be allowed.
expr boolean true if an expression should be allowed to be the complete program (in a file).
forin boolean true if for...in statements must filter via hasOwnProperty().
immed boolean true if immediate function invocations must be wrapped in parens.
latedef boolean true if a variable must be defined (declared and have a value) before you can use it.
laxbreak boolean true if protected line breaks (e.g. after return), which lead so semicolon-insertion, should be allowed.
loopfunc boolean true if it should be allowed to define functions within loops.
noempty boolean true if empty blocks should be forbidden.
nonew boolean true if constructors should not have side effects.
onecase boolean true if one case switch statements should be allowed.
regexdash boolean true if an unescaped last dash (-) inside brackets should be permitted in regular expressions.
regexp boolean true if the . should not be allowed in regular expression literals. It tends to match too many characters.
undef boolean true if variables should be declared before use.
scripturl boolean true if javascript: URLs are allowed (JSHint finds them in string literals).
shadow boolean true if variable shadowing should be tolerated.
supernew boolean true if new function () { ... }; and new Object; should be tolerated.
validthis boolean true if this inside a non-constructor function is valid. This is a function scoped option only.

Related reading

  1. JSHint homepage
  2. node-jshint on GitHub
  3. JavaScript’s JSON API
  4. Automatic semicolon insertion in JavaScript

No comments: