How to set up husky & lint-staged to limit bugs in your codebase?

Article describes how to setup lint-staged and husky in the project, forcing developers to push the code that is free of simple issues and coding smells.

In this article, we'll discuss how to setup lint-staged and husky in the project, forcing developers to push the code that is free of simple issues and coding smells.


Why to use lint-staged and husky?

In an ideal world, everyone on the team would have a unified environment that catches errors as code is written. In such a world, every team member would address these errors promptly since they'd already be flagged as well. Unfortunately, the reality often falls short of this ideal.

There may be obvious reasons for this. One is the environment differences - something that works for one developer may cause unexpected problems for others. Another common thing is the attitude of some people who, even when they see errors with a clear indication of "here’s an error" don’t feel the need to take any action. Simple laziness plays a role here too.

Even when the team consistently improves code quality, regularly discusses common coding smells, and tries to keep unified environments, the same problems may still occur no matter how hard the team tries to avoid them. So, instead of wasting time repeatedly discussing the same issues over and over, it can be more effective to enforce standards before code is pushed to the repository. This is where tools like lint-staged and husky come to the rescue.

Lint-staged and husky are tools that help ensure that code with errors or issues that breaks defined project standards can not be committed to the repository.


Before starting, ensure you have tools like ESLintStylelint or PHPCS set up in your project to verify coding standards, as we'll be working with husky and lint-staged for this purpose. If you're unsure what they are or how to set them up, please refer to the basic guides to start.

ESLint: Setting Up From Start to Finish

In this article, I'll discuss ESLint, the tool that ensures your JavaScript code remains consistent and clean. I'll guide you through setting it up, customizing its behavior, and integrating it with Prettier and VS Code.

Stylelint: CSS Linter You Must Know As A Frontend

Let me show you Stylelint, a tool for anyone working with CSS or SCSS, making sure the code doesn't go off the rails. It identifies errors, and rule violations, suggests fixes, and sometimes solves issues for you.

PHP Code Sniffer: In-Depth Tutorial for Developers

Learn how to integrate PHP Code Sniffer into your development workflow and create coding standards tailored to your needs. Discover how it works, from its core functionalities to its integration with Visual Studio Code and don't miss any problems ever again.


When to use lint-staged and husky?

To illustrate the ideas behind these tools, let's intentionally add a few issues to the codebase. For styles, we'll use the incorrect selector, which should be the lowercase "a." For scripts, we'll use double quotes instead of single ones, and for PHP, we'll add wrong spacing in brackets.

A {
  text-decoration: none;
}
console.log("hello there");
define('FM_VERSION', '0.1.1');
define('FM_ROOT', str_replace(ABSPATH, '/', dirname(__DIR__, 1)));
define('FM_PATH', dirname(__DIR__, 1));
define('FM_URI', home_url(FM_ROOT));
define('FM_HMR_HOST', 'http://localhost:5173');
define('FM_HMR_URI', FM_HMR_HOST . FM_ROOT);
define('FM_ASSETS_PATH', FM_PATH . '/dist');
define('FM_ASSETS_URI', FM_URI . '/dist');

define( 'FM_TEST', 'test' );

require_once FM_PATH . '/inc/bootstrap.php';

Linting tools, when set correctly, should indicate issues within the codebase that need to be addressed, as is the case for us. Issues are identified correctly, turning on the red flag.

yarn stylelint resources/styles/styles.scss

resources/styles/styles.scss
  94:1  ✖  Expected "A" to be "a"  selector-type-case

✖ 1 problem (1 error, 0 warnings)
  1 error potentially fixable with the "--fix" option.

error Command failed with exit code 2.
yarn eslint resources/scripts/scripts.js

/Users/przemyslawhernik/Sites/fm.tentyp.dev/wp-content/themes/footmate/resources/scripts/scripts.js
  1:13  error  Strings must use singlequote  quotes

✖ 1 problem (1 error, 0 warnings)
  1 error and 0 warnings potentially fixable with the `--fix` option.

error Command failed with exit code 1.
phpcs resources/functions.php

FILE: /Users/przemyslawhernik/Sites/fm.tentyp.dev/wp-content/themes/footmate/resources/functions.php
-------------------------------------------------------------------------------------------------------------------------------------------------------------
FOUND 2 ERRORS AFFECTING 1 LINE
-------------------------------------------------------------------------------------------------------------------------------------------------------------
12 | ERROR | [x] Space after opening parenthesis of function call prohibited (PSR2.Methods.FunctionCallSignature.SpaceAfterOpenBracket)
12 | ERROR | [x] Expected 0 spaces before closing parenthesis; 1 found (PSR2.Methods.FunctionCallSignature.SpaceBeforeCloseBracket)
-------------------------------------------------------------------------------------------------------------------------------------------------------------
PHPCBF CAN FIX THE 2 MARKED SNIFF VIOLATIONS AUTOMATICALLY
-------------------------------------------------------------------------------------------------------------------------------------------------------------

Time: 89ms; Memory: 10MB

Without tools like husky and Lint-staged, we might take shortcuts and push incorrect code to the repository. This is what we aim to avoid in this guide. We plan to use these tools when we want to ensure that simple fixes are implemented before pushing to the repository, which often takes just seconds, rather than rushing and dealing with bugs later.


What’s husky and how to use it?

husky is a tool that helps to run tasks when specific events occur in a repository, like a pre-commit, which is triggered before a commit is done. It uses git hooks, allowing for instance to check if the code that is going to be pushed to the repository, complies with defined project standards and blocks the submission if it does not.

What else can you do with husky? If you're feeling playful, you could create and trigger a Slack API integration that shares memes with your colleagues with every commit. If you’re aiming for something more ambitious, you can write a simple AI utility that generates commit messages for you. The sky's the limit, and everything is in your hands.

Simply check what hooks you can use, and build your own process tailored to your needs.

How to install husky?

Start by installing the tool with yarn add husky --dev and run the initialization process with yarn husky init command. It will create a pre-commit file inside the .husky directory of your project, which can be used to define what should happen before making a commit.

footmate
└─── .husky
│   │   pre-commit
└─── app
└─── inc
└─── resources

Open this file and add the command to run when the pre-commit hook is invoked in the repository. We're going to run the linting task defined in the package.json that checks the codebase compliance with defined coding standards with PHPCS, ESLint and Stylelint.

yarn lint

You can use other hooks, such as a pre-push to run a task before pushing code to the remote repository. To add a new task to be triggered by a specific hook, use the command below, or manually create a new file with the name of the hook in the .husky directory.

echo "yarn lint" > .husky/pre-push

The initialization process will also modify the prepare script in the project’s package.json file to automatically refresh GIT hooks whenever dependencies are installed, ensuring they are updated in each user's environment. If you feel the tool doesn't work, try to run yarn install.

{
  "scripts": {
    "lint": "stylelint **/*.scss && eslint . && phpcs",
    "format": "stylelint **/*.scss --fix && eslint . --fix && phpcbf",
    "prepare": "husky"
  },
}

⚠️ If your .husky directory isn't located in the repository's root, like in my case, when it's placed in /wp-content/themes/footmate, you'll need to follow some extra steps.

How to use husky?

Stage some incorrect files and try to make a commit. The tool should automatically execute the yarn lint command before the commit and prevent it if errors are detected.

git add .
git commit -m "test commit"

$ stylelint **/*.scss && eslint . && phpcs

resources/styles/styles.scss
  1:1  ✖  Expected "A" to be "a"  selector-type-case

✖ 1 problem (1 error, 0 warnings)
  1 error potentially fixable with the "--fix" option.

error Command failed with exit code 2.

Pushing code with intentionally created problems triggers an error, preventing the user from performing the commit. This is what we wanted to achieve.


Hi Guys! This article is part of a larger initiative I've been recently putting my heart into 👇

I've been working on something I’m really proud of - a new eBook all about code linting and formatting in web development. It’s filled with practical tips to help you set up the project environment so your code stays clean, consistent, and free of those annoying little errors.

The plan is to sell it for $15, but if you're on my newsletter, you’ll get almost 70% discount. Just drop your email below, and you’ll get it for just $5 when it’s ready 🙌


What’s lint-staged and how to use it?

lint-staged is a tool that allows firing actions on staged files in a git repositoryIt iterates through the files that are going to be comitted and runs specified tasks based on their types. For instance, it can be used to fix formatting issues and perform syntax checks on every JavaScript file that is going to be committed, and block process when errors occurrs.

Why should you use this tool when you can simply run such a check with husky? Depending on the complexity of your project, running a full codebase check could take a minutes. lint-staged runs actions only on staged files, skipping others that might just be drafts, reducing verification time to seconds. It checks only what's added to GIT stage, and that's difference.

How to install lint-staged?

Start by installing the tool with yarn add lint-staged --dev, and create a .lintstagedrc file to configure the actions you want to apply to each type of file in the staging area.

{
  "*.js": [
    "eslint --fix",
    "eslint"
  ],
  "*.scss": [
    "stylelint --fix",
    "stylelint"
  ],
  "*.php": [
    "vendor/bin/phpcs"
  ]
}

The setup, for each defined type, first addresses any issues that can be automatically fixed, reducing your need for manual effort, then reviews the outcomes to identify remaining issues that may require manual resolution. The only difference is .

How to use lint-staged?

Stage some files and run yarn lint-staged command in the terminal. If there are any issues, the process will terminate and show errors that should be handled before proceeding further.

yarn lint-staged

$ /Users/przemyslawhernik/Sites/fm.tentyp.dev/wp-content/themes/footmate/node_modules/.bin/lint-staged
✔ Backed up original state in git stash (df692fc)
⚠ Running tasks for staged files...
  ❯ .lintstagedrc — 3 files
    ❯ *.js — 1 file
      ✖ eslint [KILLED]
    ❯ *.scss — 1 file
      ✖ stylelint [KILLED]
    ❯ *.php — 1 file
      ✖ phpcs [FAILED]
↓ Skipped because of errors from tasks.
✔ Reverting to original state because of errors...
✔ Cleaning up temporary files...

The command has verified all staged files and run linting checks. While it works, there's a catch. In this example, yarn lint-staged was fired manually, raising the usual concerns, like forgetting about this step. That’s where husky helps.


How to use husky with lint-staged?

For optimal results, it's crucial to integrate husky and lint-staged together. This way, you won't forget to check the staged files for compliance with the defined coding standards, since it will be handled automatically by husky before you make a commit.

Modify the .husky/pre-commit file and switch from yarn lint to yarn lint-staged command there. This will tell husky to run the linting process only on staged files, rather than the entire codebase, cutting down the time spent on verification.

yarn lint-staged

Add the files that contain erors to the stage and try to make a commit to see if it works.

git add .

✗ git commit -m "test commit"
✔ Backed up original state in git stash (38dea9d)
⚠ Running tasks for staged files...
  ❯ .lintstagedrc — 3 files
    ❯ *.js — 1 file
      ✖ eslint [KILLED]
    ❯ *.scss — 1 file
      ✖ stylelint [KILLED]
    ❯ *.php — 1 file
      ✖ phpcs [FAILED]
↓ Skipped because of errors from tasks.
✔ Reverting to original state because of errors...
✔ Cleaning up temporary files...

It works! Before approving the commit, this tool fixes simple issues and highlight those that need manual resolution. Once these are resolved, the commit will be approved and the updated code pushed to the repository. Otherwise, the tool will block a commit ensuring the code with problems won't be pushed to the repository.

git commit -m "test commit"

✔ Backed up original state in git stash (fd439bb)
✔ Running tasks for staged files...
✔ Applying modifications from tasks...
✔ Cleaning up temporary files...
✨  Done in 1.36s.

[feature/theme-updates cec3ae1] test commit
 2 files changed, 5 insertions(+)

How husky & lint-staged improve workflow at Coditive team?

After integrating this tool into coditive workflow, we effectively eliminated simple, repeatable issues, significantly easing the team's workload, particularly during pull requests. This allows the team to focus on more critical tasks and return to code review without spending time addressing minor problems. That was a huge improvement.

An important takeaway from our experience is that catching errors as you write minimizes the irritation of fixing issues when you're ready to commit changes. That's why it's beneficial to integrate your project's linters directly within your code editor. Here are some guides for the most popular tools we rely on: PHPCS, ESLint, Stylelint.


Tips & tricks about husky & lint-staged

Below you can find some useful tips and tricks when working with husky and lint-staged.

How to skip code checks in commit?

Sometimes, you might find yourself needing to push code that isn’t quite right. Maybe you’re in a hurry and can’t afford to delay your changes. Thankfully, there's a way to do this without changing your entire setup. By using the --no-verify flag during a commit, you can bypass any git hooks, preventing code checks from running.

git commit -m "commit message" --no-verify

How to install husky outside Rroot directory?

First, you need to change the working directory before running the tasks defined in the hooks. Simply modify the .husky/pre-commit file and switch the directory with the cd command.

cd wp-content/themes/footmate
yarn lint

Second, we need to adjust the prepare script in the package.json file, which we created earlier, to initialize the GIT hooks with actions defined in a custom, non-root directory.

{
  "scripts": {
    "lint": "stylelint **/*.scss && eslint . && phpcs",
    "format": "stylelint **/*.scss --fix && eslint . --fix && phpcbf",
    "prepare": "cd ../../.. && husky wp-content/themes/footmate/.husky"
  },
}

GIT hooks are triggered from the repository's root, so in the prepare script, we need to first navigate to the root and run husky, specifying the custom directory. After changes, run the yarn install script to ensure the updates are reflected in the GIT hooks.

Lefthook: A promising alternative

While writing this article, I came across a promising alternative to husky and lint-staged called Lefthook. It combines the advantages and core functionalities of both tools, minimizing project dependencies. This is worth giving a try, and I plan to explore it further.

Image


Thank you so much for joining me in today ❤️ Your support means a lot to me, and it keeps me motivated to create more content. If you enjoyed this video, please consider subscribing to the channel and giving it a thumbs-up - it really helps spread the word. And if you need developers who truly care about quality and delivering great results, visit my company’s site: coditive.com or contact me with the form below. Thanks again and see you next time!

avatar

Looking For a Developer Who
Truly Cares About Your Business?

My team and I provide expert consultations, top-notch coding, and comprehensive audits to elevate your success.

Feedback

How satisfied you are after reading this article?