Ned Batchelder just wrote about checking javascript syntax in Django, so I figured it was about time to document how I use jslint:
Use the jslint command line version.
Add some jslint configuration comments to the top of your javascript files.
Integrate the whole thing in Hudson for continuous checking.
Jslint can be run and configured in the browser by copy/pasting your code into http://www.jslint.com/ . Now, that’s not a thing you want to do after every javascript change, right? So there’s also a command line version for Rhino. Rhino is a command line javascript runner.
On debian/ubuntu, just do aptitude install rhino
and download the
jslint.js file. You don’t need to type the lengthly commandline on that
jslint page: on ubuntu you can just type rhino path/to/jslint.js
path/to/your.js
. Beware: as far as I could see, it did not accept
multiple javascript files to test, so testing *.js
will in fact only test
the first one.
This led me to write a small python script that calls rhino with jslint and does some directory walking to find javascript files and calls jslint on all of them:
#!/usr/bin/python
# Place this file somewhere as ``jslint`` (preferrably
# without the .py) and make it executable. Place a
# downloaded jslint.js in the same directory.
import os
import commands
import sys
RHINO = 'rhino' # "aptitude install rhino" on ubuntu.
JSLINT = os.path.abspath(os.path.join(
os.path.dirname(__file__), 'jslint.js'))
def main():
if not len(sys.argv) > 1:
print "Usage: jslint script1.js [script2.js...]"
print " or: jslint directory"
sys.exit(1)
javascript_files = sys.argv[1:]
if len(javascript_files) == 1:
possible_dir = javascript_files[0]
if os.path.isdir(possible_dir):
javascript_files = []
for (dirpath, dirnames, filenames) in os.walk(
possible_dir):
javascript_files += [
os.path.join(dirpath, filename)
for filename in filenames
if filename.endswith('.js')]
for javascript_file in javascript_files:
(status, output) = commands.getstatusoutput(
' '.join([RHINO, JSLINT, javascript_file]))
if status == 0:
# Success!
print "%s is OK" % javascript_file
else:
print "Error checking %s" % javascript_file
print "exit code:", status
print output
sys.exit(status)
sys.exit(0)
if __name__ == '__main__':
main()
Jslint is pretty picky, so you need to tame it a bit. Most common things you need to do:
Tell it you run your javascript in a browser so that several (but not all) typical variables are known to exist. If you’re missing one: google for “jslint” plus the missing one and you’ll probably find a good explanation.
Tell it about your globally available variables that you know are prepared
for you by external libraries. Like $
if you’re using jquery :-)
You do that by adding a few comment lines to the top of your javascript file, like this:
// jslint configuration; btw: don't put a space before 'jslint' below.
/*jslint browser: true */
/*global $, OpenLayers, window, updateLayer */
Some of the things that jslint enforces:
No extraneous or missing commas, semicolons, etcetera. You’re guaranteed that Internet Explorer does not hickup because you forgot one of those.
Stating your variables beforehand. You’re required to state them all at the
beginning of your file or your functions. No more half-way var xyz =
123;
. It enforces good habits in this sense.
It also forces you to change things that are perfectly ok, but that’s pretty
rare. The most common one is that it dislikes the normal “i++” style of
loop. You have to change that into for (i = 0; i < something; i += 1)
{
.
The last thing is to add jslint checking to Hudson. Continuous integration should also mean continuous checking of your javascript code. I just added a manual “shell” step that calls the above jslint-running python script with the correct path. The script does a sys.exit() with a non-zero value if something goes wrong, so if you return that exit code to hudson, hudson will treat it as a full-blown test failure.
It really helped me to get a good feeling about the health of my javascript files!
My name is Reinout van Rees and I program in Python, I live in the Netherlands, I cycle recumbent bikes and I have a model railway.
Most of my website content is in my weblog. You can keep up to date by subscribing to the automatic feeds (for instance with Google reader):