Система сообщений и уровней ошибок

Система сообщений ESLint строится вокруг двух взаимосвязанных уровней: уровня правила (severity) и самого сообщения (message), которое формируется внутри правила при обнаружении проблемного кода. Эти два слоя определяют, как линтер классифицирует нарушения, как они отображаются и как влияют на процесс сборки.


Каждое правило в ESLint может работать в одном из трёх режимов:

  • off (0) — правило отключено, сообщения не генерируются
  • warn (1) — предупреждение, не ломает процесс выполнения
  • error (2) — ошибка, считается критическим нарушением

Конфигурация может задаваться как числовыми значениями, так и строковыми эквивалентами:

{
  "no-unused-vars": "off",
  "eqeqeq": "warn",
  "no-undef": "error"
}

или:

{
  "no-unused-vars": 0,
  "eqeqeq": 1,
  "no-undef": 2
}

Внутри ESLint эти значения нормализуются в числовую шкалу, где именно она участвует в принятии решений о статусе проверки.


Наследование и переопределение уровней

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

  • глобальная конфигурация
  • overrides для файловых шаблонов
  • inline-директивы в коде
{
  "rules": {
    "no-console": "error"
  },
  "overrides": [
    {
      "files": ["*.test.js"],
      "rules": {
        "no-console": "off"
      }
    }
  ]
}

Inline-комментарии дают локальное управление:

console.log("debug"); // eslint-disable-line no-console

или:

/* eslint-disable no-console */

Эти механизмы не изменяют само сообщение правила, но полностью подавляют его генерацию.


Структура сообщения правила

Каждое правило ESLint при срабатывании формирует объект сообщения, который передаётся через context.report. Минимальная форма:

context.report({
  node,
  message: "Unexpected console statement"
});

Однако современный ESLint опирается на более формализованный подход через messageId.

Сообщения через messageId

Внутри meta.messages правило может определять набор шаблонов:

meta: {
  messages: {
    unexpectedConsole: "Unexpected console statement.",
    avoidConsole: "Avoid using console in production code."
  }
}

Использование:

context.report({
  node,
  messageId: "unexpectedConsole"
});

Такой подход обеспечивает:

  • переиспользование текстов сообщений
  • упрощение локализации
  • единообразие формулировок

Интерполяция данных в сообщениях

ESLint поддерживает подстановку значений через объект data:

meta: {
  messages: {
    restrictedName: "Identifier '{{name}}' is restricted."
  }
}
context.report({
  node,
  messageId: "restrictedName",
  data: {
    name: node.name
  }
});

Механизм интерполяции использует шаблон {{ключ}}, подставляя значения из data.


Объект сообщения и его поля

Полный объект, передаваемый в context.report, может включать:

  • node — AST-узел, связанный с нарушением
  • loc — ручное указание позиции (редко используется)
  • message или messageId
  • data — данные для шаблона
  • fix — функция автоматического исправления
  • suggest — альтернативные исправления

Пример с автофиксом:

context.report({
  node,
  messageId: "unexpectedSemicolon",
  fix(fixer) {
    return fixer.remove(node);
  }
});

Автоматические исправления и их влияние на сообщения

Сообщения ESLint не содержат логики исправления, но тесно связаны с возможностью fix.

Если правило объявлено как fixable, это указывается в метаданных:

meta: {
  fixable: "code"
}

Типы:

  • "code" — исправляет код
  • "whitespace" — изменяет только форматирование

Сообщение при этом остаётся диагностическим слоем, не зависящим от исправления.


Система предложений (suggestions)

Помимо fix, ESLint поддерживает множественные варианты исправления:

context.report({
  node,
  messageId: "useConst",
  suggest: [
    {
      messageId: "convertToConst",
      fix(fixer) {
        return fixer.replaceText(varNode.kind, "const");
      }
    }
  ]
});

Каждое предложение может иметь собственное сообщение, что расширяет модель диагностики: одно нарушение → несколько интерпретаций решения.


Разделение severity и сообщений

Важно различать:

  • severity — уровень критичности правила
  • message — текстовое описание проблемы

Сообщение не имеет собственной степени важности. Оно всегда наследует severity от правила.

Это означает:

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

Fatal ошибки и их отличие от правил

Помимо сообщений правил, существует отдельная категория — фатальные ошибки парсинга.

Пример:

  • синтаксическая ошибка JavaScript
  • невозможность разобрать модуль

Такие ошибки:

  • не связаны с context.report
  • не имеют ruleId
  • не подчиняются severity правил

Они формируются парсером (например, Espree или Babel parser) и передаются как критические события уровня анализа файла.


Ограничение предупреждений и режим quiet

Система сообщений дополняется механизмами фильтрации вывода:

  • --quiet — скрывает warnings, оставляет только errors
  • --max-warnings — задаёт порог, после которого процесс завершается с ошибкой

Пример логики:

  • warnings накапливаются
  • если превышен лимит — ESLint возвращает ненулевой код выхода

Это влияет не на генерацию сообщений, а на финальную агрегацию результатов.


Локальные подавления сообщений

ESLint предоставляет точечное управление генерацией сообщений через комментарии:

// eslint-disable-next-line no-debugger
debugger;

или:

/* eslint-disable no-debugger */
debugger;

Также возможно временное включение:

/* eslint-enable no-debugger */

или полное игнорирование строки:

debugger; // eslint-disable-line

Эти механизмы работают на уровне фильтрации, не изменяя сам процесс создания сообщений внутри правил.


Агрегация сообщений и вывод

Каждое сообщение ESLint проходит несколько стадий:

  1. обнаружение в правиле
  2. формирование через context.report
  3. нормализация (message/messageId, data)
  4. привязка к файлу и позиции
  5. группировка по ruleId
  6. вывод через форматтер

Форматтеры (stylish, json, compact) используют единый объект сообщения:

  • ruleId
  • message
  • line, column
  • severity

Таким образом, сообщение становится универсальной единицей анализа.


Множественные сообщения одного правила

Одно правило может генерировать неограниченное количество сообщений в рамках одного файла. При этом каждое сообщение:

  • независимо по позиции
  • имеет собственный AST-узел
  • может иметь собственный messageId

Это делает модель ESLint событийно-ориентированной: правило не возвращает результат, а потоково регистрирует нарушения.


Контекст выполнения и формирование сообщений

context передаётся в каждое правило и содержит:

  • информацию о файле
  • доступ к AST
  • методы report, getSourceCode и др.

Сообщения формируются синхронно во время обхода дерева:

create(context) {
  return {
    Identifier(node) {
      if (node.name === "eval") {
        context.report({
          node,
          messageId: "unexpectedEval"
        });
      }
    }
  };
}

Таким образом, система сообщений является прямым результатом обхода AST, а не постобработки.


Влияние структуры сообщений на масштабируемость правил

Использование messageId и шаблонов:

  • снижает дублирование строк
  • уменьшает стоимость поддержки правил
  • упрощает тестирование (можно проверять messageId вместо текста)
  • позволяет изменять формулировки без изменения логики

Эта архитектура делает сообщения ESLint не просто текстовыми строками, а частью декларативной модели диагностики кода.