On-the-fly jslint validation in emacs

In the hope that someone will come across this trying not to waste time figuring things out, here’s how I managed to have emacs run jslint in my JavaScript buffers in real time. It’s not perfact, but it works where no other method I found online did.

We’ll use the jslint package from node.js1:

1. Install node.js. I use the homebrew package manager on OSX, so I just had to type brew install node.

2. Install the jslint module of node.js: npm install jslint

After this you should have a jslint command which you can test in a terminal (eg, jslint --terse test.js) to see something that looks like:

18n.js(2):Expected 'en' at column 9, not column 3.
i18n.js(3):Expected 'record' at column 13, not column 5.

By default (without --terse) the output is more verbose, but having one line per error is easier in emacs.

3. Add the following to your .emacs file:

(when (load "flymake" t)
  (defun flymake-jslint-init ()
    (let* ((temp-file (flymake-init-create-temp-buffer-copy
           (local-file (file-relative-name
                        (file-name-directory buffer-file-name))))
      (list "jslint" (list "--terse" local-file))))

  (setq flymake-err-line-patterns
	(cons '("^\\(.*\\)(\\([[:digit:]]+\\)):\\(.*\\)$"
		1 2 nil 3)

  (add-to-list 'flymake-allowed-file-name-masks
               '("\\.js\\'" flymake-jslint-init))

  (require 'flymake-cursor)

(add-hook 'js2-mode-hook
	  (lambda ()
      (flymake-mode 1)
      (define-key js2-mode-map "\C-c\C-n" 'flymake-goto-next-error)))

flymake-cursor replaces the default behaviour of popping up a menu with the error message by just putting it in the minibuffer.

Emacs should of course be set up so that it knows where jslint is, in order to invoke it on the javascript buffers3.


1. I also tried jslint4java but it turned out to be too slow for real-time invocation with flymake. Other Java-based methods (using Rhino) are, unsurprisingly, reported to be equally slow, and I failed to get spidermonkey installed.

3. In OS X, applications get their environment variables (like PATH, here) from the environment.plist in ~/.MacOSX. See this emacswiki page for instructions.

[updates 9 Oct 2012]:

  • Step 2 is no longer necessary, as npm is now included in homebrew’s node package
  • At step 3, removed -g
  • With the --terse option, it is no longer necessary to modify files inside the jslint module.
  • The lisp code has been updated to include --terse and match the new jslint error syntax

Thanks to the people who commented, leading to the update.

This entry was posted in General. Bookmark the permalink.