Опция banner и footer: вставка произвольного кода

Во время сборки приложений нередко возникает необходимость автоматически добавлять определённый код в начало или конец результирующих файлов. Это может быть лицензия, комментарий с информацией о версии, директивы интерпретатора, вспомогательные переменные, код инициализации среды выполнения или специальные инструкции для браузера и 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";

(() => {
  ...
})();

Следует учитывать, что размещение дополнительных инструкций перед директивой может изменить её поведение. Поэтому порядок содержимого имеет значение.


Добавление shebang для Node.js

Для исполняемых файлов 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");

Порядок выполнения:

  1. Выполняется код из banner.
  2. Выполняется основной код приложения.
  3. Выполняется код из footer.

Работа с CSS

Опции поддерживают не только 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 */
(()=>{...})();

Поэтому через данные опции можно гарантированно сохранить важные комментарии и метаданные.


Влияние на sourcemap

Добавление кода через banner и footer изменяет итоговое расположение строк в выходном файле.

При включённых sourcemap Esbuild автоматически учитывает вставленные фрагменты и корректирует отображение исходных файлов.

Пример:

await esbuild.build({
  sourcemap: true,

  banner: {
    js: "// Build Banner"
  }
});

Отладчик продолжит правильно сопоставлять исходный код и результирующий бандл.


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

Лицензионные уведомления

banner: {
  js: "/*! MIT License */"
}

Shebang для CLI

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 становятся удобным механизмом внедрения служебного кода, лицензий, метаданных, диагностических сообщений и настроек окружения непосредственно в итоговые файлы, не затрагивая исходную структуру проекта.