Lint Like It’s 2015

Dan Abramov
7 min readFeb 27, 2015

If you write ES6 with Babel but JSHint holds you back, I have good news!

It’s ES6 and it lints fine.

I’m a static analysis person. I come from C# and I trust my tools to understand what I write and let me type absent-mindedly.

JSHint ignited my love to JavaScript in 2012 by making it not a total pain to write in. After I switched to React in 2014, JSXHint combined with SublimeLinter via SublimeLinter-jsxhint had my back. These tools have been indispensable and I’m grateful to their authors for a good year.

It wasn’t bad.

However, as my build workflow got more sophisticated, I had a falling-out with JSHint.

In early 2015, I switched from React’s JSX compiler to Babel in order to use ES6 (and some ES7) syntax freely. It’s a tad slower but …rest assured, it’s the future. I never regretted switching to Babel for a moment, and I only have the highest praise for its authors and contributors. My only pain was getting Babel code to lint.

Unlike React’s JSX Harmony mode, Babel supports { [key]: value } JSON syntax, let and const, for-of loops and generators.

See, JSXHint works by taking JSX sources, running them through React’s JSX transformer, and then feeding the result to JSHint. Not only was this slow due to the code being parsed twice, but I also could not use any Babel features that weren’t also supported by React’s JSX transformer. Doesn’t this defeat the purpose of adding Babel in the first place?

To be fair, JSXHint has a special mode that uses Babel’s transformer instead of React’s. Still, I found it unusable because Babel often generates additional lines for readable generated code but JSXHint doesn’t know how to preserve the line numbers, so they’re wildly off.

Enter ESLint.

If you haven’t heard of it, ESLint is the linter I always wanted JSHint to be. It’s smarter than JSHint, it’s completely customizable with custom linting rules and it doesn’t purposefully annoy you. Every warning and error also comes with the exact option you need to write into the configuration file to turn it off (or at least tone it down).

So ESLint is clearly what you want as a linter in 2015. It can even do JSX! Sadly it doesn’t support full ES6 quite yet. Are we going to live in the linter hell for a few more months?

Hell, no.

Sebastian McKenzie, creator of Babel, wrote babel-eslint. Did you know that ESLint supports pluggable parsers? I didn’t. Well, turns out that it does, and Sebastian found a way to make ESLint parse (and lint) all valid Babel code.

It’s just been released and has two or three bugs on my codebase, but I’m sure they will be fixed soon. I already switched to the new tool and suggest you do the same!

Follow these steps to integrate Babel and ESLint-powered ES6 linting into Sublime Text 3 or your build pipeline.

An Important Disclaimer

Only do this if you don’t mind a few false flags while the bugs are being fixed. On my 700-something module codebase, I have about 20 invalid flags caused by 4 babel-eslint bugs. I reported them all and fully expect them to be fixed soon! Since I don’t run hinting as a build step, this doesn’t affect my productivity, but you may want to wait out a little bit.

Installing eslint and babel-eslint

Start by creating a file called .eslintrc in the root of your project. It might look like this:

// I want to use babel-eslint for parsing!
"parser": "babel-eslint",
"env": {
// I write for browser
"browser": true,
// in CommonJS
"node": true
// To give you an idea how to override rule options:
"rules": {
"quotes": [2, "single"],
"eol-last": [0],
"no-mixed-requires": [0],
"no-underscore-dangle": [0]

Saved it? Good.

Install eslint and babel-eslint NPM packages locally to your project. (Yes, an earlier version of this tutorial said “globally” but local installation works for me now, and it’s a way better idea.) Having different versions of global and local eslint will only confuse you so I suggest you uninstall global eslint if you have one.

npm uninstall -g eslint # uninstall the global if you have itnpm install --save-dev eslint@latest
npm install --save-dev babel-eslint@latest

Make sure you can run the local eslint:

./node_modules/.bin/eslint -v

It should output a version (mine says 0.20.0). Note that local eslint won’t work if it can’t find babel-eslint next to it in your node_modules.

Running ./node_modules/.bin/eslint src (or whatever your source directory is) now should parse your sources with Babel. It would be a good idea to alias this as npm run lint by putting this into your package.json:

  "scripts": {
"lint": "eslint src"

If you don’t use Sublime, you should be all set.
If you do, read on!

Configuring Sublime

If you started reading from this section because you have already set up ESLint before, make sure it’s installed globally and available in PATH. Still better, do read the previous section, it will save you configuration woes.

You probably already have Package Control by Will Bond if you use Sublime Text. If you don’t, install it so you can manage Sublime plugins easily.

Install SublimeLinter package via Package Control. This guide has you covered. SublimeLinter is a generic package, it works with many languages and doesn’t include any specific linter. You’ll need to also install SublimeLinter-contrib-eslint via Package Control. See this guide. If you already have it, make sure to update to a recent version.

While we’re at it, make sure you uninstall (or disable if you know how to do that) any other JavaScript linter plugins for SublimeLinter. I just removed SublimeLinter-jshint and SublimeLinter-jsxhint and suggest you do the same.

After linter plugins are installed, we need to teach Sublime to recognize ES6 syntax and highlight it correctly. For that, I suggest you uninstall any existing JSX-specific Sublime plugins first. If you use anything like sublime-react, remove this package with Package Control. Make sure you have no “alternative” JavaScript or JSX syntax packages in Sublime’s lower right corner Set Syntax menu.

Done? Install babel-sublime. It’s a JavaScript code syntax for Sublime based on JavaScriptNext, but also including JSX and Flow support. It’s definitely much better than the language file shipping with sublime-react so go ahead and switch. After you’re done install babel-sublime, make sure you choose it as the default syntax twice: both for JS files and for JSX files. You want all of them to work with ES6, right?

I don’t like the default color schemes in babel-sublime so I suggest you install and use oceanic-next-theme which is neat and optimized for it.

We’re almost there! Now you need to tell SublimeLinter that “JavaScript (Babel)” syntax you just installed as part of babel-sublime and chose as default for JS and JSX files, is actually a variation of JavaScript, so SublimeLinter-contrib-eslint recognizes it and turns on for JS and JSX files.

To do this, choose SublimeLinter Settings — User in command menu:

You need to have an entry in syntax_map config entry that matches “javascript (babel)” syntax to use the current javascript linter (which will happen to be ESLint if you followed previous instructions).

Here’s how you add it to syntax_map:

Boom! Done! Restart Sublime and open a JS(X) file. Write some nonsense on purpose. Hit “Save”.

Depending on your SublimeLinter configuration, it will be linted immediately, while editing or on save.

You can configure this by using Choose Lint Mode command:

Happy linting in 2015!

If You Use React Too…

Make sure you use include some rules from eslint-plugin-react! Some of them help ESLint understand the semantics of React JSX usage, and others are just nice additional static checks specifically for React code.


Follow Dan Abramov on Twitter



Dan Abramov

Working on @reactjs. Co-author of Redux and Create React App. Building tools for humans.