Встроенная поддержка CSS

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

Поддержка CSS реализована на уровне ядра инструмента и тесно интегрирована с системой сборки JavaScript-модулей. Благодаря этому CSS может импортироваться непосредственно из JavaScript-файлов, участвовать в графе зависимостей и проходить через этапы оптимизации вместе с остальными ресурсами проекта.


Обработка CSS как модуля

Esbuild рассматривает CSS как полноценный тип модуля. Это означает, что файл стилей можно импортировать напрямую:

import "./styles.css";

При анализе проекта Esbuild обнаруживает импорт и включает соответствующий CSS-файл в процесс сборки.

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

src/
├── app.js
└── styles.css

Файл app.js:

import "./styles.css";

console.log("Application started");

Команда сборки:

esbuild src/app.js --bundle --outfile=dist/app.js

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

dist/
├── app.js
└── app.css

JavaScript и CSS будут автоматически разделены на отдельные файлы.


Сборка CSS-файлов напрямую

Esbuild позволяет использовать CSS как точку входа без участия JavaScript.

Исходный файл:

body {
  margin: 0;
  font-family: Arial, sans-serif;
}

Команда:

esbuild src/styles.css --bundle --outfile=dist/styles.css

Результатом станет единый CSS-файл со всеми подключёнными зависимостями.

Такой подход удобен при создании дизайн-систем, библиотек компонентов и отдельных наборов стилей.


Импорт CSS через директиву @import

Esbuild умеет обрабатывать стандартную CSS-конструкцию:

@import "./reset.css";
@import "./layout.css";
@import "./theme.css";

Во время сборки содержимое импортируемых файлов объединяется в итоговый бандл.

Структура:

styles/
├── main.css
├── reset.css
├── layout.css
└── theme.css

Файл main.css:

@import "./reset.css";
@import "./layout.css";
@import "./theme.css";

После сборки создаётся единый файл:

/* reset.css */
...

/* layout.css */
...

/* theme.css */
...

Это уменьшает количество сетевых запросов и упрощает доставку стилей браузеру.


Автоматическое разрешение зависимостей

При обработке CSS Esbuild строит дерево зависимостей аналогично JavaScript.

Пример:

@import "./base.css";

Файл base.css:

@import "./variables.css";

Файл variables.css:

:root {
  --primary-color: #2563eb;
}

Esbuild последовательно обнаружит все зависимости и объединит их в правильном порядке.


Работа с изображениями внутри CSS

Часто таблицы стилей содержат ссылки на изображения:

.hero {
  background-image: url("./banner.png");
}

Esbuild может автоматически обрабатывать такие ресурсы.

Пример настройки:

esbuild src/app.js \
  --bundle \
  --loader:.png=file \
  --outdir=dist

После сборки путь будет заменён на корректный URL:

.hero {
  background-image: url("./banner-ABCD1234.png");
}

Изображение будет скопировано в каталог сборки.


Обработка шрифтов

Тот же механизм применяется к шрифтам.

Исходный код:

@font-face {
  font-family: "Inter";
  src: url("./fonts/inter.woff2");
}

Настройка:

esbuild src/app.js \
  --bundle \
  --loader:.woff2=file \
  --outdir=dist

Результат:

@font-face {
  font-family: "Inter";
  src: url("./inter-QX91KL.woff2");
}

Хэширование позволяет эффективно использовать кэширование браузера.


Минификация CSS

Esbuild содержит встроенный механизм минимизации стилей.

Исходный код:

body {
  margin: 0;
  padding: 0;
}

.container {
  width: 100%;
}

Команда:

esbuild src/styles.css \
  --bundle \
  --minify \
  --outfile=dist/styles.css

Результат:

body{margin:0;padding:0}.container{width:100%}

Минификация включает:

  • удаление лишних пробелов;
  • удаление переносов строк;
  • сокращение некоторых значений;
  • устранение избыточных конструкций.

Минификация вместе с JavaScript

При использовании общего флага:

esbuild src/app.js \
  --bundle \
  --minify \
  --outdir=dist

Минифицируются одновременно:

  • JavaScript;
  • CSS;
  • встроенные зависимости.

Такой подход упрощает создание production-сборок.


CSS Code Splitting

При использовании динамического импорта Esbuild способен разделять стили по чанкам.

Пример:

import("./dashboard.js");

Файл dashboard.js:

import "./dashboard.css";

Сборка:

esbuild src/app.js \
  --bundle \
  --splitting \
  --format=esm \
  --outdir=dist

Результат:

dist/
├── app.js
├── dashboard.js
├── app.css
└── dashboard.css

Стили загружаются только тогда, когда загружается соответствующий JavaScript-чанк.

Это особенно полезно для крупных одностраничных приложений.


CSS Modules

Esbuild поддерживает CSS Modules через специальный загрузчик.

Файл:

.button {
  background: blue;
  color: white;
}

Импорт:

import styles from "./button.module.css";

button.className = styles.button;

Настройка:

esbuild src/app.js \
  --bundle \
  --loader:.module.css=local-css \
  --outfile=dist/app.js

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

.button_A1B2C3 {
  background: blue;
  color: white;
}

В Jav * aScript:

styles.button === "button_A1B2C3";

Проблема глобального пространства имён

Обычный CSS создаёт глобальные селекторы:

.button {
  color: red;
}

При большом количестве компонентов возможно пересечение имён.

CSS Modules решает проблему путём генерации уникальных идентификаторов:

.button_x82ks
.button_qw91er
.button_lm44zx

Каждый компонент получает собственную область видимости.


Экспорт классов из CSS Modules

Файл:

.container {
  display: flex;
}

.title {
  font-size: 24px;
}

Jav * aScript:

import styles from "./layout.module.css";

console.log(styles);

Результат:

{
  container: "container_HG72K",
  title: "title_PQ93X"
}

Это позволяет обращаться к классам через объект.


Глобальные селекторы внутри CSS Modules

Иногда требуется сохранить глобальный класс.

Пример:

:global(.theme-dark) {
  background: black;
}

Локальный селектор:

.button {
  color: white;
}

После сборки:

.theme-dark {
  background: black;
}

.button_J82LQ {
  color: white;
}

Глобальный класс остаётся без изменений.


Поддержка вложенных импортов ресурсов

Esbuild способен обрабатывать сложные зависимости внутри CSS.

Пример:

.icon {
  background-image: url("./icons/menu.svg");
}

Файл SVG может содержать собственные зависимости, которые также будут корректно включены в сборку в зависимости от выбранного загрузчика.


Использование API Node.js

Работа с CSS доступна не только через CLI, но и через программный интерфейс.

import * as esbuild from "esbuild";

await esbuild.build({
  entryPoints: ["src/styles.css"],
  bundle: true,
  outfile: "dist/styles.css"
});

Минификация:

await esbuild.build({
  entryPoints: ["src/styles.css"],
  bundle: true,
  minify: true,
  outfile: "dist/styles.css"
});

Генерация карты исходников

Для упрощения отладки можно создавать source maps.

await esbuild.build({
  entryPoints: ["src/styles.css"],
  bundle: true,
  sourcemap: true,
  outfile: "dist/styles.css"
});

После сборки:

dist/
├── styles.css
└── styles.css.map

Браузер сможет сопоставлять итоговый CSS с исходными файлами.


Режим наблюдения

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

CLI-вариант:

esbuild src/styles.css \
  --bundle \
  --watch \
  --outfile=dist/styles.css

Каждое изменение в любом подключённом CSS-файле запускает новую сборку.

Node.js API:

const context = await esbuild.context({
  entryPoints: ["src/styles.css"],
  bundle: true,
  outfile: "dist/styles.css"
});

await context.watch();

Инкрементальная сборка CSS

Esbuild хранит промежуточные результаты анализа зависимостей.

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

Особенно заметен выигрыш на проектах, содержащих:

  • десятки CSS-модулей;
  • большое количество компонентов;
  • множество импортируемых ресурсов.

Совместимость с PostCSS

Хотя Esbuild предоставляет встроенную поддержку CSS, он не стремится заменить специализированные инструменты постобработки.

Часто используется следующая схема:

CSS
 ↓
PostCSS
 ↓
Esbuild
 ↓
Production bundle

Либо:

CSS
 ↓
Esbuild
 ↓
PostCSS
 ↓
Production bundle

Такой подход позволяет совмещать высокую скорость Esbuild с возможностями экосистемы PostCSS.


Совместимость с Sass и Less

Esbuild не компилирует Sass и Less самостоятельно, однако легко интегрируется с соответствующими плагинами.

Типичный процесс:

SCSS
 ↓
Sass Compiler
 ↓
CSS
 ↓
Esbuild

Либо через плагины сборки:

plugins: [
  sassPlugin()
]

После преобразования SCSS в CSS дальнейшая обработка выполняется встроенными механизмами Esbuild.


Особенности производительности

Поддержка CSS встроена непосредственно в архитектуру Esbuild и реализована на языке Go. Благодаря этому достигаются следующие преимущества:

  • высокая скорость анализа зависимостей;
  • быстрое объединение файлов;
  • эффективная минимизация;
  • минимальные накладные расходы на обработку ресурсов;
  • быстрое обновление сборки в режиме разработки.

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