JavaScript developers have a special relationship with a type of punishment that I like to call "closing bracket hell". You probably already know exactly what I'm talking about. It's when you have a block of JavaScript code and you cannot figure out where the last closing bracket or brace goes. JavaScript isn't analyzed by a compiler, so you can spend a lot of time banging your face on your desk until someone comes along and goes "Oh - it's right there", and then everything just jumps into place. ARRRGGGGG.

IDE's are getting better about this, but you can also get into "missing comma" hell, "should have been : instead of =" hell, or my personal favorite, "you mispelled a variable" hell. As a another frantic soul once said while standing on the edge of a very deep hole, "This is madness!" No. THIS. IS. JAVASCRIPT! <frontKick data-area="chest" />

Linting Origins

A few years ago, Douglas Crockford decided that most of the JavaScript being churned out was not very good, and set about writing a library that would parse your code and tell you that it was bad. It's probably better at provoking your punching a hole in the wall due to it's incredibly strict and opionated nature, but the fundamental idea was genious: Use JavaScript to analyze JavaScript (yo dawg) and have the system tell you where your stupid mistakes are before you run it in the browser. Parsers and compilers are very good at this sort of thing. You on Monday morning after a Fringe binge, not so much. He called his creation JSLint.

People loved the fundamental concept of JSLint, but didn't like that it was essentially a library that enforced one person's opinion about how your JavaScript should be written, and provided no way to relax the rules.

Don't Harsh My Mellow

Then came JSHint. A fork of JSLint that would enforce rules, but allow you to turn them off. For instance, why should you be forced to use single quotes? Why do you need to know about every peice of trailing whitespace? You don't. That doesn't help - that's just nagging. JSHint allows you to turn this off with a convenient set of switches. It's essentially a much more mellow JSLint.

Code linters for JavaScript are your army of 300 to take on the thousand nations of the web development empire.

Using JSHint

JSHint is most often used as a NodeJS package (although any JavaScript engine will suffice).

Installing JSHint

// install jshint
$ npm install jshint -g

It is installed via npm and then executed against a file with a command. For instance, say you have the following lovely bit of completely broken JavaScript:

Failure Defined

// you included jQuery, but never used it
(function ($) {

  // pub is defined but never used
  var pub = {
    // this is part of an object literal, not an assignment
    init = function(response) {
      // respnse should be response
      console.log(respnse);
    }
  }

// you're missing a semicolon here
// also - its jQuery, not jquery
}(jquery))

Now browser dev tools are good, but they are not great for syntax checking as it requires you to leave your IDE and then come back. That's pretty iterative. What's more, the browser is only going to report the first error that the parser finds and then it exits the process.

At least the browser told us about the '=' sign with a fairly informative error. However, it said nothing about the fact that I am trying to pass 'jquery' into this function instead of 'jQuery'. I don't get to find that out until I fix the '=' error, save, and then refresh.

This means that if you have 10 syntax errors in your code, you are going to be refreshing and checking the console 10 times. Ouch. Debugging your JavaScript in the browser is entirely expected, but using it to check your syntax is kind of like riding a bike uphill. You're working pretty hard, but you aren't going very fast and what you are really doing is wearing yourself out.

Linters provide this syntax check before you ever hit the browser - AND - they report all of the errors at once.

Using JSHint

// run the jshint parser against your file
$ jshint test.js

// jshint spits out everything it thinks looks bogus
test.js: line 5, col 3, Missing "use strict" statement.
test.js: line 7, col 19, Missing space after 'function'.
test.js: line 11, col 4, Missing semicolon.
test.js: line 15, col 11, Missing semicolon.
test.js: line 9, col 19, 'respnse' is not defined.
test.js: line 15, col 3, 'jquery' is not defined.
test.js: line 5, col 10, 'pub' is defined but never used.
test.js: line 2, col 13, '$' is defined but never used.
test.js: line 7, col 28, 'response' is defined but never used.

9 errors

Pretty nice huh! It gives you line numbers and column numbers too so you know exactly where to start looking.

Relax Buddy

You might notice that it's complaining about 'use strict'. While you probably should be using strict mode, you may have a valid reason not to. You can turn this off for JSHint. You can include a file in your project called .jshintrc which specifies what sort of checks you want to make. The reason why mine is complaining about strict mode is because I asked it to double-check me on that. Here is my .jshintrc file.

My .jshintrc File

{
  "node": true,
  "browser": true,
  "es5": true,
  "esnext": true,
  "bitwise": true,
  "camelcase": true,
  "curly": true,
  "eqeqeq": true,
  "immed": true,
  "indent": 2,
  "latedef": true,
  "newcap": true,
  "noarg": true,
  "regexp": true,
  "undef": true,
  "unused": true,
  "strict": true,
  "smarttabs": true,
  "globals": {
    "angular": false,
    "$": false,
    "kendo": false,
    "jQuery": false
  }
}

There are a lot of settings here, but down towards the bottom you can see that I have strict set to true. Also notice that I have some globals defined. If I don't define these, JSHint will report them as undefined. Unless I toggle undef to true. See how easy going it is? Easy like Sunday morning.

It's important to note that JSHint will walk up your directory structure looking for a .jshintrc file until it finds one. That means that you can specify a global set of configurations for JSHint by having 1 .jshintrc file. Any .jshint file that you create in your project directories will override the global one.

Integrating Linters Into Your Build Process

Of course, you probably don't want to drop down to a command line and manually execute JSHint against your file. You have a couple of options here.

Grunt It Out

If you are a Grunt user, there is grunt-contrib-jshint. This allows you to lint your code as part of your client-side build process. If you are using grunt-contrib-watch, you can have Grunt execute JSHint everytime a file is saved.  Lets setup an evironment for doing Grunt builds.

Once you get Grunt installed and running, you can install the grunt-contrib-jshint plugin.

Installing grunt-contrib-jshint

// execute from the directory where your project is
npm install grunt-contrib-jshint --save-dev

Then you load the module into Grunt and add a task for it in the Gruntconfig file

Sample Gruntconfig file

module.exports = function(grunt) {

  // Project configuration.
  grunt.initConfig({
    jshint: {
      options: {
        jshintrc: '.jshintrc'
      },
      all: [ 'Gruntfile.js', 'js/**/*.js' ]
    }
  });

  // add the jshint grunt task
  grunt.loadNpmTasks('grunt-contrib-jshint');

  // Default task(s).
  grunt.registerTask('default', ['jshint']);

};

Note that in my testing, the JSHint Grunt Contrib did not walk my tree and find the global .jshintrc file. It appears to require you to specify settings in the Gruntfile, or a path to your .jshintrc.

Specify Settings For Undefined And Strict Mode

// Project configuration.
grunt.initConfig({
  jshint: {
    options: {
      undef: true,
      strict: true
    },
    all: [ 'Gruntfile.js', 'js/**/*.js' ]
  }
});

You can combine this with the grunt-contrib-watch plugin so that you get linted everytime you save a file.

Watch Files And Lint On Save

module.exports = function(grunt) {

  // Project configuration.
  grunt.initConfig({
    jshint: {
      options: {
        undef: true,
        strict: true
      },
      all: [ 'js/**/*.js' ]
    },
    watch: {
      scripts: {
        files: 'js/**/*.js',
        tasks: 'jshint'
      }
    }
  });

  // add the jshint and watch grunt tasks
  grunt.loadNpmTasks('grunt-contrib-jshint');
  grunt.loadNpmTasks('grunt-contrib-watch');

  // Default task(s).
  grunt.registerTask('default', ['jshint', 'watch']);

};

This is all still happening behind the scenes of your IDE, so you can add in the grunt-notify plugin, which pipes the terminal output into a Growl, OS X Mountain Lion or Windows notification to give you a visual cue that something is rotten in the state of Denmark.

Add Growl Notify Plugin

// notify us when things fail
grunt.loadNpmTasks('grunt-notify');

IDE Integration

Most IDE's have some sort of integrated support for JSHint. If not directly, you can install it via a package.

Sublime Text 2

You can install the SublimeLinter plugin which lints all kinds of languages. It also puts marks in the gutter and outlines the offending lines. This is the plugin that I use.

By default, this plugin puts a box around each of the offending lines and tells you in the strip at the bottom what your error is.

This is very visually disrupting for me. You can change the way that SublimeLinter visually marks your errors by going into the Package Preferences, and then the User Settings File for SublimeLinter. Set the sublimelintermarkstyle to "none", and then setting sublimelinterguttermarks: true. That will put a little mark in the gutter next to each offending line with underscores at the exact placement of the error.

There is also a plugin called JSHint which will shell out errors to the console in Sublime Text 2. This one is good, but I don't use it for a very specific reason that I'll talk about below.

Visual Studio

JSHint support comes to visual studio by way of the Web Essentials plugin that I have unashamadly raved so much about in past posts. This posts warnings to the warnings console - right where you would expect them to be. Clicking on the warning takes you right to the offending line. If you are using Visual Studio, there is no reason not to be linting your JavaScript. Microsoft has provided really great support for JSHint.

Others?

Surely there are other linters for IDE's out there. Eclipse, VIM, EMACS, Textmate - have I missed any? Have one to add? Please leave it in the comments and I'll post an update to get them included.

Take Linting To The Next Level

So far we have talked about how to lint your JavaScript. While JSHint/Lint are incredibly powerful, there are some mistakes that they just can't catch. Specifically, it can't foresee issues in 3rd party library integrations. This is a core development problem in UI libraries such as Kendo UI. You select an element, and pass in configuration values. The problem is that if you pass in an invalid configuration object, nobody complains. It simply doesn't work and you don't know why.

Consider this example where I am initializing a Kendo UI DropDownList with a bunch of invalid configuration values:

Invalid Kendo UI DropDownList Configuration

$("#birthdate").kendoDropDownList({
  // read defined outside of a transport
  dataSource: {
    read: "things.json"
  },
  // the configuration is optionLabel
  optionalabel: "Select A Thing",
  dataTextField: "name",
  // should be dataValueField
  dataValuefield: "id"
});

This code lints just fine with JSHint, but the outcome is a sad little empty DropDownList and no errors.

Using The Kendo Linter

You may have noticed last week that Brandon posted about some of the new and delicous activity over on the Kendo UI Labs. One of the most exciting projects is the new code analysis that Mihai Bazon has been doing with a new project (which you will hear about at the keynote. Have you signed up yet?) Mihai is the same guy who wrote UglifyJS, so he knows a thing or two about analyzing code with code.

The Kendo UI Linter works in the same way as JSHint or JSLint. It analyzes the file and tries to help you catch mistakes in your Kendo UI widget declarations. This way you don't have to submit a support ticket because you didn't capitalize the 's' in 'dataSource'. I was experimenting with this new utility this weekend. It installs just like JSHint.

Installing Kendo-Lint

// install the kendo linter globally
npm install -g kendo-lint

You also run it against your file just like JSHint. Let's see what it tells us about our depressed DropDownList.

Running Kendo-Lint Against A File

// lint the file with kendo linter
kendo-lint js/test.js

// output from kendo-linter
➜  demos  kendo-lint js/test.js
js/test.js[7,6]: Option read not found
js/test.js[10,4]: Option optionalabel not found
js/test.js[13,4]: Option dataValuefield not found

Alright then! That's some helpful information. If the option isn't found, then you didn't specify it correctly. You also get a line number so you can go directly to the problem.

Let's switch this up a bit and see if we can't fool the linter. What happens when we go with declarative intialziation? For the record, I use declarative intialization almost exclusively. There are still some scenarios where I select an element and initialize it, but those times are few and far between.

DropDownList With Declarative Initialization

<select id="birthdate" data-role="dropdwnlist" data-optionlabel="Select A Thing" data-text-field="name" data-valuefield="id" data-sourc="APP.things"></select>

That pathetic bit of code produces the same dejected non-functional Kendo UI DropDownList. The Kendo Linter is actually capable of linting your declarative markup for errors as well. SERIOUSLY. Check it out:

Lint The HTML For Kendo UI Declarative Markup

// lint the HTML file
kendo-lint index.html

// output
➜  demos  kendo-lint index.html
index.html: Option optionlabel not found
index.html: Option textField not found
index.html: Option valuefield not found
index.html: Option sourc not found

If you just had an OMG moment, your in good company. Obviously, this is something we need to get into our workflow yesterday. Lets look at how to do that with a few different options.

Grunt

Brandon worked up a little grunt plugin that you can install, and then add to your Grunt tasks.

Install Kendo Linter Grunt Plugin

// install
npm install grunt-kendo-lint --save-dev

Now we load the module in the Gruntfile and add in a task for linting with the Kendo Linter. Of course, we are watching files so we can watch all our js and html files and the kendo linter will fire automagestically.

Adding Kendo-Lint To Gruntfile

module.exports = function(grunt) {

  // Project configuration.
  grunt.initConfig({
    jshint: {
      options: {
        undef: true,
        strict: true
      },
      all: [ 'js/**/*.js' ]
    },
    kendo_lint: {
      options: {
        force: true
      },
      files: [ 'js/**/*.js', '*.html' ]
    },
    watch: {
      scripts: {
        files: 'js/**/*.js',
        tasks: 'lint'
      }
    }
  });

  // add the jshint and watch grunt tasks
  grunt.loadNpmTasks('grunt-contrib-jshint');
  grunt.loadNpmTasks('grunt-contrib-watch');

  // load up the kendo linter
  grunt.loadNpmTasks('grunt-kendo-lint');

  // notify us when things fail
  grunt.loadNpmTasks('grunt-notify');

  // Default task(s).
  grunt.registerTask('default', ['lint', 'watch']);
  grunt.registerTask('lint', ['jshint', 'kendo_lint']);

};

Notice that I added a force:true to the kendo linter options. This is because the Kendo Linter Grunt plugin will cause Grunt to exit the build if errors are found - effectively killing the watch process. This is the default. Forcing it to continue on says, "I know there may be errors, but it's OK to keep going". Sometimes you don't want your build to continue on error, but in my case, it's exactly what I want.

Sublime Text 2

a Sublime Text user, I immediately wondered how hard it would be to get it to work inside of Sublime Text 2 without me having to drop to a command line to analyze the file or use Grunt. As it turns out, its pretty trivial!

You can create a new build system which will analyze the current file - either JavaScript or HTML. Create a new build system, and call it "kendoui". Here is the syntax that you need for the build system...

{
  "cmd": ["kendo-lint", "$file"],
  "selector": ["source.js", "source.html"],

  "windows": {
    "shell": true
  }
}

I had to throw in a clause there for windows installations to get it to use the shell. This seemed to be enough for me to get it to work. I would be interested if this works for you on Windows as well.

Now when you press command/ctrl B (depends on your OS), Sublime Text 2 will open the console and show you any errors from the Kendo Linter. This is why I don't use the JSHint plugin. I want my build system to be the output. SublimeLinter takes care of JSHint for me and I can pipe my grunt output to the console via the build system. The build file is rediculously simple.

Executing Grunt From Build System

// childs play
{
  "cmd": ["grunt"]
}

The Gruntfile is a lot simpler now as well. I removed JSHint because Sublime Linter is doing that for me. I don't need to watch the files since I only need to build on demand.

Simplified Grunt File

module.exports = function(grunt) {

  // Project configuration.
  grunt.initConfig({
    kendo_lint: {
      options: {
        force: true
      },
      files: [ 'js/**/*.js', '*.html' ]
    }
  });

  // load up the kendo linter
  grunt.loadNpmTasks('grunt-kendo-lint');

  // Default task(s).
  grunt.registerTask('default', ['kendo_lint']);

};

When I do build, it lints all of my files with the Kendo Linter.

Moratorium On Harshed Mellows

Do you know what's better than a rock solid workflow? Nothing. Ok, possibly summer sausage, but even that's up for debate. I'm really excited about the new Kendo Linter because I can already see all of the time that I'm going to being getting back, and that is definately lovely and wonderful in all ways. It requires us as web developers to wrap our brains around having a build system for JavaScript - which frankly seems odd. Someone once said that the browser already has a build system - it's called the "refresh" button. The refresh button is really just the same as a double-click on an executable. The build system makes sure that the "refresh" actually produces runable code.

You can get your hands on Kendo UI today of course, and you can get the Kendo UI Linter and Kendo Lint Grunt plugins off of NPM as described above. Start linting and don't let UI frameworks and JavaScript harsh your mellow.

Edits

In the spirit of continuous improvement...

WebStorm has JSHint integration right out of the box.  It also respects the global .jshintrc, which is quite nice. (via Bart)

JSHint/JSLint Errors is a site which explains the console errors from JSLint/Hint. (via James Ardalis)

Kendo UI Lint For Vim is a plugin for - well - Vim which lints your code on command and even jumps directly to the correct line of the error when you select it in the console. (via @derickbailey)


Burke Holland is the Director of Developer Relations at Telerik
About the Author

Burke Holland

Burke Holland is a web developer living in Nashville, TN and the Director of Developer Relations at Telerik. He enjoys working with and meeting developers who are building mobile apps with jQuery / HTML5 and loves to hack on social API's. Burke works for Telerik as a Developer Advocate focusing on Kendo UI.

Related Posts

Comments

Comments are disabled in preview mode.