Linting JavaScript in 2015
When writing in whatever programming language, you will want to write something that looks consistent. It doesn't really matter how you write your code but it's important that you keep it consistent.
That may be easy when you're the only developer. It can be hard for a bigger team though to write in the same style. Some may prefer four instead of two spaces or even tabs over spaces (sigh)!
Linting helps you (and your team) setting up clear style guides for how the code should be written. It's actually even more than that since it can help you detect (mundane) errors while you code.
Short lint history for JavaScript
It all started with JSLint. It is pretty easy to configure, because there is no configuration! The rules are defined by Douglas Crockford from his JavaScript: The Good Parts. You install it and scan your files.
Next came JSHint which can be considered the successor to JSLint.
JSHint can be configured with a dotfile called .jshintrc
unlike JSLint. Here
you can define how many spaces, tabs, globals etc. It has some different
options you can set yourself. It's still somewhat limited since you
can't write plugins for it.
What these two have in common is they both check for style and syntax. If you are accessing an undefined variable they will tell you.
JSCS is only concerned about your style though. It will not check for syntax errors and warn you about them. If you are only caring about your code style this might be for you since it comes with a lot of presets.
Then came ESLint
What I recommend is ESLint. It offers more than JSHint and JSCS and even performs faster.1 What's really great about ESLint and why I recommend it is because you can use your own parser or extend it with plugins.
What does it mean to use your own parser? ESLint does all of ES62 but if
you're using Babel to transpile you might use some experimental
features. That's why Babel wrote their own parser to lint for all
valid Babel code. This mean you can instead make use of babel-eslint
to lint
your ES6/ES7 code.
We can even extend ESLint to better support JSX than what it currently does out-of-the-box. All it requires is to install the ESLint-plugin-React module and tell ESLint to use it. Now we have plenty of new specific JSX rules we can use.
This is what I find really great about ESLint.
Better code with ESLint
Should you have trouble following along then I have created a very simple example repository with the different code examples from this post.
By default ESLint will have all rules disabled. This is different compared to earlier versions where some rules would be enabled by default. We'll keep them all disabled now and later show how to enable the most sane defaults easily.
So let's lint some files. Be sure to have Node and npm (should come with Node) installed.
We will need a new directory for our project, so let's create one: mkdir test
&& cd $_
.
Rules can be defined as warnings or errors. The difference is the exit code.
Warnings will exit with 0
but errors will exit with 1
. This can be really
useful in combination with Git pre-commit or your build tools, as you can use
this to stop doing 'dirty' commits for example.
So let's define some rules and their severity:
- Error: We will indent with two spaces
- Error: No unused variables
- Warning: No alert
Keeping it simple for now.
Create index.js
with your favorite text editor and write some ugly code:
var unusued = 'I have no purpose!';
function greet() {
var message = 'Hello, World!';
alert(message);
}
greet();
Two wrongly indented lines, an unused variable and a message alert! ESLint is not going to like that.
So now we need our package.json
to install ESLint. Type npm init
and just
keep hitting Enter. Let's install it as a dev dependency: npm install
--save-dev eslint
or npm i -D eslint
.
ESLint will look for a file called .eslintrc
. That is where our rules will go
in, but we also have to define the environment in which our code will run. We
can assume this code will only run in the browser (because of alert
since it
is only available in the browser) so we will set the env to browser.
Create .eslintrc
and write:
{
"rules": {
"indent": [2, 2],
"no-unused-vars": [2, {"vars": "all", "args": "after-used"}],
"no-alert": 1
},
"env": {
"browser": true
}
}
Some rules take an array. The first value is the severity of breaking the rule. 0 means it is disabled, 1 is a warning, and 2 is an error. The second is (optional) options for that rule.
In "indent"
the second 2 is the number of spaces we will allow.
The "vars": "all"
has to do whether we only disallow unused local variables or
globals too. Local here being in new scopes (such as functions for example).
"args": "after-used"
means only the last parameter in a function is required
to be used.
We set "no-alert"
to 1 only since it has no options and we just want a
warning.
The browser env tells ESLint about document
and window
.
We can finally lint our file! Let's add it as a script to our package.json
so
we can easily lint in the future.
{
...
"scripts": {
"lint": "eslint .; exit 0"
},
...
}
You can now lint easily using npm with npm run lint
. You should get a nice
list of the errors and the warning we specified:
> [email protected] lint /Users/Lauritz/Developer/eslint-example
> eslint .; exit 0
index.js
1:5 error unusued is defined but never used no-unused-vars
4:5 error Expected indentation of 2 characters but found 4 indent
5:5 error Expected indentation of 2 characters but found 4 indent
5:5 warning Unexpected alert no-alert
✖ 4 problems (3 errors, 1 warning)
Note: we use ; exit 0
because we get errors and npm thinks something bad
is going on. Try remove it and re-run. You might not use this if you want to use
the exit code of ESLint for anything. Consider writing a separate script for
that. The eslint .
means we will lint every file (by default files with the
.js
extension) in this directory.
Let's fix this. Open up index.js
and make ESLint happy:
function greet() {
var message = 'Hello, World!';
alert(message);
}
greet();
Type npm run lint
again and you should see no messages! That's good, it means
ESLint isn't complaining anymore.
Be lazy and use others' style
You may have heard of Airbnb's style guide for JavaScript. It is a pretty decent style guide for writing ES6 (and they also have for ES5).
If you (mostly) agree with their guide you can use it as a base. You might go
through the rules or realize they already have written an
.eslintrc
file but it is filled with comments.
You can actually install their guide as a module for ESLint. Their module is
dependent on another parser though, the babel-eslint
I mentioned earlier so we
will need that as well (it is unfortunately also dependent on
eslint-plugin-react
even if you don't care about React).
npm i -D babel-eslint eslint-config-airbnb eslint-plugin-react
Now rewrite your .eslintrc
file with:
{
"extends": "eslint-config-airbnb"
}
That is it really. Now you will use their guide to lint and style check your own
projects. You can type npm run lint
again but now you will see an error and
warning.
This is because we are using var
that is discouraged in ES6 and should instead
be a const
. By default we shouldn't really keep alert
as it is mostly used
for debugging but it is only a warning.
Say you want to keep using var
(for whatever reason) and alert
, you can just
override their rules:
{
"extends": "eslint-config-airbnb",
"rules": {
"no-var": 0,
"no-alert": 0
}
}
Now ESLint should stop complaining.
Sane defaults
Want to start using ESLint but with some sane defaults? You can enable the recommended rules with:
{
"extends": "eslint:recommended"
}
It will turn all rules marked "(recommended)" from the rules on.
Automatically lint as you write
It was my plan to show how you can setup a text editor (Vim) to automatically lint your files as you write them, but I think this guide is already longer than I planned it to be.
Instead I will split it up into another post and hopefully be able to cover more text editors (in the future).
Ending notes
This tutorial showed you how linting can be done for JavaScript. This isn't new
or even exclusive to JavaScript though. Linting started with C with lint
and a
linter is available for most languages.
I am sure you can find a configurable linter for your other languages, that will as well help you write cleaner and more consistent code.
Setup a linter once and stop having arguments about code style with your team members. Agree on a standard and use ESLint to abide them.
Be sure to check out the repository for the different code examples from this post.
The post can be discussed at reddit.
Updated 2015/08/08: post has been updated to latest version of ESLint (v1.1 at the time of writing).
-
Have we decided on ES6 or ES2015? ↩