Lint staged files on commit

How to run linting on staged files when committing code with no dependency: no (lint-staged | husky | pre-commit) needed.

Motivation

On most of my projects I want to run some checks on the code before it is committed. This is usually done with tools like lint-staged, husky or pre-commit.

You do not need those dependencies, it can be done easily with custom code.

Prepare

Of course the file names below are just examples, you can easily adapt them to your project structure.

Let’s create a scripts/prepare.js that will create the git hook when you run npm install.

import { chmod, copyFile } from 'node:fs/promises'

// Install Git pre-commit hook.
const preCommitHook = '.git/hooks/pre-commit'
await copyFile('scripts/pre-commit.sh', preCommitHook)
await chmod(preCommitHook, 0o755)

And add a prepare script to your package.json:

{
  "scripts": {
+    "prepare": "node scripts/prepare.js"
  }
}

Pre-commit hook

Now create the scripts/pre-commit.sh file that will be executed by git when you commit code.

This is just a starting point, you may add more logic. For now it just runs tests and ESLint on staged files. Notice that it greps only extensions js, ts and tsx, you may want to add more.
#!/bin/bash

npm test
[ $? -ne 0 ] && exit 1

EXIT_CODE=0

git diff --name-only --cached | grep -E '\.(js|ts|tsx)$' | while read FILE
do
  [ -f "$FILE" ] || continue
  ./node_modules/.bin/eslint --fix "$FILE"
  git add "$FILE"
  [ $? -ne 0 ] && EXIT_CODE=1
done

exit $EXIT_CODE