Одной из важных особенностей Esbuild является встроенная работа с CSS. В отличие от многих сборщиков, где для обработки таблиц стилей требуется подключение большого количества загрузчиков и плагинов, Esbuild способен анализировать, объединять и оптимизировать CSS практически сразу после установки.
Поддержка CSS реализована на уровне ядра инструмента и тесно интегрирована с системой сборки JavaScript-модулей. Благодаря этому CSS может импортироваться непосредственно из JavaScript-файлов, участвовать в графе зависимостей и проходить через этапы оптимизации вместе с остальными ресурсами проекта.
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 будут автоматически разделены на отдельные файлы.
Esbuild позволяет использовать CSS как точку входа без участия JavaScript.
Исходный файл:
body {
margin: 0;
font-family: Arial, sans-serif;
}
Команда:
esbuild src/styles.css --bundle --outfile=dist/styles.css
Результатом станет единый CSS-файл со всеми подключёнными зависимостями.
Такой подход удобен при создании дизайн-систем, библиотек компонентов и отдельных наборов стилей.
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 последовательно обнаружит все зависимости и объединит их в правильном порядке.
Часто таблицы стилей содержат ссылки на изображения:
.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");
}
Хэширование позволяет эффективно использовать кэширование браузера.
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%}
Минификация включает:
При использовании общего флага:
esbuild src/app.js \
--bundle \
--minify \
--outdir=dist
Минифицируются одновременно:
Такой подход упрощает создание production-сборок.
При использовании динамического импорта 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-чанк.
Это особенно полезно для крупных одностраничных приложений.
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
Каждый компонент получает собственную область видимости.
Файл:
.container {
display: flex;
}
.title {
font-size: 24px;
}
Jav * aScript:
import styles from "./layout.module.css";
console.log(styles);
Результат:
{
container: "container_HG72K",
title: "title_PQ93X"
}
Это позволяет обращаться к классам через объект.
Иногда требуется сохранить глобальный класс.
Пример:
: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 может содержать собственные зависимости, которые также будут корректно включены в сборку в зависимости от выбранного загрузчика.
Работа с 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();
Esbuild хранит промежуточные результаты анализа зависимостей.
При повторной сборке изменённые файлы пересчитываются значительно быстрее.
Особенно заметен выигрыш на проектах, содержащих:
Хотя Esbuild предоставляет встроенную поддержку CSS, он не стремится заменить специализированные инструменты постобработки.
Часто используется следующая схема:
CSS
↓
PostCSS
↓
Esbuild
↓
Production bundle
Либо:
CSS
↓
Esbuild
↓
PostCSS
↓
Production bundle
Такой подход позволяет совмещать высокую скорость Esbuild с возможностями экосистемы PostCSS.
Esbuild не компилирует Sass и Less самостоятельно, однако легко интегрируется с соответствующими плагинами.
Типичный процесс:
SCSS
↓
Sass Compiler
↓
CSS
↓
Esbuild
Либо через плагины сборки:
plugins: [
sassPlugin()
]
После преобразования SCSS в CSS дальнейшая обработка выполняется встроенными механизмами Esbuild.
Поддержка CSS встроена непосредственно в архитектуру Esbuild и реализована на языке Go. Благодаря этому достигаются следующие преимущества:
В результате даже проекты с сотнями CSS-файлов собираются значительно быстрее по сравнению со многими традиционными решениями, использующими длинные цепочки загрузчиков и промежуточных преобразований.