TypeScript/ESLint, импорты и правила: настройка качества для UI компонентов
В предыдущих статьях мы настроили SonarQube Server, выбрали стратегию проект на пакет и разобрали sonar-project.properties, чтобы SonarQube честно видел sources/tests/exclusions и подхватывал coverage.
Следующий практический слой качества в UI Kit — это правила, которые влияют на ежедневную разработку компонентов:
TypeScript как контракт (типы, публичный API, безопасность refactor’ов)
ESLint как быстрая обратная связь (импорты, границы пакетов, локальные паттерны)
SonarQube как единый quality gate в CI (надежность/безопасность/поддерживаемость на новом коде)Эта статья про то, как настроить правила вокруг TypeScript и импортов так, чтобы SonarQube получал корректный контекст анализа, а команда — понятные и не конфликтующие сигналы качества.
!Пайплайн качества пакета: ESLint и тесты готовят базу, SonarQube делает итоговый gate
Роли инструментов и почему важно развести ответственность
Если не договориться о ролях, получится либо двойная проверка одного и того же (шум), либо дыры (важное не проверяется нигде).
TypeScript
TypeScript отвечает за:
корректность типов и контрактов (props, события, generics)
безопасность рефакторинга (переименования, извлечения, изменение сигнатур)
качество публичного API пакета (что экспортируем и как)Официальная документация:
TSConfig ReferenceESLint
ESLint отвечает за:
быстрые проверки в редакторе и в pre-commit
правила на уровне команды (импорты, ограничения, архитектурные границы)
автофиксы (например, удаление неиспользуемых импортов)Официальная документация:
ESLint DocumentationSonarQube
SonarQube отвечает за:
единый отчёт и историю метрик
качество New Code через Quality Gate
более “архитектурные” сигналы: code smells, complexity, duplications, reliability/securityДокументация:
JavaScript/TypeScript analysis
Quality ProfilesТиповая цель для UI Kit: стабильный API и контролируемые зависимости
Для UI компонентов качество часто ломается не “сложной бизнес-логикой”, а нарушениями дисциплины:
глубокие импорты в чужие внутренности (deep import)
циклические зависимости между компонентами/утилитами
импорт из “запрещённых” пакетов (например, UI тянет app-слой)
расползание публичного API (экспортируем всё подряд)
скрытые any, неявные unknown, агрессивные ! (non-null assertion)Эти проблемы лучше всего ловятся комбинацией TypeScript + ESLint, а SonarQube дальше превращает это в управляемый gate на PR.
TypeScript: настройки, которые напрямую влияют на качество анализа
SonarQube для JS/TS анализирует код статически, но качество результата сильно зависит от того, насколько правильно проект описан через tsconfig.json.
Выбор tsconfig для пакета
В монорепо чаще всего есть:
корневой tsconfig.base.json (общие опции)
packages/<name>/tsconfig.json (опции пакета, include/exclude, ссылки на базу)Рекомендация: в каждом анализируемом пакете держите свой tsconfig.json и убедитесь, что он:
включает исходники пакета (src)
исключает артефакты (dist, coverage, сгенерированное)Пример packages/ui/tsconfig.json:
Почему это важно:
если include/exclude настроены плохо, в анализ “протечёт” мусор (сборка, генерация) и метрики исказятся
если tsconfig не соответствует реальному коду пакета, TypeScript-часть анализа будет менее точной, а ошибки могут выглядеть странноАлиасы импортов и paths
UI Kit часто использует алиасы:
@ui/*
@tokens/*
@shared/*Чтобы импорты были понятны TypeScript и инструментам, в базе обычно задают baseUrl и paths. Важно, чтобы эти настройки были доступны пакетному tsconfig.json через extends.
Пример фрагмента tsconfig.base.json:
Практика для UI компонентов:
алиасы помогают избегать “хрупких” относительных импортов вроде ../../../utils
но алиасы могут скрыть неправильные зависимости, поэтому их нужно подкреплять ESLint-правилами границ (об этом ниже)Строгость типов как договорённость
Для UI Kit обычно полезно, чтобы строгие настройки были включены, иначе ошибки “просачиваются” через базовые компоненты и распространяются на всех потребителей.
Часто полезные опции в compilerOptions:
strict
noUncheckedIndexedAccess
noImplicitOverride (если используете классы)Важно: включать строгость лучше поэтапно, но для нового кода (подход Clean as You Code) можно держать планку выше.
ESLint: импорты, границы и правила, которые реально защищают UI компоненты
ESLint особенно полезен там, где:
нужно быстро получить фидбек до CI
нужно enforce’ить архитектурные правила, специфичные для вашей командыБазовый набор плагинов вокруг импортов
eslint-plugin-import
eslint-plugin-unused-importsЧто они дают для UI Kit:
контроль циклов и “неправильных” импортов
автоудаление неиспользуемых импортов
единообразие импортов, что снижает диффы и шанс конфликтовПравила, которые обычно дают максимальный эффект в UI Kit
Ниже примерный набор целей (не “копипаст”, а ориентир, что именно вы хотите запретить/зафиксировать).
#### Запрет deep imports в чужие внутренности
Проблема:
компонент из packages/ui начинает импортировать не из публичного входа пакета, а из внутреннего файла src/internal/...
потом вы рефакторите внутренности, и ломаются потребителиРешение:
определить публичный API пакета (обычно src/index.ts)
запретить deep imports из пакета, оставив разрешённым только импорт из корняПример идеи через no-restricted-imports (конкретные паттерны подстроите под вашу структуру):
#### Защита от циклических зависимостей
Проблема:
циклы между компонентами и утилитами постепенно появляются незаметно
это усложняет сборку, тестирование и рефакторингРешение:
включить проверку циклов на уровне ESLintПример:
#### Запрет “тяжёлых” или запрещённых зависимостей
Проблема:
в UI пакет “протаскивают” зависимости, которые увеличивают бандл или нарушают архитектуруРешение:
запретить конкретные импорты, например:Примечание: это не универсальное правило, но сам паттерн полезен.
#### Удаление неиспользуемых импортов как правило гигиены
Неиспользуемые импорты:
захламляют дифф
мешают читать код
иногда ломают tree-shakingПример:
Как подружить SonarQube и ESLint, чтобы не было шума
И SonarQube, и ESLint умеют находить часть пересекающихся проблем. Цель — не “вырезать” пересечение полностью, а сделать так, чтобы:
разработчик ловил проблемы быстро (ESLint локально)
PR блокировался по понятным критериям (SonarQube Quality Gate)Практичная стратегия разделения:
ESLint:
- импорты и архитектурные ограничения
- автофиксы (unused imports)
- командные соглашения
SonarQube:
- Quality Gate на новом коде (bugs/vulnerabilities)
- maintainability сигналы (complexity, duplications)
- единая точка отчётности
Таблица, как обычно распределяют ответственность:
| Тип проблемы | Где ловим в первую очередь | Почему |
|---|---|---|
| Неиспользуемые импорты | ESLint | автофикс и быстрый фидбек |
| Циклы импортов | ESLint | проще enforce’ить как “нельзя никогда” |
| Глубокие импорты (нарушение public API) | ESLint | правило зависит от вашей структуры |
| Дублирование, сложность | SonarQube | удобнее как метрика и тренд |
| Bugs/Vulnerabilities/Hotspots | SonarQube | удобнее как gate на PR и процесс review |
Ключевой принцип: если ESLint уже “железно” блокирует класс проблемы и даёт автофикс, нет смысла строить процесс, где SonarQube становится единственной точкой обнаружения.
Настройка SonarQube проекта пакета, чтобы TypeScript контекст был корректным
В прошлой статье мы уже сделали основу sonar-project.properties. Для TS-пакетов добавьте явную привязку к tsconfig.json пакета, если у вас есть нестандартная структура, references или алиасы.
Пример для packages/ui/sonar-project.properties (добавка к уже существующему шаблону):
Практическая проверка:
анализ запускается с sonar.projectBaseDir=packages/ui
тогда tsconfig.json корректно находится в корне пакетаЕсли вы запускаете анализ из корня репозитория, но сканируете пакет через sonar.projectBaseDir, идея та же: все пути должны быть валидны относительно baseDir.
Как это уложить в Turborepo пайплайн
Чтобы SonarQube не анализировал “в вакууме”, обычно фиксируют зависимость:
sonar зависит от test (чтобы был lcov.info)
часто полезно, чтобы sonar зависел и от lint (чтобы базовая гигиена была пройдена раньше, а PR не блокировался “мелочами” уже на этапе quality gate)Ссылки по Turborepo:
Turborepo DocumentationКонкретную сборку turbo.json и запуск анализа “только затронутых пакетов” логично делать на следующем шаге внедрения, когда вы уже стабилизировали правила.
Практический чеклист для UI Kit
На уровне TypeScript:
- в каждом анализируемом пакете есть свой
tsconfig.json
-
include содержит только исходники,
exclude выкидывает артефакты
- алиасы (
paths) доступны пакетам через
extends
На уровне ESLint:
- есть правила, защищающие public API (запрет deep imports)
- включена защита от циклов
- включено удаление неиспользуемых импортов
- запрещены критичные зависимости или направления импортов (по вашим договорённостям)
На уровне SonarQube:
- анализ идёт по пакету (проект на пакет)
-
sonar.sources/tests/exclusions корректны
- coverage подключён, а
New Code и Quality Gate реально блокируют плохие изменения
Дальше, когда правила стабилизированы, имеет смысл автоматизировать запуск в CI: запускать анализ только для затронутых пакетов и “декорировать” PR результатами Quality Gate.