Структура типичного проекта с esbuild

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

Основная идея организации проекта заключается в том, что исходный код располагается отдельно от артефактов сборки, а сама сборка выполняется либо через CLI-команды, либо через программный API esbuild. Это формирует устойчивый шаблон структуры, который легко масштабируется от небольших библиотек до полноценных фронтенд-приложений.


Наиболее распространённый вариант структуры проекта выглядит следующим образом:

project/
  src/
    index.js
    app.js
    components/
    utils/
  dist/
  build/
  public/
  package.json
  esbuild.config.js

src — исходный код

Каталог src является центральной частью проекта. В нём хранится весь исходный JavaScript/TypeScript код до трансформации:

  • index.js или main.js — точка входа
  • components/ — переиспользуемые модули интерфейса
  • utils/ — вспомогательные функции
  • services/ — логика взаимодействия с API
  • styles/ (если CSS обрабатывается через esbuild-плагины)

В случае использования TypeScript структура расширяется файлами .ts и .tsx, при этом esbuild обрабатывает их напрямую без необходимости предварительной компиляции через tsc.


dist — результат сборки

Каталог dist используется как выходной для собранных файлов:

  • dist/bundle.js
  • dist/bundle.css (если CSS включён в сборку)
  • dist/assets/*

Особенность esbuild заключается в том, что этот каталог часто полностью пересоздаётся при каждой сборке. Инкрементальная логика реализуется внутри процесса, а не через сохранение артефактов на диске.


public — статические файлы

Каталог public содержит ресурсы, которые не проходят через сборку:

  • HTML-файлы (index.html)
  • изображения
  • favicon
  • статические JSON-файлы

При использовании dev-серверов этот каталог часто копируется в dist без изменений.


Файл конфигурации esbuild

Несмотря на то, что esbuild можно использовать напрямую из CLI, в большинстве проектов создаётся файл конфигурации:

esbuild.config.js

Пример базовой конфигурации:

const esbuild = require("esbuild");

esbuild.build({
  entryPoints: ["src/index.js"],
  bundle: true,
  outdir: "dist",
  minify: true,
  sourcemap: true,
  target: "es2018"
});

Конфигурация обычно не становится монолитной — вместо этого она разделяется на логические блоки:

  • конфигурация dev-сборки
  • конфигурация production-сборки
  • конфигурация watch-режима
  • конфигурация серверной сборки (Node.js)

Разделение сборок: dev и prod

В типичном проекте выделяются два режима:

Development-сборка

Характерные параметры:

  • отключена или частично отключена минификация
  • включены sourcemaps
  • используется watch-режим
  • быстрые пересборки

Пример:

esbuild.context({
  entryPoints: ["src/index.js"],
  bundle: true,
  outdir: "dist",
  sourcemap: true
}).then(ctx => ctx.watch());

Production-сборка

Характерные параметры:

  • включена минификация
  • отключены лишние проверки
  • оптимизированный вывод
  • возможное разделение чанков
esbuild.build({
  entryPoints: ["src/index.js"],
  bundle: true,
  outdir: "dist",
  minify: true,
  splitting: true,
  format: "esm"
});

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

Для проектов с несколькими точками входа структура расширяется:

src/
  pages/
    home.js
    about.js
    dashboard.js

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

esbuild.build({
  entryPoints: [
    "src/pages/home.js",
    "src/pages/about.js",
    "src/pages/dashboard.js"
  ],
  bundle: true,
  outdir: "dist",
  splitting: true,
  format: "esm"
});

В результате dist содержит несколько независимых бандлов, а также общие чанки при включённом code splitting.


Подключение TypeScript

При использовании TypeScript структура практически не меняется, но добавляется файл:

tsconfig.json

При этом esbuild:

  • игнорирует типовую проверку
  • использует TS только как трансформер синтаксиса
  • требует отдельного процесса tsc --noEmit для проверки типов (при необходимости)

Типичная структура:

src/
  index.ts
  types/
  modules/

Плагины и расширяемость структуры

Проекты с esbuild часто используют плагины для расширения функциональности:

  • обработка CSS (PostCSS-подобные цепочки)
  • импорт SVG как компонентов
  • алиасы путей
  • интеграция с фреймворками

Структура проекта при этом может включать:

build/
  plugins/
    css-plugin.js
    alias-plugin.js

Пример подключения:

const cssPlugin = require("./build/plugins/css-plugin");

esbuild.build({
  entryPoints: ["src/index.js"],
  bundle: true,
  outdir: "dist",
  plugins: [cssPlugin]
});

Алиасы и модульная организация

Для крупных проектов используется логическая группировка модулей:

src/
  ui/
  core/
  api/
  state/
  config/

Часто добавляются алиасы:

alias({
  "@ui": "./src/ui",
  "@core": "./src/core"
});

Это влияет на структуру импортов:

import Button from "@ui/Button";
import request from "@core/request";

Watch-режим и структура dev-процесса

При разработке структура проекта дополняется временными механизмами:

  • live rebuild
  • in-memory rebuild (без записи на диск)
  • dev-server слой

Иногда добавляется файл:

dev.js
const esbuild = require("esbuild");

esbuild.context({
  entryPoints: ["src/index.js"],
  bundle: true,
  outdir: "dist",
  sourcemap: true
}).then(ctx => {
  ctx.watch();
  ctx.serve({ port: 3000 });
});

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

В монорепозиториях структура меняется:

packages/
  app/
  ui/
  shared/
tools/
  build/

Каждый пакет может иметь собственный esbuild-конфиг или общий конфиг уровня tools/build.

Особенность esbuild в таких структурах — возможность быстро пересобирать отдельные пакеты без глобального пересчёта всего графа зависимостей.


Управление зависимостями проекта

Файл package.json играет ключевую роль:

{
  "scripts": {
    "build": "node esbuild.config.js",
    "dev": "node dev.js"
  }
}

Типичная структура зависимостей:

  • esbuild — основной инструмент
  • плагины сборки
  • runtime-зависимости приложения
  • devDependencies для линтинга и тестирования

Разделение конфигурации на модули

В больших проектах конфигурация esbuild часто разбивается:

build/
  base.js
  dev.js
  prod.js
  shared.js

Пример:

// base.js
module.exports = {
  entryPoints: ["src/index.js"],
  bundle: true
};
// prod.js
const base = require("./base");

module.exports = {
  ...base,
  outdir: "dist",
  minify: true
};

Выходные форматы и структура результата

esbuild поддерживает разные форматы:

  • iife — для браузеров без модулей
  • esm — современный стандарт
  • cjs — Node.js окружение

Это влияет на структуру dist:

dist/
  app.js
  app.esm.js
  app.cjs.js

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


CSS и ассеты в структуре проекта

При обработке CSS и статических файлов структура расширяется:

src/
  styles/
    main.css
  assets/
    logo.svg

Выход:

dist/
  main.css
  assets/
    logo.hash.svg

esbuild может либо встраивать ассеты, либо выносить их отдельно, в зависимости от конфигурации loader’ов.


Общая логика организации проекта

Структура проекта вокруг esbuild обычно строится на трёх принципах:

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

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