Consistent JavaScript Code

Some time ago I had a dream. I was creating a new project. I was thinking about all cutting-edge technologies I could use in it. My team was very small at the beginning. Everything was going smoothly, we were developing fast and without problems, it was great. Somewhere along the way new people started joining us and they brought gifts. But the gifts were treacherous, they were different coding standards and formattings. After some time I saw that from our perfectly organized code a new entity started emerging. It was a creature that could have had its origin in one of Lovecraft's stories. In its left hand it was holding 4 spaces and in the right one 1 tab. One of its eyes was a semicolon while the other was looking at me with empty void. I woke up screaming. I couldn't sleep for a week after that incident and the creature still haunts me in my dreams to this day.

I'm pretty sure a lot of developers had an experience similar to mine. We all know that reading code which has different formattings and standards implemented is hard and tiring. In this tutorial we will learn how to stop this nightmarish beast from rising and how to fight it, if it's too late for prevention. We will focus on JavaScript today, but if you are interested in PHP make sure to check this great post from Maciek.

Linters

Linters are very handy tools. They can analyse the code and compare it with a provided configuration. They look for places where our code doesn't follow the defined styleguide, let us know about such places and can even fix them.

We will use one of the most popular JS linters - ESLint - combined with a styleguide made by Airbnb.

Setting up

To be able to use ESLint and test some code, first we need to create a new project using:

$ npm init

We are now able to install ESLint:

$ npm install eslint --save-dev

Configuring ESLint

We have ESLint installed in our project, but we need to configure it to use the desired styleguide. Fortunately there is a nice way to set it up:

$ ./node_modules/.bin/eslint --init

The configurator will ask us a couple questions ("❯" shows the selected answer):

? How would you like to use ESLint?
  To check syntax only
  To check syntax and find problems
❯ To check syntax, find problems, and enforce code style
? What type of modules does your project use?
  JavaScript modules (import/export)
  CommonJS (require/exports)
❯ None of these
? Which framework does your project use?
  React
  Vue.js
❯ None of these
? Where does your code run? (Press <space> to select, <a> to toggle all, <i> to invert selection)
❯◉ Browser
 ◯ Node
? How would you like to define a style for your project? (Use arrow keys)
❯ Use a popular style guide
  Answer questions about your style
  Inspect your JavaScript file(s)
? Which style guide do you want to follow? (Use arrow keys)
❯ Airbnb (https://github.com/airbnb/javascript)
  Standard (https://github.com/standard/standard)
  Google (https://github.com/google/eslint-config-google)
? What format do you want your config file to be in? (Use arrow keys)
❯ JavaScript
  YAML
  JSON

After these questions ESLint will ask us to install additional packages (of course we answer "yes") and create a new file - .eslintrc.js. In my case it looks like this:

module.exports = {
  env: {
    browser: true,
    es6: true,
  },
  extends: 'airbnb-base',
  globals: {
    Atomics: 'readonly',
    SharedArrayBuffer: 'readonly',
  },
  parserOptions: {
    ecmaVersion: 2018,
  },
  rules: {
  },
};

Linting the code

Before we use ESLint, we need to have some code to test. You can use it on any real project, but I've created an example file just for this purpose. You can download it here.

Now it's time for linting:

$ ./node_modules/.bin/eslint .

Whoa! That's a lot of errors! ESLint just checked all the files and showed us the lines that don’t meet the Airbnb styleguide requirements. In my case it looks like this:

   1:8   error    Unable to resolve path to module 'whatwg-fetch'  import/no-unresolved
   2:8   error    'foo' is defined but never used                  no-unused-vars
   2:17  error    Unable to resolve path to module './foo'         import/no-unresolved
   4:1   error    Unexpected var, use let or const instead         no-var
   5:1   error    Unexpected var, use let or const instead         no-var
   5:5   error    'b' is assigned a value but never used           no-unused-vars
  10:1   error    Expected indentation of 2 spaces but found 4     indent
  11:1   error    Expected indentation of 2 spaces but found 4     indent
  13:1   error    Expected indentation of 2 spaces but found 4     indent
  14:1   error    Expected indentation of 2 spaces but found 4     indent
  16:1   error    Expected indentation of 2 spaces but found 4     indent
  17:1   error    Expected indentation of 2 spaces but found 4     indent
  21:1   error    Expected indentation of 2 spaces but found 4     indent
  22:1   error    Expected indentation of 2 spaces but found 4     indent
  23:1   error    Expected indentation of 4 spaces but found 8     indent
  24:1   error    Expected indentation of 2 spaces but found 4     indent
  24:12  error    Unnecessary 'else' after 'return'                no-else-return
  25:1   error    Expected indentation of 4 spaces but found 8     indent
  26:1   error    Expected indentation of 2 spaces but found 4     indent
  29:10  error    'run' is defined but never used                  no-unused-vars
  30:1   error    Expected indentation of 2 spaces but found 4     indent
  30:5   warning  Unexpected console statement                     no-console
  31:2   error    Newline required at end of file but not found    eol-last

✖ 23 problems (22 errors, 1 warning)
  17 errors and 0 warnings potentially fixable with the `--fix` option.

Each error on the list consists of line number, description and the rule's name. The full list of rules can be found here.

We have a lot of "indent" errors. It makes sense, because Airbnb styleguide advises to use 2 spaces for indentation. The thing is I like to use 4 spaces and don't want to change it. So let's tweak the configuration a little bit.

Changing the configuration

In order to use a different indentation we need to open the configuration file and overwrite the default rule. The documentation is very helpful in showing us how exactly we can change different settings. Here's the "indent" rule.

We just need to add our rule to the .eslintrc.js file:

rules: {
  'indent': ['error', 4]
},

After running the linting command again we can see that a lot of errors has been resolved:

   1:8   error    Unable to resolve path to module 'whatwg-fetch'  import/no-unresolved
   2:8   error    'foo' is defined but never used                  no-unused-vars
   2:17  error    Unable to resolve path to module './foo'         import/no-unresolved
   4:1   error    Unexpected var, use let or const instead         no-var
   5:1   error    Unexpected var, use let or const instead         no-var
   5:5   error    'b' is assigned a value but never used           no-unused-vars
  24:12  error    Unnecessary 'else' after 'return'                no-else-return
  29:10  error    'run' is defined but never used                  no-unused-vars
  30:5   warning  Unexpected console statement                     no-console
  31:2   error    Newline required at end of file but not found    eol-last

✖ 10 problems (9 errors, 1 warning)
  4 errors and 0 warnings potentially fixable with the `--fix` option.

Not that bad as before, but our code can still look better.

Automatic fixing

You've probably noticed a small message below the errors list - "4 errors and 0 warnings potentially fixable with the --fix option". What a nice guy this ESLint! Can it be true that it want's to fix our errors for us? Well, we need to find out. Go ahead and add the suggested --fix option:

$ ./node_modules/.bin/eslint example.js --fix

Wow, it really did fix our code! Not everything, but it's a great feature. It can save us a lot of time. The code is still not perfect though.

Fixing the code manually

In the ESLint output we can see that there are two errors, because it was unable to resolve paths to some modules. We need to investigate it. The modules are on top of the file:

import 'whatwg-fetch';
import foo from './foo';

It looks like we don't have these modules in the project. Moreover, we don't even use them. It should be safe to remove them.

Awesome! Our list of errors is getting smaller and smaller:

   2:7   error    'b' is assigned a value but never used  no-unused-vars
  25:10  error    'run' is defined but never used         no-unused-vars
  26:5   warning  Unexpected console statement            no-console

As an exercise try to resolve the remaining errors by yourself 🙂

When we see no errors it means our work is done.

Conclusion

By using tools like ESLint we are able to enforce the same style of writing code in the whole project. Therefore, making the code style consistent and easier to look at.

Using such tools brings many benefits, for example, it saves time by eliminating the need of checking for code formatting issues during code reviews and improves overall readability. Also, when the project grows and new developers need to start coding, it is easier for them to follow the rules if they are the same over the whole project.

I encourage everyone to try it and explore overriding or even creating custom rules, depending on your preferences.

Co-authors of the post: Tomasz Kulka and Konrad Jaguszewski.

FacebookTwitterPinterest