Automating JSLint Validation

JavaScript validation doesn’t need to be painful, but can be if the only test we use is to run it in the browser and hope it works. Enter JSLint a powerful testing library for JavaScript written by Douglas Crockford. To test your JavaScript code, you can cut and paste all of your JavaScript file content into the textarea, click “Good Parts” then “JSLint” and prepare to spend the rest of your day fixing the numerous errors it returns.

However, there’s an easier way. Thanks to the node.js project, an environment written to run JavaScript code independent of a browser, we can run the JSLint code automatically. I used a post by Frontend Force to install Node and get started. I skipped the parts about growl notifications because I am developing all of my code remotely. I also wanted to ensure that I ran the validation as a blocking component of my deploy script.

cd
git clone http://github.com/ry/node.git
cd node
./configure
make
sudo make install

I ran into a SSL issue with my install and had to use ./configure –without-ssl to get it running. Next I created a directory for my validator.

mkdir ~/validator
cd ~/validator
wget http://www.jslint.com/jslint.js
echo "exports.check = JSLINT;" >> jslint.js

The validation script from Frontend Force runs against the path in which it resides. This is great if you have a single project, but I manage code sets for multiple clients and didn’t want an incomplete feature in one project blocking automated deployment of another client’s code. Another feature I didn’t need was the continuous integration. My deployment process happens at regular intervals and so I only run automated checks as part of that deployment instead of continuously.

Further, the example code didn’t allow for the setting of any options such as being tolerant of whitespace, or even the stricter “good stuff” that the website allows. This can be really problematic if you are validating libraries of code that you’ve pulled from other sources or want to maintain a specific set of coding standards.

The script that Frontend Force provides automatically traverses the entire path structure and processes any file ending in .js. This is a possible gotcha to be aware of if you are generating ANY JavaScript inside another file – as part of a .NET or PHP process or embedding it inline as part of an HTML file – it won’t be validated! The best fix is to ensure all your JavaScript code is moved outside of these files. You can then only generate the bare minimum to validate manually.

My changes to the script allows me to do three things:

  • Pass in a unique working directory at runtime, making this scriptable.
  • Run just once and act on the output.
  • Specify a global set of parameters to invoke the jslint check script with.

My version of the check process – ~/validator/check.js – built heavily off of the work of Frontend Force accomplishes all of these and also make the output more useful for my use case.

View Code JAVASCRIPT
var	sys = require('sys'),
	fs = require('fs'),
	path = require('path'),
	jslint = require('./jslint'),
	dir = __dirname,
	jslint_options = {
		'passfail':false,	// Stop on first error
		'white':true,		// Strict whitespace (enabled in "good parts")
		'browser':false,	// Assume a browser
		'devel':false,		// Assume console, alert,...
		'widget':false,		// Assume Yahoo widget
		'windows':false,	// Assume Windows
		'rhino':false,		// Assume Rhino
		'safe':false,		// Safe Subset
		'adsafe':false,		// ADsafe
 
		'debug':false,		// Tolerate debugger statements
		'evil':false,		// Tolerate eval() statements
		'laxbreak':false,	// Tolerate sloppy linebreaking
		'forin':false,		// Tolerate unfiltered for in
		'sub':false,		// Tolerate inefficient subscripting
		'css':false,		// Tolerate CSS workarounds
		'cap':false,		// Tolerate HTML case
		'on':false,		// Tolerate HTML event handlers
		'fragment':false,	// Tolerate HTML fragments
		'es5':false,		// Tolerate ES5 syntax
 
		'onevar':true,		// Allow one var statement per function (enabled in "good parts")
		'undef':true,		// Disallow undefined variables (enabled in "good parts")
		'nomen':true,		// Disallow dangling _ in identifiers (enabled in "good parts")
		'eqeqeq':true,		// Disallow == and != (enabled in "good parts")
		'plusplus':true,	// Disallow ++ and -- (enabled in "good parts")
		'bitwise':true,		// Disallow bitwise operators (enabled in "good parts")
		'regexp':true,		// Disallow insecure . and [^...] in /RegExp/ (enabled in "good parts")
		'newcap':true,		// Require Initial Caps for constructors (enabled in "good parts")
		'immed':true,		// Require parens around immediate invocations (enabled in "good parts")
		'strict':false,		// Require "use strict";
	};
 
function validate(filename, callback){
	fs.readFile(filename, 'utf8', function(err, file){
		sys.print('Checking ' + filename.replace(__dirname + '/', '') + '... ');
		callback( jslint.check( file.replace(/^\#!.*/,''), jslint_options ) );
	});
}
 
function status(ok){
	if(ok){
		sys.puts("all right");
	} else {
		var errors = jslint.check.errors;
		sys.puts(errors.length + ' errors found' + "\n");
		for(var i=0; i<errors.length; i+=1){
			if(errors[i]){
				sys.puts(' ' + errors[i].line + ':' + errors[i].character + '  : ' + errors[i].reason + ' >> ' + errors[i].evidence);
			}
		}
	}
	sys.puts("\n");
}
 
function walk(filename, callback){
	fs.stat(filename, function(err, stats){
		if(stats.isFile() && filename.match(/\.js$/)){
			// Filename
			callback(filename);
		} else if(stats.isDirectory()){
			// Directory - recurse
			fs.readdir(filename, function(err, files){
				for(var i=0; i<files.length; i+=1){
					walk(filename + '/' + files[i], callback);
				}
			});
		}
	});
}
 
if(process.argv.length > 2){
	dir = process.argv[2];
}
 
walk(dir, function(filename){
	validate(filename, status);
});

My deploy script then inspects the output from the check script and decides what to do.

#!/bin/bash
ERRORS=0
node ~/validator/check.js /var/www/html/dev_path > ~/validator/dev_path.txt
for i in `cat ~/validator/dev_path.txt | grep "errors found" | awk '{print $2'}`
do
	ERRORS=$((ERRORS+1))
done
 
if test $ERRORS -gt 0
then
	echo Had $ERRORS errors.
	cat ~/validator/dev_path.txt
	exit
fi
 
#
#remainder of deploy script code here
#

The JavaScript code becomes a blocker to the deploy script ensuring that processing aborts and we have a written record as to why in the validator directory.

This entry was posted in JavaScript, Testing and tagged , , , . Bookmark the permalink.

2 Responses to Automating JSLint Validation

  1. Pingback: Tuesday « Protoblogger

  2. Datalion says:

    Nice tip,

    but this validation is available for end scripts.
    I’m means that I didn’t find any way to add some libraries to check fr optimization.
    I’m using prototypejs on all my scripts so execute jslint on all script, so I have plenty of errors :
    some are due to my fault
    some are due to ‘inimplemented function’ (for example, $ is a prototypejs function) so not implemented)

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>