Настройка через Husky

Git hooks представляют собой механизмы, позволяющие выполнять произвольные скрипты на определённых этапах работы системы контроля версий. Наиболее часто они применяются для автоматизации проверки качества кода перед фиксацией изменений. Одним из ключевых этапов становится pre-commit, на котором выполняется линтинг с использованием ESLint.

Husky выступает надстройкой над Git hooks, упрощающей их настройку и версионирование внутри репозитория. Вместо ручного создания скриптов в .git/hooks, используется декларативная конфигурация внутри проекта, что делает процесс предсказуемым и переносимым между окружениями.

Установка Husky в проект

Базовая интеграция начинается с установки пакета:

npm install husky --save-dev

Инициализация структуры hooks выполняется через команду:

npx husky init

После выполнения создаётся директория .husky/ и базовый hook pre-commit. Также в package.json добавляется скрипт подготовки:

{
  "scripts": {
    "prepare": "husky"
  }
}

Скрипт prepare обеспечивает автоматическую активацию hooks после установки зависимостей.

Структура директории .husky

После инициализации формируется каталог:

.husky/
  pre-commit

Каждый файл внутри соответствует отдельному Git hook. Файл представляет собой shell-скрипт:

#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

Эта строка подключает внутреннюю инфраструктуру Husky и обеспечивает корректное выполнение команд в контексте Git.

Интеграция ESLint в pre-commit

Простейшая интеграция ESLint в Husky заключается в запуске проверки всего проекта:

npx eslint .

Однако такой подход неэффективен для больших кодовых баз, так как проверяются все файлы независимо от изменений.

Более оптимальный вариант — анализ только staged-файлов.

Оптимизация через lint-staged

Для выборочной проверки изменённых файлов используется lint-staged:

npm install lint-staged --save-dev

Конфигурация добавляется в package.json:

{
  "lint-staged": {
    "*.js": "eslint"
  }
}

После этого pre-commit hook изменяется:

npx lint-staged

Принцип работы lint-staged

Механизм работы основан на следующих этапах:

  1. Получение списка файлов из Git index (staged area)
  2. Фильтрация по маскам (например, *.js, *.ts)
  3. Запуск ESLint только для выбранных файлов
  4. При успешной проверке — продолжение коммита
  5. При ошибках — остановка процесса фиксации изменений

Такой подход существенно сокращает время проверки и снижает нагрузку на систему.

Комбинация Husky и lint-staged

Наиболее распространённая схема конфигурации:

Установка зависимостей

npm install husky lint-staged eslint --save-dev

pre-commit hook

npx lint-staged

package.json

{
  "scripts": {
    "prepare": "husky"
  },
  "lint-staged": {
    "*.js": [
      "eslint --fix",
      "git add"
    ]
  }
}

Использование --fix позволяет автоматически исправлять часть проблем до завершения коммита.

ESLint с флагом –cache

Для ускорения повторных проверок применяется кеширование:

npx eslint . --cache

Файл кеша .eslintcache сохраняет результаты предыдущих проверок и позволяет пропускать неизменённые участки кода.

При использовании с lint-staged кеширование становится менее критичным, однако остаётся полезным при дополнительных ручных запусках.

Применение в TypeScript проектах

В TypeScript окружении добавляется расширенная конфигурация:

{
  "lint-staged": {
    "*.{ts,tsx}": "eslint"
  }
}

Дополнительно ESLint должен быть настроен с соответствующими парсерами:

{
  "parser": "@typescript-eslint/parser",
  "plugins": ["@typescript-eslint"]
}

Pre-push hook и расширенная проверка

Помимо pre-commit, Husky позволяет использовать pre-push для более тяжёлых проверок:

npx eslint .
npm run test

Такой подход разделяет уровни контроля:

  • pre-commit — быстрый линтинг изменённых файлов
  • pre-push — полная проверка проекта и тестирование

Многопроектные репозитории (monorepo)

В монорепозиториях структура усложняется, и линтинг часто ограничивается конкретными пакетами.

Пример конфигурации lint-staged:

{
  "lint-staged": {
    "packages/*/src/**/*.{js,ts}": "eslint"
  }
}

Husky при этом остаётся единым уровнем оркестрации, не зависящим от структуры репозитория.

Частые проблемы интеграции

Игнорирование .husky в CI окружении

В некоторых CI системах Git hooks не активируются автоматически. Решением становится явный запуск ESLint:

npx eslint .

Конфликт с форматтерами

При совместном использовании Prettier и ESLint порядок выполнения становится критичным:

{
  "lint-staged": {
    "*.{js,ts}": [
      "eslint --fix",
      "prettier --write"
    ]
  }
}

Медленная работа hooks

Причины замедления:

  • отсутствие lint-staged
  • проверка всего проекта вместо staged файлов
  • отсутствие кеширования ESLint

Архитектурный смысл связки Husky + ESLint

Комбинация Husky и ESLint формирует слой локального контроля качества кода, встроенный непосредственно в Git workflow. Это позволяет перенести часть ответственности за корректность кода с CI-систем на локальное окружение разработчика, уменьшая количество дефектов, попадающих в удалённые ветки.

Структурно процесс выглядит как последовательность:

git commit
  ↓
Husky pre-commit hook
  ↓
lint-staged
  ↓
ESLint (staged files)
  ↓
разрешение или блокировка commit