Linting the easy way

For a project I am currently working on I wanted to lint my js files before everything gets built. I usually abuse Komodo Edit to build it anytime on save by calling a simple macro that runs make. But I was still using jshint inside Firefox and it really felt boring to prepend the same custom lint config on top of each new js file. Weeks ago I’ve seen a commit message in jquerys’ git repository mentioning a replacement of jslint in favor for jshint. As they already integrated that tool it was pretty easy to extend their jshint make-target to my needs.

First of all I added a “dependencies.json”-file to my build directory - example content following:

{
    "name": "buildtool",
    "version": "0.0.1",
    "private": true,
    "dependencies": {
        "jshint" : ">=0.5.8"
    }
}

To install jshint with npm I ran the following command inside that build directory: [projectdir]$ npm install -d

I came up with the following js code - saved as jshint-check.js:

var fs = require("fs"),
    jshint = require("jshint").JSHINT,
    targets = [
        "/path/to/file1.js",
        "/anotherpath/to/file2.js"
    ],
    config = {
        // predefine globals from pjs
        prototypejs : true,
        // unfiltered forin
        forin : true,
        // allow the new keyword
        nonew : false,
        evil : true,
        browser : true,
        // allow == null
        eqnull : true,
        expr : true,
        curly : true,
        // no trailing ws
        trailing : true,
        // sloppy ws
        sloppy : true,
        // don't tell me how to format my code
        strict : false,
        // crockfords whitespace settings - nope
        white : false,
        // no undefined vars
        undef : true,
        smarttabs : true,
        noarg : true,
        noempty : true,
        // require ===
        eqeqeq : true,
        // no bitwise operators plz
        bitwise : true,
        indent : 4,
        eqeq : false,
        nomen : false,
        laxbreak : true,
        loopfunc : true,
        predef : [
            // prefefined var
        ],
        maxerr: 100
    },
    content = "";

(function() {
    if( typeof jshint == "undefined") {
        console.log("Install JSHint first - run `npm install -d`.");
        return;
    }
    
    targets.forEach( function( file, key ) {
        content = fs.readFileSync( file , "utf8");
        if ( !! content && jshint( content, config ) ) {
            console.log( "JSHint check passed for file: " + file );
        } else {
            console.log( "JSHint found errors." );
            jshint.errors.forEach(function( e ) {
                if ( !e ) { return; }
        
                var str = e.evidence ? e.evidence : "",
                character = e.character === true ? "EOL" : "C" + e.character;
        
                if ( str ) {
                    str = str.replace( /\t/g, " " ).trim();
                    console.log( file + ": [line " + e.line + ":" + character + "] " + e.reason + "\n  " + str + "\n");
                }
            });
        }
    });
})();

I documented some options - but you may be better off with the options page to customize that part.

The Makefile

My make test-target looks like this:

JS_ENGINE ?= `which node nodejs 2>/dev/null`
test:
    @@if test ! -z ${JS_ENGINE}; then \
        echo "Checking files against JSHint..."; \
        ${JS_ENGINE} jshint-check.js || return -1 \
    else \
        echo "You must have NodeJS installed in order to test js files against JSHint."; \
    fi

Remember to replace spaces with tabs (POSIX requires that) - I just used spaces for readability here.