Automatically format JavaScript commits using Prettier, Husky and lint-staged
What we will accomplish.
Automatically run the code formatter Prettier against all relevant files currently staged ahead of being committed to your repository.
Equally important, our solution should ignore all files not currently staged and leave these untouched.
This will ensure you can spend more time checking actual code quality in pull requests by leaving the formatting to your tools.
If you want to learn more on setting up Prettier and ESLint then take a look at my previous article on the topic.
Install dependencies
First install the required dependencies.
npm install --save-dev husky lint-staged
Husky provides the ability to create git commit hooks allowing us to run a few commands as a precommit sequence. Perhaps worth noting that you will need a git repository (even empty) already initiated within the project prior to installing Husky as it will setup git hooks during installation.
Lint-staged on the other hand provides a way to pipe only the files currently staged for commit to Prettier as opposed to the entire codebase.
Update our npm scripts
At its simplest this is all you need to add to your package.json
to leverage these pre-commit hooks.
"scripts": {
"precommit": "lint-staged"
},
"lint-staged": {
"*.{js,jsx,json}": ["prettier --write", "git add"]
}
The precommit
script is handled by Husky and will run lint-staged (or any other command we provide) ahead of the actual commit.
Lint-staged then looks at its own configuration in the lint-staged
section of our package.json
.
In our example, all .js
, .jsx
, and .json
files will automatically be formatted by Prettier before adding these newly formatted file changes back into our list of staged files.
Why lint-staged?
You might wonder why we don't just use Husky to run a few npm scripts.
The key concept is that we only want to apply the code formatter to the list of files currently staged - nothing else. I've repeated that phrase a few times through this short tutorial because it's an important point to keep in mind.
You might already have an npm script to run prettier on your .js
and .jsx
files (similar to "format": "prettier --write '**/*.{js,jsx}'"
). If so, you might be tempted to add npm run format
into the lint-staged configuration instead of prettier --write
- but don't do this.
The npm script format
is running prettier against all files matching the '**/*.{js,jsx}'
pattern. This would mean lint-stage would run Prettier against your entire code base and not purely on files currently staged. To repeat - this setup has the side-effect of writing format changes to files not currently staged.
I mention the above as I have seen a few Husky / Prettier tutorials out there advocating the use of an npm script that is inadvertantly run against the entire codebase. Instead leverage lint-staged to pipe the matching files from the list of staged changes into the prettier command.
BONUS: Add testing to your precommits
Husky can also be hooked up to your testing to ensure only code that passes tests are eventually committed.
Here's the most basic example of adding tests as a precommit check in your package.json
"scripts": {
"test": "exit 0",
"precommit": "lint-staged && npm test"
},
"lint-staged": {
"*.{js,jsx,json}": ["prettier --write", "git add"]
},
Obviously, this is the most foolish test pattern in existence but you get the idea how you can add something like Jest, Mocha or any other test framework that is currently already in use in your application.
Further Reading
The lint-staged documentation has some further examples on how it can be used with different file types.
Prettier lists some alternatives to lint-staged to hook code formatting into your commits.