CSS-директива @import в контексте esbuild
рассматривается не как строковая вставка, а как часть графа
зависимостей, который строится аналогично JavaScript-модулям. При
включённом бандлинге esbuild анализирует каждый CSS-файл, извлекает
импортируемые ресурсы и формирует единый связанный пакет стилей.
@importПри встрече конструкции вида:
@import "./reset.css";
@import "normalize.css";
esbuild выполняет несколько последовательных шагов:
Определение типа пути
./, ../,
./styles/...)node_modules)Разрешение относительно файла-импортёра
Поиск модуля
node_modules)Определение расширения
.css, а
также другие подключённые загрузчики при расширениях через плагины)При включённой опции bundle: true CSS-импорты не
остаются в итоговом файле. Вместо этого происходит их рекурсивная
инлайн-обработка.
Пример:
/* main.css */
@import "./base.css";
body {
margin: 0;
}
/* base.css */
html {
font-size: 16px;
}
После сборки:
html {
font-size: 16px;
}
body {
margin: 0;
}
Порядок вставки сохраняется согласно графу зависимостей: импортируемые стили всегда оказываются выше по цепочке, если они объявлены раньше в дереве зависимостей.
esbuild строго привязывает разрешение @import к
местоположению файла-источника.
Пример структуры:
styles/
main.css
shared/
reset.css
/* main.css */
@import "./shared/reset.css";
Разрешение происходит следующим образом:
main.cssstyles/./shared/reset.cssstyles/shared/reset.cssОсобенность: если файл перемещается, вся цепочка импортов автоматически переопределяется без дополнительных настроек.
node_modulesЕсли путь не начинается с ./ или ../,
esbuild трактует его как пакетный импорт:
@import "modern-normalize";
Алгоритм разрешения:
node_modules ближайшего уровняpackage.jsonstyle, main
или файловая карта экспорта)Если пакет экспортирует несколько CSS-уровней, esbuild выбирает наиболее прямой путь без промежуточных JS-обёрток.
external и исключений из бандлаЕсли модуль помечен как внешний, например:
external: ["normalize.css"]
то соответствующий @import не будет инлайнен и останется
в итоговом CSS:
@import "normalize.css";
Это используется в сценариях:
@import с media-условиямиCSS допускает расширенную форму:
@import "./print.css" print;
@import "./dark.css" (prefers-color-scheme: dark);
esbuild сохраняет семантику медиа-условий, перенося их на уровень объединённого файла:
Результат после сборки:
@media print {
/* содержимое print.css */
}
@media (prefers-color-scheme: dark) {
/* содержимое dark.css */
}
url() внутри импортируемых CSSХотя @import и url() различаются по
семантике, в esbuild они проходят через единый механизм резолвинга
путей.
.icon {
background-image: url("./icons/edit.svg");
}
Правила:
относительный путь вычисляется от текущего CSS-файла
файл попадает в граф ассетов
при бандлинге может быть:
outdiresbuild строит ориентированный ациклический граф:
@import зависимостиОбход графа выполняется в глубину:
Это гарантирует корректный порядок каскада CSS.
Если один и тот же CSS-файл импортируется несколько раз по разным путям:
@import "./reset.css";
@import "../shared/reset.css";
esbuild:
Это предотвращает:
CSS теоретически допускает циклы:
a.css → b.css → a.css
esbuild:
В итоговом CSS цикл разрывается на уровне повторного включения содержимого.
Если CSS импортирует препроцессорные файлы через плагины:
@import "./theme.scss";
то обработка зависит от loader-конфигурации:
sass-loader (через plugin) сначала компилирует SCSS →
CSS@import уже в финальном
CSSТаким образом @import становится последним этапом
трансформации, а не первым.
splitting и код-сплиттинга CSSПри включении code splitting:
каждый entry-point может формировать отдельный CSS chunk
@import между чанками превращается в:
esbuild старается минимизировать дублирование:
В режиме наблюдения за файлами:
@import регистрируется как зависимостьЭто критично для проектов с большим количеством CSS-модулей, где зависимостей многоуровневая структура.
@importЛогически поведение esbuild можно представить как последовательность:
@import