Linting JavaScript in Vim

Update - This post is no longer relevant. Just use ALE and ESLint!


Why Vim?

Because I have never really been able to get away from it. Even the best IDEs feel cluttered and heavy to me. I guess I prefer a Less-Integrated Development Environment for some reason; one that hides its clutter under an obtuse scripting language and staggering configuration files. It's weird. But I'm not the only one.

So far, trading some of the advanced tooling of good IDEs for vim's simplicity has been productive. But that's not to say that I don't like a good code toolchain. With vim, and especially when developing JavaScript, the single most important practice I have adopted is in-editor code linting. Besides forcing cleaner code, linting catches a surprising number of bugs. Doing so while still in the editor means less context switching (editor/browser/editor/browser) and streamlines development.

JSLint or: How I stopped worrying and learned to love Douglas Crockford

I'll skip the discussion about which JavaScript linting tool is best and just say that I use jslint. The more I have learned about JavaScript, the more I have come to trust the (many) opinions of JSLint's creator. Besides, as long as you trust the source, an opinionated linter is a good thing, not a bad thing. #lettheflamesbegin

Linting options in Vim

I have found two different ways of plugging jslint into vim - Jesse Hallet's jslint.vim and Martin Grenfell's Syntastic.

Verdict: I like Syntastic a little better

j slint.vim pros
  • quicker feedback; your buffer is linted on many different editor events, including switching between insert and normal mode
  • .jslintrc files; if you work on multiple jslinted projects that have different rules (or rather, break the rules in different ways), per-project .jslintrc files are really nice
syntastic pros
  • slower feedback; your buffer is linted when you save. At first I didn't like this, but I'm starting to think that this is a saner way to do it.
  • errors in the location list. When errors are detected, they can be listed in the vim location list. This is really helpful when changes result in linting errors outside of the viewing area. You can jump right to them.
  • linting for other languages

Installation

jslint.vim

As the README.md says, jslint.vim is no longer under active development. However, it still seems to work, and all it really needs is an up-to-date version of jslint. Here's how I got it working on OS X and Linux:

  1. Install the jslint npm module. We want to use a current version of jslint, not the one that is packaged with jslint.vim. It's important that the jslint npm module be installed in your module path. It might make sense for you to install it in the root directory of your project (and add it as a dependency to your package.json if you are tracking dependencies). Or you can install it somewhere else in your npm path, like $HOME/node_modules.

In your project root directory or in $HOME
npm install jslint
(optionally npm install jslint --save-dev to also add jslint as a dependency in your package.json)

  1. In a temporary directory: git clone https://github.com/hallettj/jslint.vim.git
  2. mkdir -p ~/.vim/ftplugin/javascript
  3. cp -r jslint.vim/ftplugin/javascript/jslint* ~/.vim/ftplugin/javascript/
  4. To make jslint.vim use the npm version of jslint, edit ~/.vim/ftplugin/javascript/jslint/runjslint.js. At the top of the file, change this block:
if (typeof require !== 'undefined') {
    print = require('util').puts;
    fs = require('fs');
    vm = require('vm');
    sandbox = {};
    res = vm.runInNewContext(fs.readFileSync(jslintCore), sandbox, jslintCore);
    JSLINT = sandbox.JSLINT;
} else {
    load('jslint-core.js');
}

to look like this

if (typeof require !== 'undefined') {
    print = require('util').puts;
    fs = require('fs');
    vm = require('vm');
    JSLINT = require('jslint');
} else {
    load('jslint-core.js');
}

Notice that we have simply defined JSLINT as require('jslint') and removed related variables.

  • A nice feature of jslint.vim is that it can load a .jslintrc file for project-specific options. It will look for this file in your home directory, your current working directory, and in the directory of the file being edited. Very useful if you working on multiple projects with different jslint rules. An example .jslintrc file:
/*jslint browser: true, nomen: true, indent: 2 */
/*global require, module */


Here we see jslint.vim in action. Notice the underline/highlighting and the status message explaining the error.

Syntastic

Syntastic can check the syntax of dozens of languages, many of which can use multiple linters or syntax checking tools. For JavaScript jslint, jshint, and others are supported. The vim docs are thourough and succinct, so check those out after you install. Here's how I got syntastic working on OS X and Linux:

  1. The syntastic jslint plugin makes use of the jslint executable. If you have administrator privileges on your system, do
    npm install -g jslint
    to install jslint at /usr/local/lib/node_modules and the executable in your $PATH (hopefully) at /usr/local/bin/jslint.

If you don't have the necessary permissions for a global npm install you can do
cd ~;
npm install jslint
which will install it at ~/node_modules/jslint.
You can get the executable in your path like this:
mkdir ~/.bin
ln -s ~/node_modules/bin/jslint.js ~/.bin/jslint
and then add
export PATH=$PATH:$HOME/.bin
to your .bashrc or .bash_profile.

  1. Install pathogen if you aren't already using it.
  2. cd .vim/bundle
    git clone https://github.com/scrooloose/syntastic.git
  3. If the jslint executable is in your $PATH, it should just work. Options can be passed to syntastic/jslint like this:
    let g:syntastic_javascript_jslint_conf="--browser --nomen --indent=2"
    If you need per-project options, you can use an autocmd in your .vimrc like this:
    autocmd BufNewFile,BufRead ~/Projects/my-project/* let g:syntastic_javascript_jslint_conf="--browser --nomen --indent=2"
    All JavaScript files under ~/Projects/my-project will be linted with those options.


Here we see syntastic in action. Notice the side column indicators, underline/highlighting, and the errors in the location list. Open the location list using the syntastic command :Errors.