Структура metafile: inputs, outputs

Общая модель данных метафайла

Метафайл в Esbuild представляет собой статический отчёт о сборке, который описывает зависимость между исходными файлами и итоговыми бандлами. Он генерируется при включении опции --metafile и сохраняется в формате JSON.

Ключевая идея структуры — отображение полного графа сборки в двух направлениях:

  • inputs — что вошло в сборку и как это использовалось
  • outputs — что получилось на выходе и из каких исходников оно собрано

Эти две секции дополняют друг друга, позволяя анализировать сборку как с точки входа (source-centric), так и с точки результата (bundle-centric).


Раздел inputs

Назначение

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

Каждый ключ в inputs — это путь к исходному файлу. Значение — объект с метаданными о его участии в сборке.


Структура элемента inputs

Типичный элемент выглядит следующим образом:

{
  "src/utils/math.ts": {
    "bytesInOutput": 234,
    "imports": [
      {
        "path": "src/constants.ts",
        "kind": "import-statement"
      },
      {
        "path": "node_modules/lodash/lodash.js",
        "kind": "require-call"
      }
    ]
  }
}

Поле bytesInOutput

bytesInOutput показывает, сколько байт итогового бандла пришлось на данный исходный файл.

Ключевые особенности:

  • учитывается только фактически включённый код
  • отражает вклад файла в итоговый размер
  • полезно для анализа “весовых” модулей

Практическое значение

Это поле используется для:

  • поиска самых “тяжёлых” модулей
  • оптимизации структуры зависимостей
  • выявления неэффективных импортов

Поле imports

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

Каждый элемент массива содержит:

  • path — путь к импортируемому модулю
  • kind — тип импорта

Возможные значения kind

Esbuild различает несколько типов импортов:

  • import-statement — ES Module import
  • require-call — CommonJS require
  • dynamic-import — динамический import()
  • entry-point — входная точка (в некоторых контекстах)

Роль inputs в графе зависимостей

inputs формирует обратную сторону dependency graph:

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

Это особенно важно при анализе больших проектов, где один файл может участвовать сразу в нескольких output-бандлах.


Раздел outputs

Назначение

outputs описывает результат работы сборщика: все файлы, которые были сгенерированы Esbuild.

Каждый ключ — это путь к итоговому файлу (например, dist/app.js), а значение содержит информацию о том, из каких исходников он был собран.


Структура элемента outputs

Пример:

{
  "dist/app.js": {
    "bytes": 84231,
    "inputs": {
      "src/index.ts": {
        "bytesInOutput": 1200
      },
      "src/utils/math.ts": {
        "bytesInOutput": 2300
      }
    },
    "imports": [
      {
        "path": "react",
        "kind": "import-statement"
      }
    ]
  }
}

Поле bytes

bytes — общий размер итогового файла после сборки.

Особенности:

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

Поле inputs внутри outputs

Это одна из ключевых частей метафайла.

Она представляет обратное отображение: какие исходные файлы и в каком объёме попали в конкретный output-файл.

Структура:

"inputs": {
  "src/index.ts": {
    "bytesInOutput": 1200
  }
}

Значение поля bytesInOutput в этом контексте

Здесь оно означает:

  • сколько байт данного исходного файла оказалось внутри конкретного output-бандла
  • позволяет оценить вклад модуля в конкретную сборку

Поле imports в outputs

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

Обычно сюда попадают:

  • внешние пакеты (react, lodash)
  • динамические импорты
  • иногда — мета-зависимости сборки

Связь inputs и outputs

Двусторонняя модель

Метафайл Esbuild строится как двусторонний граф:

  • inputs → показывает “что входит в файл”
  • outputs → показывает “из чего состоит бандл”

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

Анализ снизу вверх (inputs)

  • какие зависимости есть у модуля
  • куда он импортируется
  • насколько он “дорогой” по байтам

Анализ сверху вниз (outputs)

  • из каких файлов состоит бандл
  • какой вклад каждого модуля в итоговый размер
  • какие зависимости увеличивают bundle size

Пример полного фрагмента metafile

{
  "inputs": {
    "src/index.ts": {
      "bytesInOutput": 1500,
      "imports": [
        { "path": "src/app.ts", "kind": "import-statement" }
      ]
    },
    "src/app.ts": {
      "bytesInOutput": 3200,
      "imports": [
        { "path": "react", "kind": "import-statement" }
      ]
    }
  },
  "outputs": {
    "dist/app.js": {
      "bytes": 12000,
      "inputs": {
        "src/index.ts": { "bytesInOutput": 1500 },
        "src/app.ts": { "bytesInOutput": 3200 }
      },
      "imports": [
        { "path": "react", "kind": "import-statement" }
      ]
    }
  }
}

Особенности интерпретации данных

1. Дублирование информации

Одна и та же зависимость может присутствовать:

  • в inputs (как исходный модуль)
  • в outputs.inputs (как часть конкретного бандла)

Это не ошибка, а отражение разных уровней анализа.


2. Несколько output-файлов

При code splitting или multiple entry points:

  • один input может попасть в несколько outputs
  • bytesInOutput будет различаться для каждого случая

3. Tree-shaking и влияние на метафайл

При активном tree-shaking:

  • bytesInOutput уменьшается
  • некоторые inputs могут отсутствовать в outputs
  • структура становится более разреженной

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

Поиск “тяжёлых” модулей

На основе inputs.bytesInOutput:

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

Анализ конкретного бандла

Через outputs:

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

Оптимизация зависимостей

Использование imports.kind позволяет:

  • заменить require на import
  • выявить динамические импорты, создающие чанки
  • оптимизировать границы код-сплиттинга

Диагностика неожиданных зависимостей

imports в обоих разделах помогает:

  • найти случайные зависимости от node_modules
  • обнаружить транзитивные импорты
  • отследить “утечки” библиотек в бандл

Интерпретация метафайла как графа

Метафайл можно рассматривать как ориентированный граф:

  • вершины — модули (inputs и outputs)
  • рёбра — imports
  • веса — bytesInOutput и bytes

В этом графе:

  • inputs задаёт структуру исходного графа зависимостей
  • outputs задаёт проекцию этого графа на итоговые артефакты

Такое представление позволяет использовать метафайл как основу для:

  • визуализации зависимостей
  • анализа сложности проекта
  • автоматической оптимизации сборки