Metafile в esbuild представляет собой структурированное описание результата сборки, включающее информацию о входных и выходных файлах, их взаимосвязях, размерах и применённых трансформациях. Этот артефакт используется не для выполнения сборки, а для анализа её структуры и последующей оптимизации бандла.
Основная ценность metafile заключается в возможности точно определить, какие модули попали в итоговый бандл, какие зависимости оказались избыточными, и как именно распределяется вес между частями графа модулей.
Формирование metafile включается через параметр
metafile: true в конфигурации сборки.
import * as esbuild from 'esbuild';
await esbuild.build({
entryPoints: ['src/index.js'],
bundle: true,
outfile: 'dist/app.js',
metafile: true
});
После выполнения сборки результат содержит поле
metafile, представляющее объект с детализированной
информацией о бандле.
Для сохранения метаданных в файл используется отдельная сериализация:
import { writeFileSync } from 'fs';
import * as esbuild from 'esbuild';
const result = await esbuild.build({
entryPoints: ['src/index.js'],
bundle: true,
outfile: 'dist/app.js',
metafile: true
});
writeFileSync('meta.json', JSON.stringify(result.metafile));
Сформированный meta.json становится основой для
дальнейшего анализа.
Metafile в esbuild имеет два ключевых блока:
inputs — все входные файлы и их зависимостиoutputs — итоговые бандлы и информация о том, из каких
модулей они состоятПример упрощённой структуры:
{
"inputs": {
"src/index.js": {
"imports": ["react", "./app.js"],
"format": "esm"
},
"src/app.js": {
"imports": ["./utils.js"]
}
},
"outputs": {
"dist/app.js": {
"inputs": ["src/index.js", "src/app.js", "react"],
"bytes": 245000
}
}
}
Ключевой элемент — обратная связь между входами и выходами. В отличие от обычного бандл-артефакта, metafile показывает не только результат, но и происхождение каждого байта.
Metafile фактически является сериализацией графа модулей. Узлы графа — файлы, рёбра — импорты.
Каждый входной модуль содержит:
Такой формат позволяет строить точную карту зависимостей без повторного анализа исходного кода.
Особенно важным является поле imports, позволяющее
определить:
В разделе outputs каждому выходному файлу сопоставляется
список inputs, из которых он состоит.
Дополнительно esbuild может включать:
bytesInOutput — вклад каждого модуля в итоговый
размерЭто позволяет выявлять модули с непропорционально высоким вкладом в размер бандла.
Пример анализа:
"dist/app.js": {
"inputs": {
"react/index.js": {
"bytesInOutput": 68000
},
"src/app.js": {
"bytesInOutput": 1200
}
}
}
Из этого становится видно, что значительная часть веса приходится на стороннюю библиотеку.
Metafile позволяет обнаруживать зависимости, которые включаются транзитивно, но фактически не используются в runtime-логике.
Типичный сценарий анализа:
Если модуль присутствует в inputs, но его функциональность не используется напрямую, возникает потенциальная зона оптимизации.
Особенно эффективно это работает совместно с tree-shaking механизмами esbuild, так как metafile показывает результат, а не процесс устранения кода.
В сложных проектах один и тот же модуль может попадать в несколько чанков. Metafile позволяет выявить такие случаи через повторяющиеся входы в разных outputs.
Пример:
"outputs": {
"a.js": {
"inputs": ["shared/utils.js"]
},
"b.js": {
"inputs": ["shared/utils.js"]
}
}
Это сигнализирует о дублировании кода между бандлами, особенно актуальном при code splitting.
При включённом code splitting (splitting: true) metafile
становится инструментом анализа границ чанков.
await esbuild.build({
entryPoints: ['src/a.js', 'src/b.js'],
bundle: true,
splitting: true,
format: 'esm',
outdir: 'dist',
metafile: true
});
Результирующий metafile показывает:
Это позволяет корректировать стратегию разделения кода, минимизируя пересечения.
Одним из ключевых применений является сравнение двух сборок.
Процесс включает:
Такой подход позволяет выявлять:
Особенно полезно в CI-процессах, где важно контролировать размер бандла.
Metafile легко поддаётся автоматической обработке.
Пример извлечения самых «тяжёлых» модулей:
import fs from 'fs';
const metafile = JSON.parse(fs.readFileSync('meta.json', 'utf-8'));
const output = metafile.outputs['dist/app.js'];
const modules = Object.entries(output.inputs)
.map(([name, info]) => ({
name,
size: info.bytesInOutput || 0
}))
.sort((a, b) => b.size - a.size);
console.log(modules.slice(0, 10));
Такой анализ позволяет формировать список приоритетных кандидатов для оптимизации.
Metafile часто используется как источник данных для визуализаторов зависимостей. Его структура позволяет строить:
Поскольку данные уже нормализованы esbuild, дополнительный парсинг AST не требуется.
В metafile различаются внутренние и внешние модули. Внешние
зависимости (например, из node_modules) часто оказывают
наибольшее влияние на размер бандла.
Типичная стратегия анализа:
Это позволяет оценить целесообразность замены или lazy-loading отдельных библиотек.
Metafile используется как артефакт сборки в CI-пайплайнах для контроля размеров.
Типовой сценарий:
const totalSize = Object.values(metafile.outputs)
.reduce((sum, out) => sum + (out.bytes || 0), 0);
if (totalSize > 300000) {
process.exit(1);
}
Metafile позволяет выявлять не только очевидные проблемы, но и структурные:
Графовая природа данных делает возможным применение алгоритмов анализа связности и центральности.
При работе с metafile обычно рассматриваются три уровня:
Сопоставление этих уровней позволяет находить несоответствия между архитектурой проекта и фактическим результатом сборки.
Хотя tree-shaking выполняется на этапе сборки, metafile позволяет проверить его результат.
Признаки неэффективного tree-shaking:
bytesInOutput при минимальном
использовании APIВ таких случаях анализ metafile становится основой для пересмотра импортов и структуры модулей.
При наличии нескольких entry points metafile показывает пересечения зависимостей между ними.
Это используется для:
Каждый output содержит собственный набор inputs, что позволяет сравнивать их напрямую.
Metafile выполняет роль промежуточного аналитического слоя между исходным кодом и финальным артефактом. Он не влияет на процесс сборки, но фиксирует его полную структуру.
Это делает его инструментом: