@ecss/vite-plugin
v0.1.1
Published
Vite plugin for ECSS — transforms .ecss files into CSS + JS with HMR support.
Maintainers
Readme
💎 Особенности
- ⚡ Нативная интеграция с Vite — использует Vite Plugin API напрямую
- 🔥 HMR — горячая перезагрузка
.ecss-файлов без полного обновления страницы - 🎨 Виртуальный CSS — генерированный CSS инжектируется через нативный CSS-пайплайн Vite
- 🏃 Runtime — автоматически резолвит
virtual:ecss/runtimeдо реальных хелперов из@ecss/transformer - 🧩 Фреймворк-независим — поддерживает React (
className), Vue / Svelte / Solid (class) и оба варианта одновременно - 📝 TypeScript — типизированный API, generic-типы для
.ecss-импортов через./client - ⚙️ Конфиг — читает
ecss.config.jsonиз корня проекта, явные опции имеют приоритет
📦 Установка
npm install @ecss/vite-pluginили
pnpm add @ecss/vite-pluginили
yarn add @ecss/vite-plugin🚀 Быстрый старт
// vite.config.ts
import { defineConfig } from 'vite';
import ecss from '@ecss/vite-plugin';
export default defineConfig({
plugins: [
ecss({
classAttribute: 'className', // 'className' | 'class' | 'both'
}),
],
});Использование в коде
import styles from './button.ecss';
// Позиционный вызов
const props = styles.Button('dark', true);
// → { className: 'Button-a1b2c3', 'data-e-a1b2c3-theme': 'dark', 'data-e-a1b2c3-disabled': '' }
// Именованный вызов
const props2 = styles.Button({ theme: 'dark' });
// Объединение нескольких стилей
const merged = styles.merge(styles.Button('dark'), styles.Icon({ size: 'sm' }));🛠 API
ecss(options?): Plugin
Создаёт экземпляр Vite-плагина. Импортируется как default export:
import ecss from '@ecss/vite-plugin';
const plugin = ecss({ classAttribute: 'class' });⚙️ Опции
interface EcssPluginOptions {
classAttribute?: 'className' | 'class' | 'both'; // по умолчанию 'className'
classTemplate?: string; // по умолчанию '[name]-[hash:6]'
extensions?: string[]; // по умолчанию ['.ecss']
generateDeclarations?: boolean; // по умолчанию false
}| Опция | Описание |
| ---------------------- | ---------------------------------------------------------------------------------- |
| classAttribute | Какие поля (class, className или оба) включить в результат state-функции |
| classTemplate | Шаблон имени класса; поддерживает токены [name] и [hash:N] |
| extensions | Расширения файлов, которые плагин обрабатывает |
| generateDeclarations | При true записывает .ecss.d.ts рядом с каждым .ecss-файлом при старте сборки |
ecss.config.json
Опции можно задать в ecss.config.json в корне проекта — явные опции плагина имеют приоритет:
{
"classAttribute": "class",
"classTemplate": "[name]-[hash:8]",
"generateDeclarations": true
}Шаблон имени класса
Строка classTemplate поддерживает два токена:
| Токен | Описание |
| ---------- | ----------------------------------------------- |
| [name] | Идентификатор @state-def (например, Button) |
| [hash] | Первые 6 символов SHA-256 от filePath + name |
| [hash:N] | Первые N символов хеша |
Пример: "[name]-[hash:8]" для Button даст что-то вроде Button-a1b2c3d4.
📐 Как это работает
Плагин встраивается в pipeline Vite:
transform— когда Vite встречает.ecss-файл, плагин парсит его через@ecss/parser, трансформирует AST в CSS + JS через@ecss/transformerи возвращает JS с виртуальным CSS-импортомresolveId+load— виртуальный CSS-импорт (?ecss&lang.css) резолвится и отдаётся из кеша в памяти; Vite воспринимает его как нативный CSS благодаря суффиксуlang.csshandleHotUpdate— при изменении.ecss-файла плагин пере-обрабатывает его и инвалидирует виртуальный CSS-модуль, чтобы Vite применил HMR
Модуль virtual:ecss/runtime резолвится до реального пакета @ecss/transformer/runtime, чтобы сгенерированный JS мог вызывать _h() и merge() в рантайме.
📐 TypeScript-типы для .ecss-импортов
Через language service plugin (@ecss/typescript-plugin)
Для точных per-file типов подключи @ecss/typescript-plugin в tsconfig.json — он генерирует типы прямо в IDE без дополнительных файлов.
Через generic-типы (./client)
Если language service plugin недоступен, добавь reference на @ecss/vite-plugin/client в tsconfig.json:
{
"compilerOptions": {
"types": ["@ecss/vite-plugin/client"]
}
}Это даёт общий тип для всех .ecss-импортов:
declare module '*.ecss' {
const styles: Record<
string,
(...args: any[]) => Record<string, string | undefined>
> & {
merge: (
...objects: Record<string, string | undefined>[]
) => Record<string, string | undefined>;
};
export default styles;
}Через generateDeclarations
При generateDeclarations: true плагин записывает точный .ecss.d.ts рядом с каждым .ecss-файлом. Подходит для Svelte и других инструментов, которые не загружают tsserver-плагины.
🔧 Разработка
Сборка:
pnpm build # production
pnpm dev # watch modeТесты:
pnpm test
pnpm test:watchПроверка типов:
pnpm typecheckЛинтинг и форматирование:
pnpm lint # oxlint
pnpm lint:fix # oxlint --fix
pnpm fmt # oxfmt
pnpm fmt:check # oxfmt --check👨💻 Автор
Разработка и поддержка: Руслан Мартынов
Если нашёл баг или есть предложение — открывай issue или отправляй pull request.
📄 Лицензия
Распространяется под лицензией MIT.
