Конфигурация
utilsJIT принимает объект настроек.
import { defineConfig } from 'vite'
import { utilsJIT } from '@vueland/utils-jit'
export default defineConfig({
plugins: [
utilsJIT({
include: [/\.(vue|js|ts|jsx|tsx|html|svelte|astro)$/],
exclude: [/src\/fixtures/],
emitFile: false,
outFile: 'src/.generated/utils-jit.css',
breakpoints: {
xs: 0,
sm: 600,
md: 960,
lg: 1280,
xl: 1920,
xxl: 2560,
},
debug: false,
}),
],
})Плагин фреймворка (@vitejs/plugin-vue, @vitejs/plugin-react и т.д.) добавляйте в тот же массив plugins, если он нужен приложению.
Options
includeArray<string | RegExp>[/\.(vue|js|ts|jsx|tsx|html|svelte|astro)$/]excludeArray<string | RegExp>emitFilebooleanfalseoutFilestringsrc/.generated/utils-jit.cssemitFile: true.breakpointsRecord<string, number>{ xs: 0, sm: 600, md: 960, lg: 1280, xl: 1920, xxl: 2560 }attrsAttrRule[][]rulesUtilityRule[][]variantsVariantMap{}bannerstring/* @vueland/utils-jit: generated utilities */emitEmptyFilebooleantruedebugbooleanfalseemitFile
По умолчанию CSS отдаётся виртуальным модулем virtual:utils-jit.css и на диск не пишется — потребитель импортирует модуль:
import 'virtual:utils-jit.css'Если нужно увидеть результат файлом (для дебага, инспекции диффов, внешних инструментов), включите emitFile — CSS дополнительно запишется в outFile. Доставку это не меняет: импортировать всё равно нужно виртуальный модуль.
utilsJIT({
emitFile: true,
})outFile
Путь до дебаг-файла относительно root Vite-проекта. Используется только при emitFile: true — в обычном режиме файл не пишется.
utilsJIT({
emitFile: true,
outFile: 'src/styles/generated/utils.css',
})Импортировать по-прежнему нужно виртуальный модуль, а не этот файл:
import 'virtual:utils-jit.css'include
Список паттернов для файлов, которые нужно анализировать.
По умолчанию:
;[/\.(vue|js|ts|jsx|tsx|html|svelte|astro)$/]Пример:
utilsJIT({
include: [/\.(vue|ts)$/],
})Vue, React, Preact, Solid, Svelte, Astro, HTML, JS и TS файлы включены по умолчанию. Для других типов файлов добавьте их расширения явно:
utilsJIT({
include: [/\.(vue|js|ts|jsx|tsx|html|svelte|astro|mdx)$/],
})exclude
Список паттернов для файлов и директорий, которые нужно исключить из полного сканирования, transform и HMR.
По умолчанию исключаются:
node_modules
.git
dist
build
coverage
.output
.nuxt
.turbo
.generated
storybook-static
playwright-reportПример:
utilsJIT({
exclude: [/src\/fixtures/, /src\/legacy/, 'storybook-static'],
})breakpoints
Объект брейкпоинтов для адаптивных вариантов. Ключ используется как префикс класса, значение — min-width в пикселях. При передаче мёржится со встроенными дефолтами (xs: 0, sm: 600, md: 960, lg: 1280, xl: 1920, xxl: 2560): переданные ключи переопределяют значения, остальные дефолтные имена сохраняются.
utilsJIT({
breakpoints: {
xs: 0,
sm: 600,
md: 960,
tablet: 1280,
lg: 1280,
xl: 1920,
xxl: 2560,
},
})Почему мерж, а не замена
В самостоятельном использовании merge позволяет переопределить одно значение и не потерять встроенные адаптивные префиксы. В Vueland-приложении эти же имена (xs/sm/md/lg/xl/xxl) ещё и общий контракт: @vueland/ui генерит из них предопределённые .md\:pa-4, .lg\:d-flex и встроенные адаптивные пропсы CRow/CCol, а useBreakpoints отдаёт их в рантайм. Поэтому можно переопределять значения и добавлять новые имена (например tablet: 1280), но дефолтные имена остаются доступными.
После этого новый префикс можно использовать в JIT-классах:
<div class="sm:w-[640px] tablet:w-[1024px] lg:w-[1280px]"></div>Vueland UI: кастомные имена брейкпоинтов в grid-компонентах
Кастомные имена компилируются в CSS сетки, но Vue-компоненты не получают новые пропы для этих ключей. Например, breakpoints: { tablet: 1280 } сгенерирует tablet-классы сетки, но не сделает <c-col tablet="6"> или <c-row align-tablet="center"> валидными пропами.
Для кастомных имён используйте обычные class/:class:
<template>
<c-row class="tablet:justify-center">
<c-col cols="12" class="tablet-6">Половина ширины с tablet</c-col>
<c-col cols="12" class="tablet-4 tablet:offset-1">Треть ширины с tablet</c-col>
</c-row>
</template>Дефолтные breakpoint-пропы вроде sm="6", md="4", align-md="center" и justify-lg="space-between" остаются доступными и используют значения, настроенные через utilsJIT.
Ограничения на имена ключей
Ключи могут быть любыми строками — они становятся CSS-префиксами и валидны в этом контексте. Однако если вы используете @vueland/ui и хотите, чтобы те же брейкпоинты применялись к предопределённым SCSS-утилитам (sm:d-flex, md:pa-4), ключи должны быть валидными SCSS-идентификаторами: не могут начинаться с цифры.
@vueland/ui)sm, md, xxl'2xl', '3xl'Если @vueland/ui SCSS не используется, ключи вида '2xl' работают без ограничений.
attrs
Используйте attrs, чтобы сканировать пользовательские компонентные пропы или атрибуты, литеральные значения которых создают utility-классы в runtime. Каждая запись настраивается через defineAttr; примеры, валидаторы и поведение color-пропа @vueland/ui описаны на странице Custom attrs.
variants
Пользовательские variants позволяют расширять синтаксис состояний.
utilsJIT({
variants: {
hocus: {
kind: 'selector',
value: '&:hover,&:focus',
},
selected: {
kind: 'attribute',
value: '[aria-selected="true"]',
},
tablet: {
kind: 'media',
value: 900,
},
dark: {
kind: 'selector',
value: '[data-theme="dark"] &',
},
},
})emitEmptyFile
Если emitEmptyFile: true (по умолчанию) и utility-классы не найдены, виртуальный модуль отдаёт плейсхолдер-комментарий (и тот же текст пишется в дебаг-файл при emitFile):
/* @vueland/utils-jit: no utilities found */Если emitEmptyFile: false, при отсутствии utility-классов отдаётся пустой CSS.
utilsJIT({
emitEmptyFile: false,
})Работа со строками классов
Плагин сканирует статические class-like строки в файлах, которые подходят под include. Он понимает обычный HTML/Vue class, Vue :class, React/Preact className, а также строковые литералы в массивах и объектах.
Vue-примеры:
<template>
<div class="w-[200px]"></div>
<div :class="['w-[200px]', active && 'px-[16px]']"></div>
<div :class="{ 'radius-[12px]': rounded }"></div>
</template>React-пример:
export function Card({ active }: { active: boolean }) {
return <div className={active ? 'w-[320px] px-[16px]' : 'w-[240px] px-[12px]'}>Content</div>
}Кроме классов, плагин может сканировать пользовательские литеральные атрибуты, настроенные через attrs. В проектах с @vueland/ui встроенный проп color сканируется автоматически.
Runtime-значения не вычисляются. Класс должен существовать в исходном коде как статический токен.
Не сработает:
<script setup lang="ts">
const width = 320
</script>
<template>
<div :class="`w-[${width}px]`"></div>
</template>Сработает:
<template>
<div :class="isWide ? 'w-[320px]' : 'w-[240px]'"></div>
</template>Как работает генерация
Во время запуска Vite плагин:
- Обходит файлы проекта.
- Пропускает служебные директории вроде
node_modules,.git,dist,build,.generatedи других. - Анализирует только файлы, подходящие под
include. - Извлекает utility-токены.
- Валидирует значения.
- Отдаёт итоговый CSS виртуальным модулем
virtual:utils-jit.css(и опц. дебаг-файлом приemitFile).
Во время разработки плагин обновляет CSS инкрементально:
- добавляет правила для новых токенов;
- удаляет правила, если токен больше нигде не используется;
- не удаляет правило, если такой же токен используется в другом файле;
- пропускает повторную токенизацию файлов с неизменившимся содержимым;
- переиспользует cache разбора токенов и CSS-правил;
- инвалидирует виртуальный модуль для горячей замены (HMR) без перезагрузки страницы.
Ограничения и безопасность
Чтобы не генерировать небезопасный или некорректный CSS, плагин ограничивает arbitrary-значения:
- минимальная длина токена:
5; - максимальная длина токена:
180; - максимальная длина значения:
160; - запрещены
;,{,},<,>; - запрещены CSS comments внутри значения;
- значение должно содержать хотя бы одну букву или цифру;
- разрешены только безопасные символы для CSS-значений.
Эта проверка применяется к полученному значению встроенных правил и пользовательских defineRule до вызова validate или declaration конкретного правила.
Поэтому такие классы будут проигнорированы:
<div class="w-[;]"></div>
<div class="w-[{}]"></div>
<div class="w-[<script>]"></div>
<div class="w-[...........................................]"></div>Рекомендации
Используйте Utils JIT для точечных arbitrary-значений и проектных генерируемых утилит. Лучше всего он работает как сфокусированный слой рядом с дизайн-системой: повторяющиеся семантические решения держите в theme tokens, presets или вариантах компонентов, а JIT используйте для значений и правил, которые действительно нужно генерировать по исходникам.
Хорошо:
<template>
<c-card class="max-w-[720px] px-[24px] radius-[16px]"> Content </c-card>
</template>Также хорошо подходят локальные пользовательские утилиты:
defineRule({
name: 'grid-cols',
matcher: /^grid-cols-(\d+)$/,
declaration: (value) => ({
display: 'grid',
gridTemplateColumns: `repeat(${value}, minmax(0, 1fr))`,
}),
})Если значение или поведение становится глобальным решением дизайн-системы, лучше перенести его в theme token, preset или вариант компонента.
Устранение проблем
Утилиты не применяются
Проверьте, что:
utilsJIT()добавлен вvite.config.ts;- приложение импортирует
virtual:utils-jit.cssв точке входа; - в проекте есть хотя бы один поддерживаемый utility-класс;
- файл с классами подходит под
include; - файл не попадает под
exclude.
Если utility-классы не найдены и emitEmptyFile: true, виртуальный модуль отдаёт комментарий:
/* @vueland/utils-jit: no utilities found */Если emitEmptyFile: false, при отсутствии классов отдаётся пустой CSS.
Если включён
emitFile: true, но дебаг-файл не появился — проверьте, что путьoutFileкорректен и пишется в пределах проекта.
Класс есть, но CSS не генерируется
Проверьте, что:
- файл подходит под
include; - файл не попадает под
exclude; - класс написан статически, а не собирается в runtime;
- значение проходит валидацию;
- utility поддерживается встроенными правилами или добавлен через
rules; - variant существует в
breakpointsилиvariants.
Не работает адаптивный префикс
Префикс должен быть именем существующего брейкпоинта. Дефолтный набор — xs, sm, md, lg, xl, xxl; чтобы добавить свой (например tablet), передайте его в breakpoints — он смёржится с дефолтными:
utilsJIT({
breakpoints: {
tablet: 900, // добавится к дефолтным xs/sm/md/lg/xl/xxl
},
})Если вы также используете SCSS-утилиты
@vueland/ui, избегайте ключей начинающихся с цифры ('2xl','3xl'). См. Ограничения на имена ключей.
Не работает custom rule
Проверьте, что matcher описывает именно utility-часть без variants.
Для класса:
<div class="hover:surface-[#fff]"></div>matcher должен матчить:
surface-[#fff]