Metafile в Esbuild представляет собой JSON-документ, содержащий полное описание процесса сборки: входные файлы, граф зависимостей, выходные артефакты, размеры модулей и связи между ними. Его основное назначение — анализ сборки вне самого bundler’а, без повторного запуска компиляции.
Ключевые поля верхнего уровня:
На практике основная ценность сосредоточена в связке
inputs → outputs → imports.
Metafile можно рассматривать как ориентированный граф:
Каждый input описывается структурой:
Пример логики узла:
{
"src/app.js": {
"bytes": 1340,
"imports": [
{ "path": "./utils/math.js", "kind": "import-statement" },
{ "path": "react", "external": true }
]
}
}
inputs содержит полное дерево зависимостей до
трансформации.
Каждый ключ — это абсолютный или относительный путь к модулю, а значение — описание его участия в сборке:
Основные параметры:
Esbuild фиксирует не только прямые импорты, но и транзитивные зависимости через цепочки модулей. Это позволяет вручную восстанавливать полный dependency graph без дополнительных инструментов.
outputs описывает финальные файлы, которые будут
записаны на диск или в память.
Каждый output содержит:
Пример структуры:
{
"dist/app.js": {
"bytes": 245000,
"inputs": {
"src/app.js": { "bytesInOutput": 1200 },
"src/utils/math.js": { "bytesInOutput": 800 }
},
"imports": ["react", "react-dom"]
}
}
Метafile позволяет строить граф без дополнительных парсеров.
Алгоритм:
outputsinputs каждого outputПсевдокод:
const graph = new Map();
for (const [outFile, outData] of Object.entries(metafile.outputs)) {
for (const input of Object.keys(outData.inputs)) {
if (!graph.has(input)) graph.set(input, []);
graph.get(input).push(outFile);
}
}
Одна из ключевых задач — определение «тяжёлых» модулей.
Esbuild предоставляет поле bytesInOutput, которое
показывает вклад модуля в конкретный output.
Для расчёта общего влияния:
function calculateImpact(metafile) {
const impact = {};
for (const output of Object.values(metafile.outputs)) {
for (const [input, data] of Object.entries(output.inputs)) {
impact[input] = (impact[input] || 0) + data.bytesInOutput;
}
}
return impact;
}
Это позволяет выявлять:
Metafile часто показывает повторное включение одних и тех же модулей в разных чанках.
Признаки:
bytesInOutput выше ожидаемогоАналитика:
const occurrences = {};
for (const [out, data] of Object.entries(metafile.outputs)) {
for (const input of Object.keys(data.inputs)) {
occurrences[input] = (occurrences[input] || 0) + 1;
}
}
Высокое значение указывает на отсутствие эффективного code splitting.
External imports (например, react, lodash)
не включаются в bundle, но фиксируются в metafile.
Они находятся в:
"imports": [
{ "external": true, "path": "react" }
]
Metafile позволяет реконструировать цепочку:
entry → moduleA → moduleB → moduleC
Алгоритм обхода:
function buildChains(inputs) {
const chains = [];
function dfs(file, path = []) {
const node = inputs[file];
if (!node) return;
const newPath = [...path, file];
for (const imp of node.imports || []) {
if (!imp.external) {
dfs(imp.path, newPath);
}
}
chains.push(newPath);
}
for (const file of Object.keys(inputs)) {
if (inputs[file].entryPoint) {
dfs(file);
}
}
return chains;
}
Metafile позволяет оценить, насколько эффективно удаляется неиспользуемый код.
Признаки плохого tree-shaking:
bytesInOutput при низком количестве
импортируемых символовМетод оценки:
ratio = bytesInOutput / bytesInInput
Чем ближе к 1 — тем меньше эффект оптимизации.
Metafile часто используется как источник для графов (D3, Cytoscape).
Формирование узлов:
const nodes = Object.keys(metafile.inputs).map(id => ({
id
}));
const edges = [];
for (const [file, data] of Object.entries(metafile.inputs)) {
for (const imp of data.imports || []) {
edges.push({
source: file,
target: imp.path
});
}
}
В outputs можно обнаружить:
Метод анализа:
Shared modules — ключ к оптимизации splitting.
Metafile можно использовать для сравнения двух билдов.
Алгоритм:
outputsbytesinputsПример логики:
function diff(metaA, metaB) {
const result = {};
for (const file of Object.keys(metaA.outputs)) {
const a = metaA.outputs[file];
const b = metaB.outputs[file];
result[file] = {
sizeDiff: (b?.bytes || 0) - (a?.bytes || 0)
};
}
return result;
}
Metafile позволяет обнаружить:
Типичные сигналы:
Практический анализ metafile приводит к следующим действиям:
Metafile выступает как низкоуровневый инструмент профилирования сборки, позволяющий анализировать не только результат, но и структуру формирования бандла на уровне графа зависимостей.