hungryturtlecode

Hungry Turtle Code Logo

Please share this post if you enjoy it!

Please note that the video is a little out of date now - the lint-staged config used in it is wrong. This article has been updated to reflect the correct config

Javascript Tooling

If we are maintaining an open source project we have to think about the fact that there will likely be other people from all over the world who could contribute to the project and they all have different levels of ability and different coding preferences. That is absolutely fine, but we still want to strive for consistency in our code.

Last thing we want to see is parts of the code using one coding convention while another uses a totally different convention. One obvious example of this is tabs vs spaces, which we fixed by adding the .editorconfig when we wrote the library code.

Unfortunately, there are many other examples of such conventions that can differ - semicolons, trailing commas, line breaks etc just to name a few.

So to fix this we will use two tools called eslint and prettier. eslint is a great tool where we define certain “rules” about coding convention and style and eslint will check to ensure the code conforms to those rules.

Prettier is a formatting tool that will format our code in a consistent way every time we run the tool. Things are no longer up to personal preference, we just decide on a convention, put it into a prettier config and let prettier handle it.

Using these two tools results in super consistent code style and formatting.

ESLint

First we will install eslint and the airbnb (yes the company you book a house to stay in) base configuration for eslint. We use this base config because it has a lot of good defaults to start with and saves us from having to configure everything manually.

We will also install a plugin that will work well with prettier (specifically it will stop eslint from shouting at us for things prettier will fix for us).

1
yarn add --dev eslint eslint-config-airbnb-base eslint-config-prettier eslint-plugin-jest

Then we will need to create an .eslintrc configuration file. Put the following into it:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
{
 "extends": ["airbnb-base", "prettier"],
  "parserOptions": {
    "ecmaVersion": 2018,
    "sourceType": "module"
  },
  "env": {
    "browser": true,
    "node": true,
    "es6": true,
    "jest": true,
  },
  "plugins": [],
  "rules": {
    "no-unused-vars": [
      "error",
      {
        "vars": "local",
        "args": "none"
      }
    ],
    "no-plusplus": "off",
    "no-underscore-dangle": "off",
  }
}

Here we tell it to use the airbnb configuration and the prettier configuration defaults and supply it will a few extra rules in the “rules” property. These are just a few that I like to use. You can see a whole list of rules for eslint here.

It will also install the eslint plugin for jest that will allow eslint to be aware of jest syntax (when we use jest: true in the env of our .eslintrc). If we don’t install that plugin eslint will shout at us that the jest global functions like expect() and test() don’t exist. So it’s a pretty good idea to add it.

You can also create an .eslintignore to tell eslint to ignore certain files, this can be really useful.

Prettier

Prettier is a simple one to install.

1
yarn add --dev prettier

Just like eslint we also need a config for prettier - this time it is .prettierrc. This is the one I use:

1
2
3
4
5
6
7
8
9
10
11
{
  "printWidth": 80,
  "tabWidth": 2,
  "useTabs": false,
  "semi": true,
  "singleQuote": true,
  "trailingComma": "all",
  "bracketSpacing": false,
  "jsxBracketSameLine": false,
  "proseWrap": "always"
}

Again, you can also create an .prettierignore to ignore files from being prettified. These are the files / folders I ignore:

1
2
3
4
dist
node_modules
coverage
build

And that’s all we need to do to get prettier into our project.

But how do you enforce these tools?

A valid thing to say at this point is,

“it’s all well and good having eslint and prettier to enforce code style but how to you ensure everyone who contributes runs the tools?”.

Enter git hooks using husky and a beautify tool called lint-staged.

Git hooks are an amazing tool that are all too often over looked. They are built into git and allow you to trigger scripts when during certain events in the git lifecycle. For our use case we will be using a git hook called “pre-commit”. This will trigger a script every time we try to commit code and if the script fails it won’t allow us to commit the code.

Setting up git hooks manually can be a pain and it’s difficult to distribute them through source control. Husky is a tool that allows us specify certain scripts to run as git hooks in our package.json file. Husky takes care of actually setting up the hooks and running our scripts, we just tell it when to run and what to run. Wonderful.

Lint-staged is a tool that allows us to run linters and other such tools against only the files that are currently staged on git (ie the files we have “git added”).

We will use lint-staged to run eslint, prettier and run our jest tests against the files that have been staged. Lint staged will be triggered to run by husky as a pre commit hook.

So the workflow looks like this:

  • Make changes to the code
  • git add the changed files
  • git commit the changed files
  • Before the code is actually committed husky will trigger lint-staged
  • Lint staged will run eslint, prettier and jest against all the staged files
  • If anything fails the commit won’t go through, if everything passes ok then the code is committed.

This is a no pain way to ensure that all the linters, formatters and tests are happy with the code before it ever makes it’s way into the repo. Great for open source projects to maintain consistency.

First step to get this going is to install lint-staged and husky

1
yarn add --dev lint-staged husky

Then create a lint-staged.config.js and populate it will the following:

1
2
3
4
5
6
7
module.exports = {
  '**/*.+(js|ts)': [
    'eslint --fix',
    'prettier --write',
    'jest --findRelatedTests',
  ],
};

Here we are telling lint staged to look for any file that ends in any of the following (js|ts) and then run eslint, prettier and jest on them.

The final step is to actually get husky to run this on precommit. Open up your package.json and add the following.

1
2
3
4
5
6
7
8
9
10
11
12
{
  // Rest of package.json
  "scripts": {
    // rest of scripts
    "precommit": "lint-staged",
  },
  "husky": {
    "hooks": {
      "pre-commit": "yarn precommit"
    }
  }
}

We create a script called “precommit” (you can call this whatever you want) that runs lint-staged and we create a section called husky that calls yarn precommit during the “pre-commit” git hook.

Stay hungry, and keep coding.

Adrian

 

Please give this post a share if you enjoyed it. Everyone needs that awesome friend to send them amazing stuff.