Automating Code Quality in JavaScript with Husky and Lint-Staged
In JavaScript project development, maintaining code consistency and ensuring high-quality standards can be a challenge, especially in teams with multiple contributors.
Integrating automation tools into the workflow allows for the automatic execution of essential tasks such as formatting, code analysis (lint), and running tests before commits or pushes are made.
This article explores in detail how to configure Husky and Lint-Staged to reinforce code quality in your JavaScript projects.
What are Husky and Lint-Staged?
Husky is a native solution for managing Git hooks, which are scripts that run automatically on certain Git events (like before a commit or a push). Thanks to Husky, it’s possible to ensure that a series of essential checks and tasks are completed before changes are integrated into the repository.
On the other hand, Lint-Staged is a tool that allows you to run commands only on files that have been modified and are in a “staged” state. This results in a much more efficient process, as formatting and validations are applied only to the small set of affected files, instead of analyzing the entire codebase.
Why use Husky and Lint-Staged?
Using Husky and Lint-Staged brings multiple benefits to JavaScript application development:
- Quality Assurance: They ensure that the code complies with style and quality standards by automatically running linters, formatters, and unit tests.
- Error Prevention: By intercepting the commit or push process, errors are detected and corrected before they are integrated into the codebase, preventing problems in production.
- Workflow Optimization: Lint-Staged only analyzes modified files, which significantly reduces the execution time of validation tasks.
- Team Uniformity: They allow all contributors to follow the same standards, ensuring code consistency and maintainability.
Configuring Husky and Lint-Staged
Below is a detailed process for configuring Husky and Lint-Staged in a JavaScript environment, integrating fundamental tools like Prettier, ESLint, and Vitest.
Setting up the environment
Imagine we are working on a JavaScript project where it is essential to maintain clean and error-free code. To do this, we will configure the following scripts in the package.json
file:
- Prettier as a formatter: It will be responsible for applying a consistent format throughout the code.
npm run format
- ESLint as a linter: It detects errors and applies good coding practices.
npm run lint
- Vitest as a testing framework: It validates that the code works as expected through unit tests.
npm run test
These commands will be integrated into our Git hooks so that, before each commit, the code is formatted, analyzed, and tested.
Installing and configuring Husky
Run the following command to install Husky as a development dependency:
npm install --save-dev husky
Initialize Husky in your repository (remember that you must have previously initialized Git in the project):
npx husky init
This command automatically adds a preparation script in the package.json
and creates a .husky
folder in the root of the project that will contain, among others, the pre-commit hook.
Configuring the pre-commit hook: Edit the .husky/pre-commit
file to include the necessary commands. For example:
npm run format
npm run lint
npm run test
With this configuration, every time you make a commit, Husky will execute these commands. If any of them fail, the commit process will be stopped, allowing you to correct the errors before proceeding.
Installing and configuring Lint-Staged
Add Lint-Staged as a development dependency:
npm install --save-dev lint-staged
Add the following script in your package.json
file to facilitate the execution of Lint-Staged:
"scripts": {
"lint-staged": "lint-staged"
}
Create a file called .lintstagedrc.json
in the root of the project and define the commands to be executed for the modified files. For example:
{
"*.{css,scss}": ["npx prettier . --write"],
"*.{js,jsx,ts,tsx}": [
"npx prettier . --write",
"eslint --fix",
"vitest run --environment=jsdom"
]
}
With this configuration, Lint-Staged will apply Prettier, ESLint, and Vitest only to the JavaScript files (and their variants) that have been modified and added to the staging area.
To take advantage of Lint-Staged in the pre-commit hook, modify the .husky/pre-commit
file to execute the Lint-Staged script instead of running the commands individually:
npm run lint-staged
This way, the validation process is optimized by focusing only on the changes made, saving time and resources.
Practical example with the configuration ready
Below, I leave you an example in which a change is added to the staging area, allowing us to observe in detail the step-by-step execution of each of the configured rules.
git add -A && git commit -am "Add test tutorial"
> catalitico-one-page @0.0.0 lint-staged
> lint-staged
✔ Preparing lint-staged...
⚠ Running tasks for staged files...
❯ .lintstagedrc.json — 1 file
↓ *.{css,scss} — no files
❯ *.{js,ts,tsx,vue} — 1 file
✔ npx prettier . --write
✖ eslint --fix [FAILED]
↓ Skipped because of errors from tasks.
✔ Reverting to original state because of errors...
✔ Cleaning up temporary files...
✖ eslint --fix:
/home/andrey/Xuma/catalitico-one-page/src/components/utils/Reveal.tsx
10:7 error 'a' is assigned a value but never used @typescript-eslint/no-unused-vars
✖ 1 problem (1 error, 0 warnings)
husky - pre-commit script failed (code 1)
When executing git add -A && git commit -am "Add test tutorial"
, Husky activates the pre-commit hook.
Lint-Staged starts and processes only the files that have been modified. In this case, the process attempts to:
- Format the file with Prettier (
npx prettier . --write
), which is done correctly. - Run ESLint with the automatic correction option (
eslint --fix
), but an error is detected in theReveal.tsx
file (a variable is assigned but never used).
Due to this error, the execution of the tasks is stopped, the state of the modified files is reverted, and the commit is canceled.
Conclusions
The integration of Husky and Lint-Staged in a JavaScript project is a highly effective strategy to ensure the quality and consistency of the code.
By automating fundamental tasks such as formatting, static analysis, and test execution, these tools allow for the early detection of errors and ensure that the code integrated into the repository complies with established standards.
Although the initial configuration may seem complex, the long-term benefits such as error reduction, a more agile development process, and greater team uniformity amply justify the investment of time and effort.
The implementation of Husky and Lint-Staged not only improves the quality of the final product but also promotes good practices and optimizes the workflow, allowing you to concentrate efforts on creating robust and scalable solutions in JavaScript.
I hope you find this guide useful and that it encourages you to incorporate these practices into your projects. Success in your developments!
Thanks for reading.