Линт проектов: собираем ESLint, Prettier и Stylelint в один пакет
- воскресенье, 24 мая 2026 г. в 00:00:16
В большинстве компаний линтинг со временем превращается в хаос: разные правила ESLint, устаревшие конфиги и копипаста между проектами.
Покажу, как навести порядок – собрать линт-инфраструктуру в один пакет и выстроить систему контроля кода для всех репозиториев.
Статья будет полезна:
разработчикам, которые хотят навести порядок в линтинге нескольких проектов;
тимлидам и техлидам, которые строят единые стандарты кода в команде;
тем, кто планирует вынести конфигурации ESLint, Prettier и Stylelint в отдельный пакет.
Предполагается базовое знакомство с ESLint, Prettier и экосистемой JavaScript.
Если в компании больше одного проекта, со временем почти всегда появляется одинаковая проблема: линт-конфиги начинают расползаться.
В одном репозитории ESLint настроен так, в другом – немного иначе. Где-то есть stylelint, где-то нет. В одном проекте используются recommended правила, в другом – половина из них отключена.
Через год это обычно выглядит примерно так:
одинаковые конфиги копируются из проекта в проект;
правила случайно расходятся;
при обновлении ESLint или плагинов каждый репозиторий приходится чинить отдельно;
новые проекты начинают, используя старые, устаревшие конфиги.
В итоге линтинг, который должен помогать поддерживать единый стиль кода, сам становится источником хаоса.
Решение у этой проблемы довольно простое – вынести всю линт-инфраструктуру в отдельный npm-пакет: @company/lint.
После этого:
все проекты используют одни и те же правила;
обновление линта происходит в одном месте;
новые проекты получают готовую инфраструктуру за минуту;
команда может обновлять правила постепенно.
По сути, компания начинает относиться к линтингу как к части своей инженерной платформы, а не как к случайному набору файлов конфигов.
Lint-пакет стоит рассматривать не просто как сборку конфигураций, а как централизованный гайдлайн по стандартам кода компании.
Помимо самих конфигов, здесь удобно описывать рекомендации по инструментам анализа кода и правила их использования. Такой пакет становится единым источником правды для всех проектов.
Пример реализованного пакета можно посмотреть в этом репозитории: https://github.com/Dozalex/lint-configs.
Рассмотрим инструменты, которые обычно входят в такую инфраструктуру, а далее разберем каждый подробнее.
Это один из основных инструментов для поддержки качества кода. ESLint помогает находить потенциальные ошибки, логические проблемы и нарушения принятых в проекте правил.
Ранее вынос конфигурации ESLint в отдельный пакет был не очень удобным. Каждую зависимость, используемую в конфиге (плагины, парсеры, расширения), приходилось дублировать в package.json каждого проекта. Иначе возникали конфликты зависимостей и ошибки загрузки плагинов.
С выходом ESLint 9 ситуация изменилась. Плоский конфиг (Flat Config) перестал быть экспериментальным и позволил инкапсулировать все зависимости внутри одного пакета.
Теперь рабочие репозитории могут подключать только сам конфиг, не заботясь о его внутренних зависимостях.
Prettier – это форматтер кода, который автоматически:
выравнивает отступы;
убирает лишние пустые строки;
расставляет переносы строк.
Stylelint выполняет ту же роль, что и ESLint, но для CSS.
Он помогает находить ошибки в стилях и следить за единообразием CSS-кода. Однако особенно полезным он становится при использовании плагина stylelint-order, который автоматически сортирует CSS-свойства в заданном порядке.
Это делает стили более предсказуемыми и упрощает навигацию по коду, что ускоряет разработку.
Madge – менее очевидный, но полезный инструмент анализа зависимостей.
Его основная задача – поиск циклических зависимостей между модулями. Такие зависимости могут приводить к трудноуловимым ошибкам и усложнять поддержку проекта, поэтому их важно выявлять как можно раньше.
В последнее время появляются инструменты, которые пытаются объединить линтер и форматтер в одном решении. Одним из таких проектов является Biome.
Он написан на Rust, благодаря чему заявляется высокая скорость работы. Однако инструмент пока достаточно молодой и его экосистема значительно уступает ESLint по количеству плагинов и правил.
Также, начиная с Vite 8 и связанных проектов VoidZero, экосистема Vite движется в сторону единого toolchain’а: кроме сборки, в неё интегрируются задачи линтинга, форматирования и type-checking. Для линтинга используется Oxlint из экосистемы Oxc.
Возможно, со временем такие решения смогут заменить текущий набор инструментов и сократить время выполнения линтинга без потери качества проверки кода. Но, в любом случае, инструменты, работа с которыми описана в этой статье, будут существовать в экосистеме еще долгие годы, так как проверены временем и используюся в огромном числе компаний.
Долгое время eslint-config-airbnb считался де-факто стандартом для фронтенд-проектов. Большинство команд просто брали его за основу и отключали или переопределяли отдельные правила. Однако со временем пакет перестал активно обновляться и сегодня он содержит большое количество легаси-правил, не соответствующих современному стеку разработки.
При создании собственного lint-пакета я решил детально разобрать правила из этого конфига. Там оказалось большое кол-во стилистического мусора, правил для классовых компонентов реакта и прочих древностей. Рекомендованные конфиги для React также содержат большое количество правил, которые существуют в основном для обратной совместимости.
В итоге стало очевидно, что переносить весь этот набор правил в собственный конфиг не имеет большого смысла, поэтому я отказался от recommended конфигов в пользу явного понимания того, какие правила попадут в мой конфиг. Однозначно, у этого подхода есть как плюсы, так и минусы.
Плюсы:
в конфиге нет ничего лишнего;
не нужно делать десятки выключений правил;
нужные правила не пропадут неожиданно при обновлении recommended конфига;
линт выполянется быстрее благодаря минимальному набору правил.
Минусы:
если хочется самому отфильтровать каждое правило, это займет довольно много времени;
когда появятся новые правила для нового синтаксиса языка или фреймворка, будет не достаточно просто обновить recommended конфиг, нужно будет найти эти новые правила, решить, нужны ли они, перенести в свой конфиг.
Было принято решение, что плюсы перевешивают минусы, поэтому все правила из eslint-airbnb-config прошли фильтрацию от легаси, изменены в соответствии с современными стандартами. Также были добавлены некоторые новые полезные правила.
Правила были разбиты на небольшие конфигурационные модули, на подобии тех, что используются в eslint-airbnb-config. Это позволяет гибко собирать уникальный конфиг для конкретного проекта.
eslint/ core/ – содержит best-practices для js reactBase/ – содержит best-practices для react base.mjs – базовые настройки + объединение всех конфигов из core + typescript (потому что typescript это де-факто база) react.mjs – base.mjs + объединение всех конфигов из reactBase typescript.mjs – правила для typescript, подключение tsconfig.json webpack.mjs – подключение webpack.config.ts
Такая структура делает конфигурацию расширяемой. Например, можно добавить vue.mjs, подключить базовые правила и получить полноценный конфиг для Vue-проекта, не включая при этом правила, относящиеся к React.
Также, если бы мы зашили в единый конфиг логику для подключения webpack конфига, но не использовали его в проекте, eslint бы сразу выдал ошибку.
Из интересного в конфиге хочется выделить:
Правило import/order.
Раньше часто сталкивался с тем, что некоторым разработчикам все равно, что у них там в импортах. Как IDE автоматически добавила импорт, так они и оставили – вперемешку импорты библиотек, относительные импорты, алиасы.
Прекрасно понимаю, что вручную их корректировать это безумие, но единый порядок способствует лучшей нафигации по коду, а следовательно ускорению разработки (еще и глазу приятнее). Поэтому данное правило позволяет всем оставаться довольными без применения усилий, так как автоматически сортирует и группирует импорты. Можно сделать максимально гибко, но базовых настроек уже будет хватать.
Использование eslint-config-flat-gitignore.
Этот конфиг помогает ESLint обходить ненужные для индексации и проверки файлы и директории. За основу берет .gitignore файл проекта. Соответственно, если все настроено корректно, линтер не будет ходить в build, dist, node_modules и прочие папки.
Устанавливаем зависимости
pnpm i -D @company/lint eslint
Этих зависимостей достаточно, чтобы все заработало. Это возможно благодаря тому, что все плагины и дополнительные зависимости инкапсулированы внутри @company/lint и будут корректно зарезолвлены без конфликтов.
Добавляем файл конфига
// eslint.config.js import eslintReactConfig from '@company/lint/eslint/react'; export default eslintReactConfig;
Добавляем скрипт
// package.json "scripts": { "lint:eslint": "eslint . --fix", }
Prettier – простой и надёжный инструмент форматирования кода, который автоматически приводит файлы к единому стилю.
Иногда форматирование пытаются реализовать через ESLint, поскольку его правила позволяют гибко управлять стилем кода. Однако у такого подхода есть несколько недостатков:
авторы ESLint официально отказались от поддержки форматирующих правил (их поддержка перешла в проект ESLint Stylistic);
Prettier работает быстрее, чем форматирование через ESLint;
большое количество настроек форматирования в ESLint приводит к избыточной конфигурации.
Prettier, наоборот, придерживается минималистичной философии: небольшое количество настроек, которых достаточно для поддержания читаемого и единообразного кода.
Ранее распространённым подходом было использование Prettier вместе с ESLint через eslint-config-prettier и eslint-plugin-prettier.
eslint-config-prettier отключал стилистические правила ESLint, которые конфликтовали с Prettier;
eslint-plugin-prettier позволял запускать Prettier как правило ESLint.
От этой практики отказались, теперь рекомендуется запускать эти инструменты отдельно, так как это более производительно и независимо.
Устанавливаем зависимости
pnpm i -D @company/lint prettier
Добавляем файл конфига
// .prettierrc "@company/lint/prettier"
Добавляем скрипт
// package.json "scripts": { "lint:prettier": "prettier . -w --log-level error --ignore-unknown", }
Флаг --ignore-unknown позволяет пропускать файлы, которые Prettier не умеет форматировать.
Добавляем файл для игнора
// .prettierignore pnpm-lock.yaml
Если используется pnpm, рекомендуется добавить pnpm-lock.yaml в список игнорируемых файлов. В противном случае Prettier может пытаться форматировать его при каждом запуске. В остальном, Prettier по умолчанию смотрит в .gitignore, находящийся в папке запуска, и не сканирует файлы и папки, которые там перечислены.
Устанавливаем зависимости
pnpm i -D @company/lint stylelint
Добавляем файл конфига
// stylelint.config.js module.exports = { extends: ['@company/lint/stylelint'], };
Добавляем скрипт
// package.json "scripts": { "lint:style": "stylelint \"{src,packages/*/src}/**/*.css\" --fix", }
С этим инстурументом все просто – установил, добавил конфиг, запустил и получил в консоль ошибки, если они есть.
Устанавливаем зависимости
pnpm i -D madge
Добавляем файл конфига
// .madgerc { "detectiveOptions": { "ts": { "skipTypeImports": true }, "tsx": { "skipTypeImports": true } }, "fileExtensions": ["ts", "tsx"], "tsConfig": "./tsconfig.json" }
По желанию можно удалить из конфига игнорирование импортов типов, если вы хотите, чтобы для них тоже не было рекурсии, но в рамках типов обычно это допускается, если мы делаем их через import type.
Добавляем скрипт
// package.json "scripts": { "lint:madge": "madge --circular src packages/*/src", }
Инструмент линтинга кода номер один. Обязателен в современном мире веб разработки. Если вы его не используете, значит полагаетесь на волю случая, ибо одно только использование typescript избавляет вас от 90% багов (субъективная оценка, основанная на прошлом опыте), которые могли бы быть, если бы вы использовали только javascript.
Устанавливаем зависимости
pnpm i -D typescript
Добавляем файл конфига tsconfig.json с настройками для вашего проекта
Добавляем скрипт
// package.json "scripts": { "lint:ts": "tsc --noEmit", }
Инструмент, который запускает команды только для файлов, изменённых в git staging area (то есть добавленных через git add).
Обычно используется с Husky (разберем далее) в pre-commit hook:
Меняем файлы
Делаем git add.
Перед git commit запускается lint-staged.
Он прогоняет линтеры/форматтеры только по staged-файлам.
Если всё ок – коммит проходит.
Устанавливаем зависимости
pnpm i -D lint-staged
Добавляем конфиг
// package.json "lint-staged": { "*.{css,ts,tsx}": ["stylelint --fix"], "*.{js,jsx,ts,tsx,mjs,cjs}": ["eslint --fix --quiet"], "*": ["prettier -w --log-level error --ignore-unknown"] }
Команды для разных масок запускаются параллельно. Если файл подходит под несколько масок – для него выполнятся все подходящие команды.
Инструмент, который позволит автоматически запускать реализованные выше скрипты, непосредственно перед коммитом изменений.
Устанавливаем зависимости
pnpm i -D husky
Добавляем файл конфига
// .husky/pre-commit pnpm lint:madge pnpm lint-staged pnpm lint:ts
Важные моменты:
запуск lint:madge добавляем сюда, а не в lint-staged, потому что в lint-staged идет работа с каждым файлом отдельно, а здесь один раз сделает прогон и все, экономит время.
запуск lint:ts добавляем сюда, а не в lint-staged, потому что в противном случае рискуем закоммитить код, который содержит ошибки. Например, если мы обновили только зависимости в package.json, lint-staged проверит только сам packages.json и lock файл, но не обнаружит typescript ошибок, которые могли возникнуть из за обновления этих зависимостей.
Добавляем скрипт
// package.json "scripts": { "prepare": "husky", }
Этот скрипт сработает автоматически, в момент установки зависимостей (pnpm i), и настроит husky для работы в проекте.
Теперь, когда разработчик решит сделать коммит изменений, то husky автоматически запустит указанные в pre-commit команды.
Конечно, разработчик может отключить запуск Git хуков и проигнорировать проверку, но это уже будет на его совести. Правильным решением здесь будет добавить линтинг в процесс CI, чтобы как только был создан коммит в ветку, запускалась джоба, которая выполнит все проверки и убедится в том, что предоставленный код удовлетворяет всем условиям.
{ "scripts": { "prepare": "husky", "lint:ts": "tsc --noEmit", "lint:eslint": "eslint . --fix", "lint:madge": "madge --circular src packages/*/src", "lint:prettier": "prettier . -w --log-level error --ignore-unknown", "lint:style": "stylelint \"{src,packages/*/src}/**/*.css\" --fix", "lint": "yarn lint:madge && yarn lint:style && yarn lint:eslint && yarn lint:ts && yarn lint:prettier" }, "lint-staged": { "*.{css,ts,tsx}": ["stylelint --fix"], "*.{js,jsx,ts,tsx,mjs,cjs}": ["eslint --fix --quiet"], "*": ["prettier -w --log-level error --ignore-unknown"] }, "devDependencies": { "@company/lint": "^1.0.0", "eslint": "^9.39.4", "husky": "^9.1.7", "lint-staged": "^13.0.3", "madge": "^8.0.0", "prettier": "^3.8.1", "stylelint": "^17.4.0", "typescript": "^5.9.3", } }
Удобно, во время разработки, запустить все проверки одной командой pnpm lint. Важно запускать их в правильном корядке, чтобы скрипты не конфликтовали после автоматических правок.
Создание пакета с конфигами для линта сильно облегчило мою работу:
cтало меньше бойлерплейтного кода в проектах;
правила линтинга инкапсулировались в одном месте;
удобно обновлять их в одном месте и постепенно применять в каждом проекте обновлением одной зависимости.
Если вы захотите иметь подобный набор инструментов у себя в проектах, чтобы было от чего отталкиваться, можете воспользоваться моим репозиторием https://github.com/Dozalex/lint-configs и пакетом @dozalex/lint.
Конструктивные замечания всегда приветствуются. Буду рад полезному фидбеку, чтобы сделать код еще более качественным и надежным.