Формат сообщений об ошибках и предупреждениях

В системе esbuild сообщения об ошибках и предупреждениях строятся вокруг унифицированной структуры данных, которая существует одновременно в двух формах: внутренней (объекты JavaScript API) и текстовой (вывод CLI). Несмотря на различие представления, оба варианта отражают одну и ту же диагностическую модель, включающую текст сообщения, контекст исходного кода и метаданные о месте возникновения проблемы.

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


Структура сообщений в JavaScript API

При использовании esbuild через JavaScript API ошибки и предупреждения возвращаются в виде массивов errors и warnings, содержащих объекты диагностик.

Типичная структура объекта ошибки:

{
  text: "Unexpected token",
  location: {
    file: "src/index.js",
    line: 12,
    column: 18,
    length: 1,
    lineText: "const x = import.meta.env",
    suggestion: ""
  },
  notes: [],
  detail: undefined
}

Основные поля:

text Основное сообщение ошибки или предупреждения. Содержит краткое описание проблемы без избыточного контекста.

location Объект, описывающий точку возникновения проблемы:

  • file — путь к файлу
  • line — номер строки (1-based)
  • column — номер столбца (1-based)
  • length — длина фрагмента кода, связанного с ошибкой
  • lineText — строка исходного кода целиком
  • suggestion — дополнительная подсказка (используется не всегда)

notes Массив дополнительных сообщений, связанных с основной ошибкой. Используется для цепочек причин, например при ошибках импорта или трансформации.


Формат вывода CLI

В CLI esbuild форматирует ошибки в человекочитаемом виде с акцентом на контекст строки и позиционирование указателя.

Пример типичного вывода:

error: Unexpected token

src/index.js:12:18:
  12 │ const x = import.meta.env
     ╵                  ^

Особенности CLI-формата:

  • Указывается уровень сообщения (error или warning)
  • Присутствует краткий текст ошибки
  • Отображается путь к файлу, строка и колонка
  • Добавляется фрагмент исходного кода
  • Указатель (^ или аналог) визуально показывает точное место

Формат предупреждений

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

Пример:

warning: The "this" expression is undefined in ES module scope

src/module.js:3:1:
  3 │ this.doSomething()
     ╵ ~~~~

Различие между ошибками и предупреждениями заключается не в структуре, а в семантике обработки:

  • ошибки останавливают сборку (если не используется режим --bundle с игнорированием части ошибок в watch/incremental сценариях)
  • предупреждения фиксируются, но не прерывают процесс

Позиционирование ошибок в исходном коде

esbuild уделяет особое внимание точному позиционированию ошибок. Используется комбинация:

  • номер строки
  • номер столбца
  • длина фрагмента
  • текст строки

Эта комбинация позволяет восстановить контекст без полноценного AST-дампа.

Пример интерпретации:

location: {
  line: 5,
  column: 10,
  length: 4,
  lineText: "let value = fooBar()"
}

Из этого формируется визуальный указатель:

let value = fooBar()
          ^^^^

Если длина не определена, используется один символ или позиционный маркер.


Ошибки, связанные с плагинами

При использовании plugin API ошибки могут генерироваться внутри хуков (onResolve, onLoad, transform). В этом случае esbuild добавляет дополнительную информацию о плагине:

{
  text: "Could not resolve dependency",
  location: {
    file: "src/app.js",
    line: 1,
    column: 15
  },
  pluginName: "alias-plugin"
}

Поле pluginName позволяет определить источник ошибки в цепочке плагинов, что особенно важно при сложной композиции трансформаций.


Цепочки причин и дополнительные заметки

Поле notes используется для формирования вложенных диагностик.

Пример:

{
  text: "Failed to resolve import",
  location: {...},
  notes: [
    {
      text: "The file does not exist",
      location: {
        file: "src/app.js",
        line: 1,
        column: 8
      }
    }
  ]
}

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


Влияние source maps на сообщения об ошибках

При включённых source maps (sourcemap: true) esbuild старается отображать исходные позиции до трансформации. Это влияет на:

  • file (может указывать на исходный файл вместо промежуточного)
  • line и column (пересчитываются через mapping)
  • lineText (берётся из оригинального исходника)

Это позволяет сохранять согласованность диагностики даже при агрессивной минификации или транспиляции.


Режимы логирования и форматирование сообщений

Параметр logLevel управляет объёмом и типом выводимых сообщений:

  • silent — отключает все сообщения
  • error — только ошибки
  • warning — ошибки и предупреждения
  • info — добавляет информационные сообщения
  • debug — максимальная детализация

При этом структура объектов errors и warnings не меняется, меняется только уровень их отображения в CLI и API-логике.


Поведение в incremental и watch режимах

В режиме инкрементальной сборки (incremental: true) и watch-режиме esbuild:

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

Это означает, что массив errors представляет только актуальный снимок состояния сборки.


Особенности форматирования в разных средах

CLI и API используют разные стратегии представления:

CLI:

  • человекочитаемый формат
  • визуальные указатели
  • группировка по файлам

JavaScript API:

  • структурированные объекты
  • отсутствие форматирования
  • пригодность для интеграции в IDE, логгеры и системы мониторинга

Ограничения кастомизации вывода

esbuild не предоставляет встроенных механизмов глубокой кастомизации форматирования ошибок. Нельзя:

  • изменить структуру errors
  • переопределить формат CLI вывода
  • внедрить пользовательские шаблоны диагностики

Возможна только внешняя обработка результата API:

const result = await esbuild.build(options)

for (const err of result.errors) {
  console.error(`[${err.location?.file}] ${err.text}`)
}

Такой подход используется для интеграции с CI-системами и кастомными логгерами.


Согласованность между API и CLI

Несмотря на различие представления, CLI фактически сериализует те же объекты, что возвращаются через API. Это обеспечивает:

  • одинаковую семантику ошибок
  • предсказуемое поведение между средами
  • возможность воспроизведения CLI-ошибок через API

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