Loader css

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

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


Основные режимы обработки CSS

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

  • css — стандартная обработка CSS как отдельного файла или части общего CSS-бандла
  • local-css — обработка с изоляцией классов (CSS Modules-стиль поведения)
  • комбинированные сценарии через плагины

Базовый режим css применяется для большинства типичных сценариев.


Импорт CSS как модуля

CSS может быть импортирован напрямую из JavaScript или TypeScript:

import './styles.css';

При использовании loader css Esbuild:

  • анализирует CSS-файл как модуль
  • включает его в граф зависимостей
  • выносит стили в отдельный выходной CSS-файл (или инлайнит при определённых конфигурациях)

Поведение по умолчанию

Если включена опция сборки:

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

и присутствует CSS-импорт, то:

  • CSS автоматически извлекается в отдельный файл
  • JavaScript остаётся чистым от стилей
  • формируется дополнительный asset в выходной директории

Настройка loader для CSS

Loader задаётся через API или CLI:

CLI

esbuild app.js --bundle --loader:.css=css --outfile=dist/out.js

JavaScript API

import * as esbuild from 'esbuild';

esbuild.build({
  entryPoints: ['app.js'],
  bundle: true,
  outfile: 'dist/out.js',
  loader: {
    '.css': 'css'
  }
});

Здесь .css ассоциируется с режимом css, который определяет поведение обработки.


Внутренний механизм обработки CSS

При встрече CSS-файла в графе зависимостей происходит несколько этапов:

1. Парсинг как текстового ресурса

CSS читается как строка и анализируется на уровне синтаксических правил:

  • селекторы
  • декларации
  • @-правила

2. Встраивание в граф зависимостей

Каждый импорт CSS становится узлом графа сборки. Это позволяет:

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

3. Генерация выходного CSS

На этапе вывода Esbuild:

  • объединяет все CSS-узлы
  • нормализует порядок правил
  • удаляет неиспользуемые части (при определённых оптимизациях)

Поведение при нескольких CSS-файлах

Если проект содержит несколько импортов:

import './reset.css';
import './layout.css';
import './theme.css';

Esbuild:

  • сохраняет порядок импортов
  • объединяет стили в один файл (по умолчанию)
  • либо разделяет их при использовании code splitting

Взаимодействие с JavaScript

CSS loader не просто подключает стили, но и интегрируется с системой модулей.

Побочный эффект импорта

import './styles.css';

Такой импорт:

  • не возвращает значение
  • выполняется ради эффекта
  • гарантирует включение CSS в сборку

Условные импорты

CSS может импортироваться динамически:

if (theme === 'dark') {
  import('./dark.css');
}

В этом случае Esbuild:

  • создаёт отдельный чанкированный CSS-ресурс
  • подключает его только при загрузке соответствующего JS-кода

CSS Modules через loader

Режим local-css используется для изоляции классов.

import styles from './button.css';

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

loader: {
  '.css': 'local-css'
}

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

  • каждый класс преобразуется в уникальное имя
  • создаётся JS-объект соответствий
  • CSS остаётся валидным, но с изменёнными селекторами

Пример трансформации:

.button {
  color: red;
}

Может быть преобразован в:

.button_hash123 {
  color: red;
}

А в Jav * aScript:

styles.button // "button_hash123"

Оптимизация CSS при сборке

Esbuild выполняет базовые оптимизации:

Удаление лишних пробелов

  • минимизация whitespace
  • сокращение структуры

Слияние дублирующихся правил

Если несколько правил совпадают:

.a { color: red; }
.a { color: red; }

результат будет объединён.


CSS и code splitting

При включённом code splitting:

esbuild app.js --bundle --splitting --format=esm

CSS ведёт себя аналогично Jav * aScript:

  • создаются отдельные CSS chunks
  • каждый chunk соответствует части графа
  • загрузка происходит лениво

Подключение CSS из node_modules

Esbuild обрабатывает CSS из зависимостей:

import 'library/dist/style.css';

Особенности:

  • CSS из node_modules обрабатывается тем же loader’ом
  • зависимости автоматически включаются в граф
  • нет необходимости в дополнительных резолверах

Взаимодействие с PostCSS и плагинами

Хотя loader сам по себе не является трансформером, он может быть расширен через плагины PostCSS.

Плагины позволяют:

  • добавлять автопрефиксы
  • трансформировать нестандартный синтаксис
  • внедрять CSS-in-JS обработку

Пример интеграции:

plugins: [
  {
    name: 'postcss',
    setup(build) {
      build.onLoad({ filter: /\.css$/ }, async (args) => {
        const css = await fs.promises.readFile(args.path, 'utf8');
        const result = await postcss.process(css);
        return { contents: result.css, loader: 'css' };
      });
    }
  }
]

Inline CSS через loader

Хотя основной режим предполагает отдельный файл, возможна инлайнизация:

  • через плагины
  • через модификацию loader pipeline

В этом случае CSS может быть:

  • преобразован в строку
  • вставлен в <style> через JS
  • включён прямо в бандл

Ограничения CSS loader

Несмотря на гибкость, существуют ограничения:

  • нет полноценного CSS AST API на уровне loader
  • отсутствует встроенный PostCSS pipeline
  • нет сложных условных трансформаций без плагинов
  • минимальная поддержка runtime-интеракций

Поведение при ошибках

При синтаксических ошибках CSS:

  • сборка останавливается
  • выводится позиция ошибки
  • указывается файл и строка

Пример типов ошибок:

  • незакрытые скобки
  • некорректные директивы
  • битый синтаксис селекторов

Влияние loader на производительность

CSS loader в Esbuild оптимизирован под скорость:

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

Это позволяет:

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