Check your scripts with JSLint on Rails
This year, for several months I’ve been working on a project which involved quite a lot of JavaScript. I’ve already written about how this prompted me to start writing JavaScript unit tests. But as I found out later, there were some kinds of JavaScript errors which the unit tests didn’t help me find. Let me give you an example…
(TLDR: link to the Github project page)
When I work on a Rails web app, I don’t usually test the project in Internet Explorer all the time – if I did that, I’d have gone mad long time ago. Instead, I do everything in Firefox, and leave IE testing for more patient people – either our tester, or even the client in smaller projects. This seems to work well most of the time; however, in this project every 2 weeks or so I used to get such bug report: “The site crashes in IE”. Crashes here means that it doesn’t load at all. You see, if JavaScript is just a nice optional add-on to your project, it’s not a big thing if something doesn’t work; but if your entire application depends on JavaScript for everything it does, then one tiny mistake and you’re screwed.
The problem is that IE has this nasty habit of breaking on code that has a comma at the end of a hash – you know, something like this: { a: 1, b: 2, c: 3, }
. And I do this mistake surprisingly often, because that tiny little comma is so easy to miss, and no one ever complains about it except IE – Firefox works fine, unit tests work fine, but next morning I get that dreadful bug report that IE just explodes. Then I wait for VirtualBox to boot Windows and eat all remaining memory, and spend half an hour debugging, only to find that this was all because of one comma…
But here’s an idea: these kinds of mistakes are trivially easy to find if you introduce another tool to your testing process – JSLint. You probably know that tool, but in case you don’t – it’s basically a JavaScript code analyzer that scans your scripts and looks for all kinds of common errors (or bad coding habits which may potentially cause errors).
So why didn’t I use it? I did. Sometimes. Like once a month, when I happened to remember I had installed it. But the thing is, such tool is useless unless it’s used automatically, every time. As the Pragmatic Programmer book says, humans just can’t be trusted with such things:
Don’t use manual procedures
Whether it is the build and release procedure, code review paperwork, or any other recurring task on the project, it has to be automatic. People just aren’t as repeatable as computers are. Nor should we expect them to be. A shell script or batch file will execute the same instructions, in the same order, time after time.
So I started thinking if I can integrate JSLint in the Cruise Control task – that way, every time I commit code with errors, I would get a notification just a few minutes later. I found some instructions how to do this in a blog post by Jonathan Julian – you need to download two JSLint files, merge them together, download Rhino, then write a Rake task that calls them all and parses the results. But then I thought: shouldn’t this be simpler? Shouldn’t you be able to just install a plugin with one line and call a rake task prepared for you? But I wasn’t able to find such plugin anywhere – so I wrote one :)
Here’s how you use it:
- Make sure you have Java installed (5.0 or later) – it’s required to run Rhino.
- Install the plugin:
./script/plugin install git://github.com/mackuba/jslint_on_rails.git
- Run the rake task:
rake jslint
Voila :) That’s all you need to check your JS code. You will get a result like this (if everything goes well):
Running JSLint:
checking public/javascripts/Event.js... OK
checking public/javascripts/Map.js... OK
checking public/javascripts/Marker.js... OK
checking public/javascripts/Reports.js... OK
No JS errors found.
If you’ve messed up something, you will get such results instead:
Running JSLint:
checking public/javascripts/Event.js... 2 errors:
Lint at line 24 character 15: Use '===' to compare with 'null'.
if (a == null && b == null) {
Lint at line 72 character 6: Extra comma.
},
checking public/javascripts/Marker.js... 1 error:
Lint at line 275 character 27: Missing radix parameter.
var x = parseInt(mapX);
Found 3 errors.
rake aborted!
JSLint test failed.
The plugin has also a configuration file, config/jslint.yml
, which is created for you during the installation. You’ll want to take a look at it, especially to change the paths:
option which tells it which files to check – I’m pretty sure you don’t want it to check entire jQuery or Prototype each time, just your own code. You can also set all JSLint options in that file – I’ve set the defaults to what I believed was reasonable, but feel free to tweak them if you disagree.
I’ve also added an option that disables one warning that always annoyed me in JSLint – about a missing semicolon at the end of a one-liner function. When I write a one-line anonymous function that’s passed as an event handler or as a block to map/each – something like: list.map(function(i) { return i.id });
– I usually don’t put a semicolon inside the function because it looks cleaner without it IMHO. And JSLint complains about it, because it wants semicolons everywhere, no exception. So I’ve added an option – “lastsemic” – which disables that warning if this happens right before the end of the block.
I know some people will say I should just do as JSLint says and don’t argue, or that missing semicolons may cause errors while compressing JavaScript – well, I wrote a lot of such functions and I never had a single error because of that. I’m with Dean Edwards on this one:
JSLint is not harmful. Bending over backwards to pass the JSLint test is. Bending over backwards, generally, is a bad idea.
Anyway, that option is disabled by default, so don’t worry. And happy JavaScript testing :)
Oh, I’d forget: thanks to Douglas Crockford for creating the JSLint itself – he’s the one that did the real work, my plugin is really just a wrapper that calls it from Ruby…
Project page on GitHub: http://github.com/mackuba/jslint_on_rails
JSLint project page: http://jslint.com
1 comment:
Dominic Mitchell
Integrating jslint into your build is such a good idea. That's why I wrote my wrapper http://code.google.com/p/jslint4java, to try and integrate it into ant builds. Your rails plugin looks very useful!