Во время сборки приложений нередко возникает необходимость автоматически добавлять определённый код в начало или конец результирующих файлов. Это может быть лицензия, комментарий с информацией о версии, директивы интерпретатора, вспомогательные переменные, код инициализации среды выполнения или специальные инструкции для браузера и Node.js.
В Esbuild для таких задач предусмотрены две специальные опции:
banner — вставляет код в начало файла;footer — вставляет код в конец файла.Обе опции работают на этапе генерации выходного файла и позволяют модифицировать результат без изменения исходного кода проекта.
Рассмотрим простой пример.
Исходный файл:
console.log("Hello World");
Конфигурация:
await esbuild.build({
entryPoints: ["app.js"],
bundle: true,
outfile: "dist/app.js",
banner: {
js: "// Generated by Esbuild"
},
footer: {
js: "// End of file"
}
});
Результат:
// Generated by Esbuild
(() => {
console.log("Hello World");
})();
// End of file
Содержимое banner оказывается перед всем кодом сборки, а
содержимое footer — после него.
Опции принимают объект, в котором ключом является тип выходного файла.
Общий синтаксис:
banner: {
js: "...",
css: "..."
},
footer: {
js: "...",
css: "..."
}
Поддерживаются различные типы ресурсов:
| Ключ | Назначение |
|---|---|
js |
JavaScript-файлы |
css |
CSS-файлы |
Пример одновременной работы с JavaScript и CSS:
await esbuild.build({
entryPoints: ["src/index.js"],
bundle: true,
outdir: "dist",
banner: {
js: "/* JavaScript Bundle */",
css: "/* CSS Bundle */"
}
});
Одна из наиболее распространённых задач — вставка лицензии в начало файла.
Пример:
banner: {
js: `
/*!
* My Library v1.0.0
* Copyright (c) 2025
* MIT License
*/
`
}
Результат:
/*!
* My Library v1.0.0
* Copyright (c) 2025
* MIT License
*/
(function() {
...
})();
Такой подход часто применяется при публикации библиотек в npm или распространении минифицированных сборок.
Во многих проектах требуется сохранить информацию о версии непосредственно внутри бандла.
Пример:
const version = "2.4.1";
await esbuild.build({
entryPoints: ["src/main.js"],
bundle: true,
outfile: "dist/app.js",
banner: {
js: `const APP_VERSION = "${version}";`
}
});
После сборки переменная окажется доступна в файле:
const APP_VERSION = "2.4.1";
(() => {
...
})();
Это удобно для диагностики, журналирования и отображения версии в интерфейсе.
"use strict"Хотя современные сборщики обычно самостоятельно обеспечивают строгий режим, иногда требуется явно добавить директиву в начало файла.
Пример:
banner: {
js: `"use strict";`
}
Результат:
"use strict";
(() => {
...
})();
Следует учитывать, что размещение дополнительных инструкций перед директивой может изменить её поведение. Поэтому порядок содержимого имеет значение.
Для исполняемых файлов Node.js часто используется специальная строка shebang.
Пример:
banner: {
js: "#!/usr/bin/env node"
}
Результат:
#!/usr/bin/env node
console.log("CLI started");
После назначения прав на выполнение:
chmod +x app.js
файл можно запускать напрямую:
./app.js
Это один из наиболее важных сценариев применения banner
в CLI-приложениях.
Иногда необходимо определить значения до выполнения основного кода.
Пример:
banner: {
js: `
globalThis.__BUILD_DATE__ = "${new Date().toISOString()}";
`
}
После сборки:
globalThis.__BUILD_DATE__ =
"2025-01-15T12:00:00.000Z";
(() => {
...
})();
Теперь переменная доступна во всём приложении.
Иногда полезно выводить информацию о сборке сразу после загрузки файла.
Пример:
banner: {
js: `
console.log("Application loading...");
`
}
Результат:
console.log("Application loading...");
(() => {
...
})();
Такой подход часто используется при отладке производственных сборок.
footer выполняет противоположную задачу — добавляет
содержимое в конец файла.
Пример:
footer: {
js: `
console.log("Application finished loading");
`
}
Результат:
(() => {
...
})();
console.log("Application finished loading");
Код выполняется после завершения работы всего содержимого бандла.
Иногда требуется автоматически добавлять служебную информацию в конец файла.
Пример:
footer: {
js: `
console.log("Build completed");
`
}
После выполнения приложения выводится сообщение:
Build completed
Это удобно при создании внутренних инструментов и тестовых сборок.
Обе опции могут использоваться одновременно.
Пример:
await esbuild.build({
entryPoints: ["src/index.js"],
bundle: true,
outfile: "dist/app.js",
banner: {
js: `
console.log("Start");
`
},
footer: {
js: `
console.log("End");
`
}
});
Результат:
console.log("Start");
(() => {
...
})();
console.log("End");
Порядок выполнения:
banner.footer.Опции поддерживают не только JavaScript, но и CSS.
Исходный стиль:
.button {
color: red;
}
Конфигурация:
banner: {
css: "/* Generated CSS */"
},
footer: {
css: "/* End CSS */"
}
Результат:
/* Generated CSS */
.button {
color: red;
}
/* End CSS */
Для крупных блоков удобнее применять шаблонные литералы.
Пример:
banner: {
js: `
/*
================================
Build Information
================================
Version: 3.2.0
Environment: Production
================================
*/
`
}
Такой формат значительно повышает читаемость конфигурации.
Поскольку значение является обычной строкой JavaScript, его можно формировать программно.
Пример:
const buildDate = new Date().toISOString();
const buildNumber = 145;
await esbuild.build({
entryPoints: ["src/main.js"],
bundle: true,
outfile: "dist/app.js",
banner: {
js: `
const BUILD_DATE = "${buildDate}";
const BUILD_NUMBER = ${buildNumber};
`
}
});
Это позволяет интегрировать Esbuild с CI/CD-пайплайнами и системами автоматической сборки.
При использовании минификации содержимое banner и
footer не удаляется автоматически.
Пример:
await esbuild.build({
entryPoints: ["src/main.js"],
bundle: true,
minify: true,
banner: {
js: "/* Production Build */"
}
});
Результат:
/* Production Build */
(()=>{...})();
Поэтому через данные опции можно гарантированно сохранить важные комментарии и метаданные.
Добавление кода через banner и footer
изменяет итоговое расположение строк в выходном файле.
При включённых sourcemap Esbuild автоматически учитывает вставленные фрагменты и корректирует отображение исходных файлов.
Пример:
await esbuild.build({
sourcemap: true,
banner: {
js: "// Build Banner"
}
});
Отладчик продолжит правильно сопоставлять исходный код и результирующий бандл.
banner: {
js: "/*! MIT License */"
}
banner: {
js: "#!/usr/bin/env node"
}
banner: {
js: `const BUILD_ID = "${process.env.BUILD_ID}";`
}
footer: {
js: `console.log("Debug build");`
}
footer: {
js: `
window.appLoaded = true;
`
}
banner: {
js: `
globalThis.RUNTIME = "production";
`
}
Опции предназначены для вставки небольших фрагментов кода.
Нежелательный вариант:
banner: {
js: `
...сотни строк JavaScript...
`
}
Подобную логику лучше хранить в отдельных модулях и подключать через импорт.
Добавленные переменные становятся частью итогового файла.
Плохой пример:
banner: {
js: `const data = {};`
}
Если внутри сборки уже существует переменная data,
возможно возникновение конфликтов.
Предпочтительнее использовать уникальные имена:
banner: {
js: `const __APP_BUILD_DATA__ = {};`
}
Код из banner выполняется раньше всего остального,
поэтому изменения глобальных объектов могут повлиять на весь бандл.
Пример потенциально опасного решения:
banner: {
js: `
Array.prototype.customMethod = function() {};
`
}
Подобные модификации способны привести к труднообнаружимым ошибкам.
При формировании многострочных строк полезно следить за итоговым форматированием:
banner: {
js: "// Header\n"
}
Это помогает избежать случайного склеивания кода после минификации или преобразований.
Опции работают независимо от выбранного формата:
format: "iife"
format: "esm"
format: "cjs"
Во всех случаях содержимое вставляется в начало или конец генерируемого файла.
Пример для ESM:
banner: {
js: "// ES Module Build"
}
Результат:
// ES Module Build
export {
...
};
Благодаря универсальности banner и footer
становятся удобным механизмом внедрения служебного кода, лицензий,
метаданных, диагностических сообщений и настроек окружения
непосредственно в итоговые файлы, не затрагивая исходную структуру
проекта.