Плагин eslint-plugin-sonarjs

eslint-plugin-sonarjs — плагин ESLint, реализующий набор правил статического анализа, основанных на подходах SonarSource к выявлению потенциальных багов, проблем с читаемостью и «кодовых запахов». Основная цель инструмента — находить неочевидные ошибки логики, избыточные конструкции и потенциально опасные паттерны до выполнения кода.

Плагин ориентирован не на стиль форматирования (как eslint:recommended или prettier), а на качество архитектуры и поведенческую корректность кода. Он дополняет базовые правила ESLint более глубоким анализом потока выполнения и структуры условий.


ESLint работает как расширяемая система правил, где каждый плагин добавляет собственные проверки AST (Abstract Syntax Tree). eslint-plugin-sonarjs подключается на уровне правил и анализирует дерево синтаксиса JavaScript/TypeScript-кода, выявляя:

  • потенциально недостижимые ветки кода
  • дублирование условий
  • сложные выражения, ухудшающие читаемость
  • логические ошибки в условиях
  • избыточные циклы и проверки
  • подозрительные конструкции, ведущие к багам

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


Установка и базовая интеграция

Плагин устанавливается как обычная зависимость проекта:

npm install eslint-plugin-sonarjs --save-dev

После установки он подключается в конфигурации ESLint:

{
  "plugins": ["sonarjs"],
  "extends": ["plugin:sonarjs/recommended"]
}

Рекомендуемая конфигурация включает набор правил, подобранных для выявления наиболее распространённых проблем качества кода.


Принцип работы правил sonarjs

Каждое правило анализирует AST и строит упрощённую модель логики. Например:

  • анализ условий if/else
  • проверка вложенности блоков
  • анализ циклов и выходов из них
  • проверка повторяющихся выражений
  • оценка когнитивной сложности

Основная идея — выявление мест, где разработчик вынужден «держать слишком много контекста в голове».


Когнитивная сложность (sonarjs/cognitive-complexity)

Одно из ключевых правил — оценка когнитивной сложности функции. Оно отличается от цикломатической сложности тем, что учитывает не только ветвления, но и восприятие человеком.

Пример функции с высокой сложностью:

function processOrder(order) {
  if (order) {
    if (order.items) {
      for (let i = 0; i < order.items.length; i++) {
        if (order.items[i].active) {
          if (order.items[i].price > 100) {
            console.log("expensive");
          } else {
            console.log("normal");
          }
        }
      }
    }
  }
}

Проблема заключается в глубокой вложенности и множестве условий, которые усложняют понимание потока выполнения.

Правило стимулирует:

  • уменьшение вложенности
  • разделение функций
  • использование guard clauses
  • упрощение логики

Дублирование условий (sonarjs/no-duplicated-conditions)

Правило выявляет повторяющиеся или перекрывающиеся условия в цепочках if/else.

Пример проблемного кода:

if (status === "loading") {
  fetchData();
} else if (status === "loading") {
  showSpinner();
}

Вторая ветка никогда не будет выполнена. Такие ошибки часто возникают при рефакторинге или копировании блоков кода.


Избыточные условия (sonarjs/no-identical-expressions)

Выявляет сравнения, где обе стороны выражения одинаковы или логически эквивалентны.

if (user.id === user.id) {
  // всегда true
}

Подобные конструкции обычно являются следствием ошибок логики или незавершённого рефакторинга.


Пустые блоки кода (sonarjs/no-empty-collection / sonarjs/no-empty-function)

Плагин фиксирует функции и блоки, которые не выполняют полезной работы.

function handleClick() {}

Или:

if (items.length === 0) {
}

Такие конструкции могут указывать на недописанную логику или пропущенные требования.


Сложные условия (sonarjs/no-collection-size-mischeck, sonarjs/no-redundant-boolean)

Плагин обнаруживает избыточные логические выражения.

Пример:

if (isValid === true) {
  submit();
}

Более корректный вариант:

if (isValid) {
  submit();
}

Логические ошибки в циклах (sonarjs/no-inverted-boolean-check)

Правило выявляет инвертированные проверки, усложняющие чтение.

if (!isActive === false) {
  run();
}

Такой код ухудшает понимание и часто является результатом неправильного рефакторинга.


Проверка вложенных условий и ранних выходов

Плагин активно рекомендует использовать ранние возвраты вместо глубокой вложенности:

Проблемный вариант:

function validate(user) {
  if (user) {
    if (user.isActive) {
      if (user.hasAccess) {
        return true;
      }
    }
  }
  return false;
}

Более читаемая структура:

function validate(user) {
  if (!user) return false;
  if (!user.isActive) return false;
  if (!user.hasAccess) return false;
  return true;
}

Дублирование кода (sonarjs/no-duplicated-branches)

Правило анализирует ветвления, где выполняется одинаковый код.

if (isAdmin) {
  logAccess();
} else {
  logAccess();
}

Такой код не несёт различий между ветками и может быть упрощён.


Избыточные условия завершения (sonarjs/no-useless-catch)

Выявляет блоки try/catch, которые не добавляют обработки ошибок.

try {
  execute();
} catch (e) {
  throw e;
}

Такой код фактически не изменяет поведение программы и только усложняет структуру.


Проверка логической полноты условий

Плагин анализирует:

  • отсутствие покрытий всех веток
  • потенциально недостижимые блоки
  • пересекающиеся диапазоны условий

Пример:

if (score > 90) {
  grade = "A";
} else if (score > 80) {
  grade = "B";
} else if (score > 70) {
  grade = "C";
} else if (score > 85) {
  grade = "D";
}

Последнее условие никогда не выполнится, так как перекрывается предыдущим диапазоном.


Настройка правил

Конфигурация может быть детализирована:

{
  "plugins": ["sonarjs"],
  "rules": {
    "sonarjs/cognitive-complexity": ["warn", 15],
    "sonarjs/no-duplicated-conditions": "error",
    "sonarjs/no-identical-expressions": "error",
    "sonarjs/no-empty-function": "warn"
  }
}

Возможность тонкой настройки позволяет адаптировать строгие проверки под реальные ограничения проекта.


Игнорирование и исключения

Как и в ESLint, можно отключать правила локально:

// eslint-disable-next-line sonarjs/cognitive-complexity
function complexFunction() {
  // допустимая сложность
}

Или для отдельных блоков:

/* eslint sonarjs/no-duplicated-conditions: "off" */

Влияние на архитектуру проекта

Использование плагина приводит к системным изменениям в стиле кодирования:

  • функции становятся короче
  • логика дробится на атомарные блоки
  • уменьшается вложенность условий
  • повышается предсказуемость поведения
  • упрощается тестирование

Код начинает строиться вокруг принципа «минимальной когнитивной нагрузки», что особенно важно в крупных кодовых базах.


Ограничения анализа

Несмотря на глубину, анализ имеет ограничения:

  • не всегда понимает бизнес-логику
  • может давать ложные срабатывания в сложных доменных моделях
  • не заменяет ревью кода
  • не анализирует runtime-поведение

Поэтому результаты следует рассматривать как рекомендации по улучшению структуры, а не как абсолютные ошибки.