Linting staged files before committing with Husky and lint-staged

Linting staged files before committing with Husky and lint-staged

How to lint files before committing them to a git repository using husky and lint-staged in Javascript projects!

ยท

4 min read

Everyone working with javascript knows that linting is incredible. It makes discussions about code style much more manageable. It also omits unnecessary fights about code styles and structure from pull request reviews.

The problem

I have seen in many projects that people do not use their linters or do manual linting (a.k.a, correcting the code by themselves). Sometimes people also have different linters setup in their code editors, which causes pain, and for every line of code changed in a git commit, there are 100 lines of linted lines.

The Solution

The solution is two added packages to your package.json, those are husky and lint-staged.

Husky is a Git hook library for Javascript. These hooks run programs during a git command execution. There's a bunch of hooks to read more about it check here. We'll be using pre-commit, which runs a command before committing. It also has a cute name!

image.png

Lint-staged is an npm library that runs your linter only on files staged with git. It is very beneficial since it doesn't run on stuff you didn't touch. It becomes even better with legacy projects, where sometimes it's forbidden by the lords of the deep state to edit certain files.

Let's give it a go!

Bootstrap the app

We'll be using create react app for simplicity since it bootstraps a project, but these steps apply to any node.js project.

Create the app with npm/yarn:

$ npx create-react-app linting-tutorial
# or
$ yarn create react-app linting-tutorial

Navigate to the directory:

$ cd linting-tutorial

Next, add the dependencies:

$ npm install --save husky lint-staged prettier
#or
$ yarn add husky lint-staged prettier

We'll be using prettier as our linter, but feel free to use whichever you prefer.

Editing package.json

Add lint-staged to package.json. This command tells the package.json on which files this should run and what it should run. In our case, we want to run prettier --write to all js, jsx, ts, tsx, json, css, scss, md files that are inside of our ./src folder:

"lint-staged":{
  "src/**/*.{js,jsx,ts,tsx,json,css,scss,md}": [
      "prettier --write"
  ]
},

Run the command to create our husky pre-commit hook:

$ npx husky add .husky/pre-commit "yarn lint-staged"

This command should create a file called .husky/pre-commit

#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

yarn lint-staged

Let's test it out

After editing our ./src/App.js file looks like this:

import './App.css';

function App() {
  return (
    <div className="App">
      <header className="App-header">




        <a

     className="App-link"
          href="https://github.com/typicode/husky"
               target="_blank" rel="noopener noreferrer"
       >Learn more about husky 
        </a>
<a
          className="App-link"
          href="https://github.com/okonet/lint-staged"
          target="_blank"
          rel="noopener noreferrer"
        >
          Learn more about lint stage
        </a>
      </header>
    </div>
  );
}

export default App;

I know no one in his right mind would even write this kind of code, so calm down, it's only for demonstration purposes!

Let's stage our changes to git and commit them:

$ git add .
$ git commit -m "feat: Added links to husky and lint-staged GitHub pages"

After a couple of seconds, you should see something like this:

image.png

Now, if we look back into our ./src/App.js, we can see a much prettier (pun intended) file:

import "./App.css";

function App() {
  return (
    <div className="App">
      <header className="App-header">
        <a
          className="App-link"
          href="https://github.com/typicode/husky"
          target="_blank"
          rel="noopener noreferrer"
        >
          Learn more about husky
        </a>
        <a
          className="App-link"
          href="https://github.com/okonet/lint-staged"
          target="_blank"
          rel="noopener noreferrer"
        >
          Learn more about lint stage
        </a>
      </header>
    </div>
  );
}

export default App;

So right before committing, husky kicked in, which ran the lint-staged command, to run prettier on every staged file!

Pretty cool right?

One thing to keep in mind is, if our listing command fails, then the commit will not go through. This adds a bunch of cool capabilities for security. One example is adding (commitlint)[github.com/conventional-changelog/commitlint], which checks if your commit title and description are properly structured!

Conclusion

In conclusion, what we did is the following: We have created an app, then added husky, lint-staged, and prettier. After that, we changed the App.js file to be something a person should never commit! We staged those changes and our linter made our code pretty again!

If you liked this, leave a couple of Emojies here or if you want to connect with me, hit me up on Twitter!

Thanks for reading ๐Ÿ‘‹

ย