Stop arguing about code style in pull requests!

How to use git hooks with code formatter

Many teams are familiar with the situation such as arguing in pull requests about how to place brackets, indent, and how to format code. This results in a waste of a lot of time and energy. It can also lead to conflict situations. If we can automate something, we should automate it. What tools can we use for this? Two are enough:

  1. lefthook
  2. prettier

Let's take a look at how to use them using an example with React app.

What is Lefthook?

As documentation says, lefthook is a “fast and powerful Git hooks manager for Node.js, Ruby or any other type of projects”. What does it mean? This means that you can set up some commands to be executed when you do, for example, git commit or git push.

In our example, we will set up autoformatting to run before the commit. Also, you can add, for instance, running tests before a commit. If some command fails (for example, tests failed), then the changes will not be committed.

What is Prettier?

It is an “opinionated” code formatter. It supports many languages.

Setup a test app

  1. create a test application

     npx create-react-app lefthook-prettier-example 
     cd lefthook-prettier-example
    
  2. install prettier

     npm install --save-dev --save-exact prettier
     # or yarn:
     yarn add --dev --exact prettier
    
     # Then, create an empty config file to let editors and other tools know you are using Prettier:
     touch .prettierrc.js
    
  3. open .prettierrc.js and specify the config object. You can see all the options here and specify the ones that suit you

     module.exports = {
       singleQuote: true,
       jsxSingleQuote: true,
       arrowParens: 'always',
       printWidth: 120,
       tabWidth: 2,
       useTabs: false,
       semi: false,
       endOfLine: 'auto',
     };
    
  4. you can optionally assign formatting when saving the file (prettier.io/docs/en/editors.html).

  5. if you configured formatting on saving, then check that everything works. To do this, open the src/App.js file in the IDE, make some changes, and click Save.

    Instead of this:

     import logo from './logo.svg';
     import './App.css';
    
     function App() {
       return (
         <div className="App">
           <header className="App-header">
             <img src={logo} className="App-logo" alt="logo" />
             <p>
               Edit <code>src/App.js</code> and save to reload.
             </p>
             <a
               className="App-link"
               href="https://reactjs.org"
               target="_blank"
               rel="noopener noreferrer"
             >
               Learn React
             </a>
           </header>
         </div>
       );
     }
    
     export default App;
    

    we get the formatted code:

     import logo from './logo.svg'
     import './App.css'
    
     function App() {
       return (
         <div className='App'>
           <header className='App-header'>
             <img src={logo} className='App-logo' alt='logo' />
             <p>
               Edit <code>src/App.js</code> and save to reload.
             </p>
             <a className='App-link' href='https://reactjs.org' target='_blank' rel='noopener noreferrer'>
               Learn React
             </a>
           </header>
         </div>
       )
     }
    
     export default App
    

    NOTE: If you specify other options in the config, the result may differ.

  6. install lefthook

     npm i @arkweid/lefthook --save-dev
     # or yarn:
     yarn add -D @arkweid/lefthook
     # NOTE: if you install it this way you should call it with npx or yarn for all listed examples below. (for example: lefthook install -> npx lefthook install)
    
     # You can also install lefthook as a global dependency
     npm install -g @arkweid/lefthook
    
  7. create and configure hooks for lefthook

     # Initialize lefthook with the following command (it creates lefthook.yml in the project root directory)
     npx lefthook install
    
     # Register your hook (You can choose any hook from https://git-scm.com/docs/githooks). In our example it pre-commit githook:
    lefthook add pre-commit
    
  8. open the lefthook.yml config file and add the commands to be executed before the commit

     pre-commit:
       parallel: true
       commands:
         prettier:
           glob: 'src/*.{js,ts,jsx,tsx}'
           run: npx prettier --write {staged_files} && git add {staged_files}
    
  9. to check, turn off auto-formatting on saving. Next, make changes to src/App.js and do a git commit

     git add -A
     git ci -m 'Add lefthook and prettier'
    
     RUNNING HOOKS GROUP: pre-commit
    
       EXECUTE > prettier
     src\App.js 50ms
    
     SUMMARY: (done in 0.75 seconds)
     ✔️  prettier
     [master 4576031] Add lefthook and prettier
    
  10. Check that src/App.js is re-formatted.

Conclusion

In this article, we used lefthook for the pre-commit hook. But there are other alternative tools like pre-commit or husky. They all have similar capabilities, and if you understand one of them, you can deal with the rest.

You can download the final result here.