Опция outbase: управление структурой директорий

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

Без использования outbase структура результирующих файлов может оказаться плоской или сформированной не так, как требуется для дальнейшей сборки, публикации или развертывания приложения.

Чаще всего outbase применяется совместно с параметрами:

  • entryPoints
  • outdir
  • entryNames

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


Проблема сохранения структуры каталогов

Рассмотрим проект со следующей структурой:

src/
├── pages/
│   ├── home/
│   │   └── index.js
│   └── about/
│       └── index.js
└── admin/
    └── dashboard.js

Конфигурация:

await esbuild.build({
  entryPoints: [
    'src/pages/home/index.js',
    'src/pages/about/index.js',
    'src/admin/dashboard.js'
  ],
  bundle: true,
  outdir: 'dist'
});

Esbuild должен создать несколько выходных файлов. Однако при наличии одинаковых имен файлов (index.js) возникает необходимость корректно организовать структуру каталогов внутри dist.

Именно для решения подобных задач используется outbase.


Принцип работы

Предположим, используется следующая конфигурация:

await esbuild.build({
  entryPoints: [
    'src/pages/home/index.js',
    'src/pages/about/index.js',
    'src/admin/dashboard.js'
  ],
  bundle: true,
  outdir: 'dist',
  outbase: 'src'
});

В этом случае Esbuild рассматривает каталог src как базовую точку отсчета.

Результат:

dist/
├── pages/
│   ├── home/
│   │   └── index.js
│   └── about/
│       └── index.js
└── admin/
    └── dashboard.js

Вся структура, расположенная ниже каталога src, переносится в выходной каталог.


Что происходит без outbase

Рассмотрим аналогичный пример:

await esbuild.build({
  entryPoints: [
    'src/pages/home/index.js',
    'src/pages/about/index.js'
  ],
  bundle: true,
  outdir: 'dist'
});

Esbuild автоматически пытается определить общий корневой каталог для всех точек входа.

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

Явное указание:

outbase: 'src'

делает структуру сборки детерминированной и понятной.


Взаимодействие с несколькими точками входа

Наиболее распространённый сценарий использования связан именно с массивом entryPoints.

Пример:

await esbuild.build({
  entryPoints: [
    'src/frontend/main.js',
    'src/backend/server.js',
    'src/tools/generator.js'
  ],
  bundle: true,
  outdir: 'dist',
  outbase: 'src'
});

Результат:

dist/
├── frontend/
│   └── main.js
├── backend/
│   └── server.js
└── tools/
    └── generator.js

Каждая точка входа сохраняет своё положение относительно директории src.

Такой подход удобен для:

  • монорепозиториев;
  • многостраничных приложений;
  • проектов с отдельными сервисами;
  • серверно-клиентских решений.

Использование с шаблонами именования

Особенно мощным инструментом outbase становится в сочетании с entryNames.

Пример:

await esbuild.build({
  entryPoints: [
    'src/pages/home/index.js',
    'src/pages/about/index.js'
  ],
  bundle: true,
  outdir: 'dist',
  outbase: 'src',
  entryNames: '[dir]/[name]-bundle'
});

Результат:

dist/
└── pages/
    ├── home/
    │   └── index-bundle.js
    └── about/
        └── index-bundle.js

Здесь используется несколько специальных плейсхолдеров:

Плейсхолдер Описание
[dir] Каталог относительно outbase
[name] Имя файла без расширения
[hash] Контентный хэш
[ext] Расширение

Опция outbase напрямую влияет на значение [dir].


Влияние на [dir]

Рассмотрим структуру:

src/
├── app/
│   └── main.js
└── admin/
    └── panel.js

Конфигурация:

await esbuild.build({
  entryPoints: [
    'src/app/main.js',
    'src/admin/panel.js'
  ],
  bundle: true,
  outdir: 'dist',
  outbase: 'src',
  entryNames: '[dir]/[name]'
});

Значения плейсхолдера [dir] будут:

Исходный файл Значение [dir]
src/app/main.js app
src/admin/panel.js admin

Результат:

dist/
├── app/
│   └── main.js
└── admin/
    └── panel.js

Если изменить outbase, изменится и вычисляемое значение [dir].


Выбор другой базовой директории

Структура проекта:

project/
└── src/
    ├── app/
    │   └── main.js
    └── admin/
        └── panel.js

Конфигурация:

await esbuild.build({
  entryPoints: [
    'src/app/main.js',
    'src/admin/panel.js'
  ],
  bundle: true,
  outdir: 'dist',
  outbase: '.'
});

Результат:

dist/
└── src/
    ├── app/
    │   └── main.js
    └── admin/
        └── panel.js

Поскольку базовым каталогом стал корень проекта, папка src также вошла в итоговый путь.

Это позволяет гибко контролировать глубину сохранения структуры.


Практический пример для многостраничного сайта

Исходная структура:

src/
├── pages/
│   ├── home/
│   │   └── main.js
│   ├── blog/
│   │   └── main.js
│   └── contacts/
│       └── main.js

Конфигурация:

await esbuild.build({
  entryPoints: [
    'src/pages/home/main.js',
    'src/pages/blog/main.js',
    'src/pages/contacts/main.js'
  ],
  bundle: true,
  outdir: 'public/assets',
  outbase: 'src/pages',
  entryNames: '[dir]/bundle'
});

Результат:

public/
└── assets/
    ├── home/
    │   └── bundle.js
    ├── blog/
    │   └── bundle.js
    └── contacts/
        └── bundle.js

Каталог pages исключается из конечного пути, поскольку именно он указан в качестве базовой директории.


Использование в монорепозиториях

Структура:

packages/
├── web/
│   └── src/
│       └── index.js
├── admin/
│   └── src/
│       └── index.js
└── api/
    └── src/
        └── server.js

Конфигурация:

await esbuild.build({
  entryPoints: [
    'packages/web/src/index.js',
    'packages/admin/src/index.js',
    'packages/api/src/server.js'
  ],
  bundle: true,
  outdir: 'build',
  outbase: 'packages'
});

Результат:

build/
├── web/
│   └── src/
│       └── index.js
├── admin/
│   └── src/
│       └── index.js
└── api/
    └── src/
        └── server.js

Сохраняется структура всех пакетов относительно каталога packages.


Работа через CLI

Опция доступна не только в JavaScript API, но и через командную строку.

Пример:

esbuild src/pages/home/index.js \
        src/pages/about/index.js \
        --bundle \
        --outdir=dist \
        --outbase=src

Результат будет аналогичен использованию JavaScript API.


Работа через Go API

Встроенный API Esbuild для Go также поддерживает данную возможность.

Пример:

result := api.Build(api.BuildOptions{
    EntryPoints: []string{
        "src/pages/home/index.js",
        "src/pages/about/index.js",
    },
    Bundle: true,
    Outdir: "dist",
    Outbase: "src",
})

Логика формирования директорий полностью совпадает с JavaScript-версией.


Когда использование outbase особенно полезно

Многостраничные приложения

Каждая страница имеет собственную точку входа:

pages/
├── home/
├── catalog/
├── profile/
└── settings/

Сохранение структуры упрощает подключение готовых ресурсов и автоматическую генерацию HTML.

Набор независимых сервисов

services/
├── auth/
├── billing/
├── notification/
└── gateway/

Каждый сервис может собираться отдельно, сохраняя собственную директорию в итоговом каталоге.

Монорепозитории

packages/
├── ui/
├── core/
├── admin/
└── docs/

Поддерживается понятное отображение структуры исходного кода в каталоге сборки.

Генерация статических сайтов

При сборке большого количества страниц и разделов структура директорий сохраняется автоматически без необходимости ручной настройки маршрутов вывода.


Типичные ошибки

Неправильно выбранный базовый каталог

Исходная структура:

src/
└── pages/
    └── home/
        └── main.js

Конфигурация:

outbase: 'src/pages/home'

Результат:

dist/
└── main.js

Вся вложенная структура исчезает, поскольку базовая директория находится слишком глубоко.


Слишком высокий уровень базы

Конфигурация:

outbase: '.'

Результат:

dist/
└── src/
    └── pages/
        └── home/
            └── main.js

В итоговую структуру попадают лишние каталоги.


Неверные ожидания от outbase

Опция не влияет на:

  • содержимое файлов;
  • дерево импортов;
  • процесс бандлинга;
  • разделение чанков;
  • минификацию;
  • генерацию sourcemap.

Она управляет исключительно вычислением путей для выходных файлов.


Рекомендации по использованию

  • Явно задавать outbase при наличии нескольких точек входа.
  • Выбирать базовый каталог таким образом, чтобы в выходную структуру попадали только действительно нужные директории.
  • Использовать совместно с entryNames, если требуется полный контроль над именами файлов.
  • Применять одинаковые правила для всех пакетов в монорепозитории.
  • Избегать слишком глубоких и слишком высоких значений базового каталога.
  • Рассматривать outbase как механизм сохранения относительных путей между исходными файлами и результатом сборки.

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