Transformer
Как превратить JSON-токены плагина в CSS, SCSS, Swift, UIKit, Kotlin, Android XML и JSON-манифест с помощью CLI Transformer: быстрый старт, команды, справочник YAML-конфигурации и типовые схемы.
Transformer (@sxl-studio/token-transformer, версия 3.4.1): утилита командной строки, которая превращает JSON-файлы токенов из плагина в код. Она работает на компьютере разработчика и в CI, настраивается одним YAML-файлом и пересобирает только то, что изменилось. Нужен Node.js 20 или новее.
| Платформа | Что получается |
|---|---|
css | Пользовательские свойства внутри :root или выбранного селектора |
scss | Переменные $; файлы тем подключают свой root-файл через @use |
swift | Константы SwiftUI и типизированные спецификации, помечены Sendable |
uikit | Константы UIKit и типизированные спецификации, помечены Sendable |
kotlin | Константы Kotlin и data-классы для Compose |
xml | Ресурсы Android: colors.xml, dimens.xml, strings.xml, bools.xml, integers.xml, floats.xml и drawable |
manifest | JSON с метаданными токенов для сайтов документации, просмотрщиков токенов и аудитов |
Для разового экспорта без терминала подойдёт сам плагин, см. Токены в код. Transformer читает те же файлы и тот же config.json.
Быстрый старт за 5 минут
Понадобятся Node.js 20 или новее и папка с JSON-файлами токенов и config.json из плагина. Откуда берётся эта папка, описано в разделе Источники токенов и синхронизация.
- Установите пакет в проект:
npm install --save-dev @sxl-studio/token-transformer.pnpm add -Dиyarn add -Dработают так же. - Создайте стартовый конфиг:
npx sxl-transform init. В текущей папке появится файлsxl-transform.config.yamlс примерами для всех платформ. - Откройте файл. Укажите в
source.tokenDirпапку с токенами и замените имена коллекций и режимов вtokenSetsна те, что есть в вашемconfig.json. Удалите ненужные outputs. - Проверьте конфиг:
npx sxl-transform validate-config. - Соберите:
npx sxl-transform sync.
npm install --save-dev @sxl-studio/token-transformer
npx sxl-transform init
npx sxl-transform validate-config
npx sxl-transform sync
Сгенерированные файлы появятся в outputDir каждого output. Рядом с конфигом утилита записывает sxl-transform.config.state.json: с ним следующий sync пересобирает только те outputs, чьи токены изменились. Файл состояния можно не хранить в Git: без него следующий запуск сделает полную пересборку.
Команды
| Команда | Что делает |
|---|---|
sxl-transform sync | Конвертирует токены и записывает файлы. Команда по умолчанию: sxl-transform --config ./x.yaml тоже запускает её |
sxl-transform validate-config | Проверяет YAML-конфиг и пути в нём, ничего не записывает. Принимает только --config |
sxl-transform init | Создаёт стартовый конфиг. --path <path> задаёт файл, --force перезаписывает существующий |
sxl-transform help [command] | Общая справка или справка по одной команде. --help и -h делают то же |
sxl-transform version | Печатает установленную версию. --version и -v делают то же |
Параметры sync
| Параметр | Что делает | По умолчанию |
|---|---|---|
--config <path> | Путь к YAML-конфигу | sxl-transform.config.yaml |
--mode <mode> | smart пересобирает только изменившиеся outputs, force пересобирает всё | smart |
--force | То же, что --mode force | |
--preserve-existing | Сохранить прежние токены и файлы, добавить новые и обновить совпавшие во всех форматах | выключен |
--state-file <path> | Где хранить файл состояния | <имя-конфига>.state.json рядом с конфигом |
--only-output <id> | Собрать только этот output. Повторяйте для нескольких | все outputs |
--only-file <glob> | Собрать только подходящие выходные файлы. Повторяйте для нескольких. css-app:modes/dark.css ограничивает шаблон одним output | все файлы |
--issue-action <mode> | Что делать при найденных проблемах: ask, debug-stop, debug-continue, autofix или skip. --issues работает как синоним | ask |
--debug-report [path] | Записать debug-отчёт даже при чистом прогоне, при желании по указанному пути | только при проблемах |
--debug-file <path> | Путь к debug-отчёту | <имя-конфига>.debug.md в текущей папке |
--explain | Печатать, почему каждый output пересобран или пропущен | |
--watch | Не завершаться и пересобирать при изменении токенов или конфига. Ctrl+C останавливает | |
--no-state-lock | Отключить блокировку, которая защищает файл состояния от параллельных запусков |
Стратегии sync: smart, force и preserve
У команды sync есть два режима пересборки: smart и force. Preserve — отдельная политика объединения с уже созданными файлами.
| Стратегия | Что происходит | Когда использовать |
|---|---|---|
smart | Добавляет и обновляет токены, удаляет отсутствующие в JSON. Записывает только файлы с изменившимся содержимым | Обычная ежедневная синхронизация |
force | Заново генерирует и записывает все выбранные файлы. Отсутствующие в JSON токены удаляются по умолчанию | Полная контрольная пересборка или восстановление файлов |
preserve | Добавляет и обновляет токены, но сохраняет отсутствующие в JSON. Записывает только изменившиеся файлы | Постепенная миграция, пока старые потребители используют прежние имена |
Важно preserve не является третьим значением --mode. Команда --mode preserve не существует: используйте --mode smart --preserve-existing. Флаг задаётся при запуске и не добавляется в YAML.
# Обычная синхронизация: JSON остаётся источником истины
npx sxl-transform sync --config ./sxl-transform.config.yaml --mode smart --issue-action debug-stop
# Полная пересборка всех настроенных outputs
npx sxl-transform sync --config ./sxl-transform.config.yaml --mode force --issue-action debug-stop
# Миграция: сохранить старые объявления, обновить совпавшие и добавить новые
npx sxl-transform sync --config ./sxl-transform.config.yaml --mode smart --preserve-existing --issue-action debug-stop
Один нейтральный набор scripts для проекта:
{
"scripts": {
"tokens:sync": "sxl-transform sync --config ./sxl-transform.config.yaml --mode smart --issue-action debug-stop",
"tokens:force": "sxl-transform sync --config ./sxl-transform.config.yaml --mode force --issue-action debug-stop",
"tokens:preserve": "sxl-transform sync --config ./sxl-transform.config.yaml --mode smart --preserve-existing --issue-action debug-stop"
}
}
Например, раньше в выходных файлах существовали --color-action и --color-legacy. После рефакторинга значение color.action изменилось, color.legacy удалён из JSON, а color.accent добавлен. Обычный smart и force обновят action, добавят accent и удалят legacy. Preserve обновит action, добавит accent и оставит legacy. Если токен переименован, preserve сохранит прежнее имя и добавит новое; обычный smart при следующей пересборке удалит прежнее.
Preserve читает старые объявления из output-файлов, поэтому перед ним нельзя очищать выходную папку. Для точечного запуска добавьте те же --only-output и --only-file, которые перечислены выше. Изменения за пределами выбранной области останутся ожидающими следующего полного sync.
# только два outputs
npx sxl-transform sync --only-output css-app --only-output swift-app
# только файл тёмной темы из output css-app
npx sxl-transform sync --only-file "css-app:modes/dark.css"
# CI: не ждать ввода, пропускать сломанные токены
npx sxl-transform sync --issue-action skip
Как preserve сопоставляет объявления
Для CSS, SCSS, Swift, UIKit, Kotlin, Android XML и manifest совпадение определяется по публичному имени и области объявления:
| Формат | Область совпадения |
|---|---|
| CSS | Имя с учётом регистра, селектор и окружающие at-правила |
| SCSS | Имя Sass-переменной внутри одного файла-модуля; дефис и подчёркивание эквивалентны |
| Swift / UIKit / Kotlin | Namespace и имя свойства; у Kotlin также package |
| Android XML | Тип ресурса, имя и квалификатор ресурсов, например values-night; drawable обновляется целиком |
| Manifest | Имя, коллекция и режим; перенос JSON между исходными файлами не создаёт дубликат |
Старые объявления, заменённые актуальными в другом файле той же области, не дублируются. Разные Sass-модули и native namespaces остаются независимыми: изменение NewTokens.size не изменяет OldTokens.size. Сохраняются устаревшие выходные файлы и их подключения через indexes.includeGenerated. Native namespace и общие вспомогательные типы получают одного владельца в пределах области компиляции.
Флаг задаётся только в команде, в YAML его нет. Он работает с --mode smart, --force, --watch, --only-output и --only-file. Запуск без флага возвращает обычную генерацию из JSON для выбранных outputs; сохранённые значения будут удалены при пересборке. options.removeStaleOutputs: false отдельно управляет сохранением целых устаревших файлов.
Если state отсутствует, прежние файлы восстанавливаются в учёте по настроенным путям генерации и split-шаблонам. Произвольные файлы вне этих шаблонов не подхватываются. Уже потерянные значения без прежнего output восстановить нельзя. Сохранённые outputs не заменяют JSON-источники при разрешении алиасов.
Повреждённый или неподдерживаемый файл останавливает запуск до записи outputs. Обработчики рассчитаны на генерируемые Transformer структуры; произвольный код custom formatter может потребовать адаптации. CSS с анонимными слоями @layer не объединяется, потому что у слоя нет устойчивого имени. Предупреждения об исходных токенах по-прежнему обрабатываются согласно --issue-action. Пустой glob после подтверждённого удаления прежних источников при сохранённых outputs становится информационным сообщением.
Smart-режим и файл состояния
sync сравнивает содержимое файлов токенов с файлом состояния и пересобирает только те outputs, которые зависят от изменившихся файлов. Сгенерированные файлы тоже проверяются: файл, удалённый или отредактированный вручную, записывается заново. Файлы, чьи токены исчезли, удаляются, если только options.removeStaleOutputs не равен false.
В 3.4.1 smart записывает только действительно отличающиеся файлы, в том числе после изменения конфига или переключения preserve. --mode force заново генерирует и записывает все выбранные outputs. --mode smart --preserve-existing сохраняет отсутствующие в JSON объявления; при переименовании остаются прежнее и новое имена. Предварительная очистка папок не требуется и удалит данные, которые preserve должен сохранить.
Чего ожидать:
- Первый запуск и запуск без файла состояния пересобирают всё. Обновления, меняющие генерацию, включая 3.4.1, однократно сбрасывают старое состояние; следующие прогоны без изменений снова пропускают генерацию.
- Запуск, ограниченный
--only-outputили--only-file, не скрывает остальные изменения: их подхватит следующий полный запуск. При--only-fileустаревшие файлы вне выборки не удаляются. - Пока идёт
sync, файл<state>.lockне даёт запустить второй параллельный прогон. Блокировка старше 10 минут снимается автоматически. - Если ваш скрипт переписывает сгенерированные файлы после
sync, следующий запуск пересоберёт их: содержимое больше не совпадает с состоянием. Запускайте такие скрипты доsyncили пишите в другую папку. --watchследит за папкой токенов, YAML-конфигом иconfig.json, игнорирует выходные папки и никогда не задаёт вопросов: при проблемах записывает debug-отчёт и продолжает.
Какие токены конвертируются
CSS, SCSS, Swift, UIKit и Kotlin распознают следующие группы токенов. Допустимые формы значений и генерируемые поля зависят от платформы; ограничения перечислены ниже.
| Группа | Типы |
|---|---|
| Цвета и заливки | color, gradient, fill, img, opacity |
| Размеры | dimension, number, spacing, sizing |
| Обводки | border, borderWidth, borderRadius, strokeStyle |
| Типографика | typography, fontFamily, fontWeight, fontStyle, fontSize, lineHeight, letterSpacing, paragraphSpacing, paragraphIndent, textCase, textDecoration |
| Эффекты | shadow, blur, backdrop-blur, effects |
| Анимация | transition, duration, cubicBezier, easing |
| Прочее | boolean, text |
Остальные типы:
-
glassраспознаётся и сохраняется в манифесте. У типизированных outputs нет отдельного представления стекла; самостоятельные значения glass не поддерживаются, а слои стекла внутри эффектов сопровождаются диагностикой. -
Токены
customсохраняются как есть. Манифест экспортирует их$valueдословно; остальные платформы пропускают их с предупреждением, потому что у значения нет фиксированной формы. -
template,compositionиgridописывают структуры Figma, а не значения, и не конвертируются никогда.options.unsupportedTypesрешает, будет это ошибкой, предупреждением или молчаливым пропуском.
Токены, скрытые в Figma ($extensions["figma.hide"]), по умолчанию экспортируются: на них часто ссылаются видимые токены. excludeHidden: true у output убирает их.
Ссылки ({color.brand}), числовые выражения и поддерживаемые цветовые модификаторы figma.modify вычисляются перед генерацией. Математика работает только для числовых типов; токен text вида "37-37" никогда не вычисляется. Исходный формат описан в разделе JSON-формат токенов; наличие поля Figma не означает, что любой output умеет его воспроизводить.
Формы значений и ограничения платформ
- Размеры и длительности принимают объекты DTCG
{value, unit}. Цвета DTCG принимают числовые компоненты вsrgb,display-p3,hsl,oklchиlchлибо поддерживаемый резервныйhex. Другим цветовым пространствам и компонентамnoneнужен резервный цвет; некорректная явно заданная альфа отклоняется. - CSS/SCSS принимают строковые градиенты, включая повторяющиеся, сокращённую запись теней и URL изображений. Полной конвертации видео, паттернов Figma и отдельных значений glass нет. Spring easing передаётся приближённо и сопровождается предупреждением.
- SwiftUI, UIKit и Kotlin экспортируют значения и типизированные спецификации. Строковые линейные градиенты сохраняют направление и двойные позиции стопов. Сложная геометрия радиальных/конических градиентов, повторяющиеся градиенты, автоматический интерлиньяж и заливки Figma video/pattern/glass остаются неподдерживаемыми формами значений.
- Фильтры и трансформации изображений, геометрия прогрессивного размытия, слои эффектов noise/texture/glass и оси вариативных шрифтов не воспроизводятся генерируемыми значениями/спецификациями. Диагностика называет поля, которые иначе были бы пропущены незаметно.
- Манифест сохраняет авторские данные разрешённых типов токенов, включая
custom. Он не отрисовывает эффекты. Сохраняйте его рядом с типизированными outputs, если нужны исходные детали.
Неподдерживаемые значения сопровождаются диагностикой и не считаются успешно преобразованными. По умолчанию ошибки останавливают генерацию. Выбирайте описанную политику предупреждений/пропуска только тогда, когда потребитель может намеренно обойтись без этих значений.
Типографика
Токену typography нужны fontFamily, fontWeight, fontSize и lineHeight. CSS и SCSS получают одно сокращённое значение в порядке свойства font: начертание, вес, размер/интерлиньяж, семейство; поля за пределами этой записи вызывают предупреждения. Swift, UIKit и Kotlin получают типизированную спецификацию с полями, включая letterSpacing, textCase, textDecoration и verticalTrim. Отступы абзацев и оси вариативных шрифтов не выводятся. XML раскладывает поддерживаемые поля на ресурсы: <name>_font_family, <name>_font_weight, <name>_font_size, <name>_line_height, <name>_letter_spacing, <name>_text_case, <name>_text_decoration.
Android XML
XML ограничен нативными ресурсами. output у файла XML задаёт папку, например values; drawable попадают в соседнюю папку drawable.
| Токен | Ресурс |
|---|---|
color | colors.xml; значение linear-gradient(...) становится drawable |
gradient, fill, img | drawable/<name>.xml |
dimension, spacing, sizing, borderWidth, borderRadius, paragraphSpacing, paragraphIndent | dimens.xml в dp |
fontSize, lineHeight, letterSpacing | dimens.xml в sp |
text, fontFamily, fontWeight, fontStyle, textCase, textDecoration | strings.xml |
boolean | bools.xml |
number (целое), duration (миллисекунды), opacity (от 0 до 100) | integers.xml |
number с дробной частью | floats.xml как <item type="dimen" format="float">, читается через ResourcesCompat.getFloat |
typography | Раскладывается на strings.xml и dimens.xml, см. выше |
Тени, размытия, стекло, эффекты, переходы, кривые easing, border и strokeStyle не имеют аналога среди ресурсов: они попадают в диагностику и пропускаются.
Справочник конфигурации
Конфиг только в YAML, version: 1. Относительные пути считаются от файла конфига. Неизвестные ключи считаются ошибкой, поэтому опечатку поймает validate-config.
Верхний уровень
| Ключ | Что делает | По умолчанию |
|---|---|---|
version | Версия формата конфига, всегда 1 | обязателен |
extends | YAML-файлы, содержимое которых подмешивается в этот: общая база для нескольких конфигов | [] |
source | Где лежат файлы токенов | обязателен |
projectConfig | Описание коллекций и режимов прямо в YAML для проектов без config.json | нет |
options | Глобальные параметры | см. ниже |
tokenSets | Именованные наборы токенов, минимум один | обязателен |
outputs | Что генерировать, минимум один | обязателен |
source
| Ключ | Что делает | По умолчанию |
|---|---|---|
tokenDir | Папка с JSON-файлами токенов | обязателен |
configFile | config.json плагина. Если не указан, берётся <tokenDir>/config.json, когда он есть; null отключает | авто |
include | Glob-шаблоны файлов для чтения | ["**/*.json"] |
exclude | Glob-шаблоны для пропуска | ["config.json", "**/diff-id*.json"] |
projectConfig повторяет структуру config.json: collections с name и modes, а в каждом режиме name и files (карта «путь к файлу: enabled»). Для выбора токенов по коллекции и режиму нужен один из этих двух источников.
options
| Ключ | Что делает | По умолчанию |
|---|---|---|
remBase | Пикселей в одном rem. Переводит rem и em в числа для Swift, UIKit, Kotlin и XML: 1.5rem даёт 24 | 16 |
collisionStrategy | Два файла задают один путь токена: error останавливает сборку, suffix добавляет __dup_N, namespace-by-file и namespace-by-mode добавляют к пути имя файла или режима | error |
maxAliasDepth | Самая длинная допустимая цепочка ссылок | 20 |
removeStaleOutputs | Удалять сгенерированные файлы, чьи токены исчезли | true |
unsupportedTypes.default | Действие для типа, который платформа не умеет выводить: error, warn или skip | warn |
unsupportedTypes.types | Переопределение по типам, например template: skip | {} |
tokenSets[]
| Ключ | Что делает | По умолчанию |
|---|---|---|
id | Имя, которое используется в outputs | обязателен |
selectors | Из каких файлов состоит набор; объединяются в порядке перечисления | обязателен |
unresolvedAliases | Ссылки, которые никуда не ведут: error, warn или ignore | error |
Селектор выбирает файлы либо по collection и mode из config.json, либо по glob-шаблонам:
| Ключ | Что делает | По умолчанию |
|---|---|---|
collection, mode | Коллекция и режим из config.json или projectConfig, всегда вместе | |
files, include | Glob-шаблоны относительно tokenDir | |
exclude | Glob-шаблоны, которые убираются из набора и из разрешения ссылок | [] |
includeRefs | Подгружать и коллекции, на которые ссылается этот режим, чтобы ссылки разрешались | true |
refModeMap | Какой режим брать у каждой связанной коллекции, например Core: Default | {} |
tokenSets:
- id: app-root
selectors:
- collection: Core
mode: Default
- collection: Themes
mode: Light
refModeMap:
Core: Default
- id: components
unresolvedAliases: warn
selectors:
- files: ["components/**/*.style.json"]
outputs[]
| Ключ | Что делает | По умолчанию |
|---|---|---|
id | Имя для --only-output и сообщений | обязателен |
platform | css, scss, swift, uikit, kotlin, xml или manifest | обязателен |
outputDir | Папка для сгенерированных файлов | обязателен |
prefix, suffix | Добавляются к каждому имени токена: prefix: ds даёт --ds-color-primary | нет |
resolveAliases | true записывает конечные значения, false сохраняет ссылки вроде var(--other) | false для css и scss, true для остальных |
showDescriptions | Описания токенов как комментарии | true |
splitEffects | Оставлен для совместимости. Токены эффектов, где смешаны тени и размытия, всегда записываются переменными -shadow, -filter и -backdrop-filter | true |
excludeHidden | Убрать токены, скрытые в Figma. На manifest не влияет | false |
includePrelude | Swift, UIKit и Kotlin: записать в файл общие вспомогательные типы SXL*. Поставьте false, если их уже даёт другой файл | true |
codeSyntax | Запасной вариант именования, см. раздел Именование | нет |
files | Статичные файлы и разбиение по исходным файлам | [] |
bundles | Несколько исходных файлов в одном выходном | [] |
bundlesFromCollections | По одному bundle на коллекцию из config.json | [] |
indexes | Входные файлы CSS и SCSS | [] |
options | Параметры платформы: параметры манифеста, customFormatters | {} |
Output должен содержать хотя бы одно из files, bundles, bundlesFromCollections или indexes.
outputs[].files[]
| Ключ | Что делает | По умолчанию |
|---|---|---|
tokenSet | Какой набор записать | обязателен |
output | Путь файла внутри outputDir; для xml папка, например values | нужен output или splitBySourceFile |
splitBySourceFile | По файлу на каждый исходный файл, см. ниже | нет |
selector | Только CSS: обёртывающий селектор, строка или список. Остальные платформы игнорируют его с предупреждением | :root |
prefix, suffix | Переопределяют аффиксы output для этого файла | значения output |
filter | Оставить только часть токенов: types (имена типов), paths (префиксы путей), excludePaths | нет |
options | Параметры файла, например параметры манифеста | нет |
splitBySourceFile:
| Ключ | Что делает | По умолчанию |
|---|---|---|
include, exclude | Какие исходные файлы получают собственный выходной файл | ["**/*.style.json"], [] |
outputPattern | Имя файла с плейсхолдерами | обязателен |
prefixPattern, suffixPattern | Аффиксы имён токенов с теми же плейсхолдерами | нет |
excludeBundledSources | Пропустить файлы, которые уже идут в bundles или bundlesFromCollections | false |
excludeFromCollections | Пропустить файлы перечисленных коллекций: mode плюс имена в include или exclude | нет |
Плейсхолдеры: {sourceFile} (относительный путь), {sourceDir}, {sourceBase} (имя с расширением), {sourceName} (без расширения), {sourceStem} (ещё и без .style), {component} (папка после components/, иначе stem), {fileName} (то же, что {sourceStem}). selector принимает те же плейсхолдеры.
bundles[] и bundlesFromCollections[]
Bundle собирает несколько исходных файлов одного набора в один выходной файл. Он проходит тот же путь, что и обычный файл, поэтому selector, filter, prefix, suffix и options работают одинаково.
| Ключ | bundles[] | bundlesFromCollections[] |
|---|---|---|
tokenSet | обязателен | обязателен |
output | путь файла, обязателен | строится из outputPattern |
include, exclude | glob-шаблоны исходных файлов, include обязателен | имена или шаблоны коллекций; вложенная форма collections: {include, exclude} |
mode | режим, чьи включённые файлы берутся, обязателен | |
fileInclude, fileExclude | glob-шаблоны поверх файлов коллекции | |
outputPattern | {collection}, {collectionKebab}, {collectionSnake}, {collectionLower}, {mode}, {modeKebab}, {modeSnake}, {modeLower} | |
strict | true превращает отсутствующую коллекцию или режим в ошибку вместо предупреждения |
indexes[]
Index: входной файл, который импортирует сгенерированные: @import "..."; для CSS, @use "..." as *; для SCSS. Остальные платформы отвечают на indexes ошибкой.
| Ключ | Что делает | По умолчанию |
|---|---|---|
output | Путь index-файла внутри outputDir | обязателен |
imports | Явные импорты относительно index-файла | [] |
includeGenerated | Glob-шаблоны по файлам, сгенерированным тем же output | [] |
exclude | Glob-шаблоны для исключения | [] |
sort | generated-order или alpha | generated-order |
skipMissing | Не падать, если явного импорта нет на диске | false |
strict | Падать, если шаблон includeGenerated ничего не нашёл | false |
Нужно задать imports или includeGenerated.
Параметры манифеста
Задаются в options output с platform: manifest или одного из его файлов.
| Ключ | Что делает | По умолчанию |
|---|---|---|
schemaVersion | Строка версии в начале файла | "1.0" |
includeResolvedValue | Добавить resolvedValue | true |
includeOriginalValue | Добавить исходный $value | true |
includeReferences | Перечислить ссылки, найденные в исходном значении | true |
includeSource | Добавить collection, mode, tokenSet и level | true |
includeExtensions | Добавить $extensions | false |
includePrivate | Оставить токены, скрытые в Figma | true |
cssVarPrefix, cssVarSuffix | Аффиксы только для поля cssVar | prefix и suffix output |
groupBy | flat, collection, mode или file | flat |
У каждой записи есть name, cssVar, path, type, value, sourceFile, references и weight. level и weight читаются из расширений level, sxl.level и sxl.weight; без них они равны null.
Имена ссылок также учитывают Code Syntax целевого токена, когда он записывается в другой файл. Цели разрешаются в пределах выбранного мода и настроенных зависимостей, поэтому ссылки из разных модов не смешиваются.
Именование
Имена строятся из пути токена плюс prefix и suffix output. Токен с Code Syntax в $extensions сохраняет своё имя: ключ Web используется для CSS, SCSS и манифеста, iOS для Swift и UIKit, Android для Kotlin и XML. См. Scopes и Code Syntax.
codeSyntax у output задаёт запасной шаблон и приоритет:
| Ключ | Значения | По умолчанию |
|---|---|---|
source | extension-first (побеждает токен), config-first (побеждает шаблон), extension-only, config-only | extension-first |
template | {var(--css-variable)}, {$sass-variable}, {@less-variable}, {UpperCamelCase}, {lowerCamelCase}, {UPPER_SNAKE_CASE}, {lower_snake_case}. Обязателен при config-first и config-only | нет |
Типовые схемы
Root и файлы тем (CSS)
outputs:
- id: css-app
platform: css
outputDir: ./ds/css
files:
- tokenSet: app-root
output: root.css
- tokenSet: app-dark
output: themes/dark.css
selector: "[data-theme='dark']"
indexes:
- output: index.css
imports: ["./root.css", "./themes/dark.css"]
Ссылки остаются как var(--...), поэтому тема переключается одним селектором.
Один CSS-файл на компонент
outputs:
- id: css-components
platform: css
outputDir: ./ds/components
files:
- tokenSet: components
splitBySourceFile:
include: ["components/**/*.style.json"]
outputPattern: "{component}/{component}.css"
prefixPattern: "c-{component}-"
selector:
- ":root"
- "[data-component='{component}']"
Новый components/WAccordion/WAccordion.style.json получит собственный WAccordion/WAccordion.css при следующем запуске без изменений в конфиге.
Свои форматтеры
Когда один тип токена должен выглядеть иначе на одной платформе, подключите к output JavaScript-модуль:
outputs:
- id: css-app
platform: css
outputDir: ./ds/css
files:
- tokenSet: app-root
output: root.css
options:
customFormatters:
module: ./transform-hooks.mjs
exportName: plugin
Модуль экспортирует объект с функциями tokenTypeFormatters.<platform>.<type>. Форматтер получает токен и возвращает undefined, чтобы оставить поведение по умолчанию, null, чтобы убрать токен, или замену токена. Необязательная функция platformEmitters.<platform> заменяет вывод всего файла. Тип CustomFormatterPlugin экспортируется пакетом.
Проблемы и debug-отчёт
Когда sync находит проблемы (ссылка никуда не ведёт, сломан синтаксис ссылки, два токена с одним путём, файл не разбирается), он спрашивает, что делать:
- Создать debug-файл и остановиться.
- Создать debug-файл и продолжить.
- Попробовать автоматически починить простой синтаксис ссылок и продолжить.
- Пропустить сломанные токены и продолжить.
--issue-action даёт ответ заранее. Без терминала, например в CI, ask ведёт себя как debug-stop; в режиме --watch как debug-continue. Debug-отчёт <имя-конфига>.debug.md перечисляет каждую проблему с файлом и путём токена, а также сгенерированные файлы. При проблемах он записывается всегда; --debug-report записывает его и при чистом прогоне.
Рекомендуемые скрипты в package.json:
{
"scripts": {
"tokens:validate": "sxl-transform validate-config --config ./sxl-transform.config.yaml",
"tokens:build": "sxl-transform sync --config ./sxl-transform.config.yaml --issue-action debug-stop",
"tokens:build:ci": "sxl-transform sync --config ./sxl-transform.config.yaml --issue-action skip"
}
}
Если что-то не работает
| Что случилось | Что делать |
|---|---|
PROJECT_CONFIG_REQUIRED | Селектор использует collection и mode, но в tokenDir нет config.json, а в YAML нет projectConfig. Укажите файл в source.configFile или опишите коллекции прямо в конфиге |
Config already exists после init | Добавьте --force или выберите другой --path |
| Предупреждения о неразрешённых ссылках в наборе темы или компонентов | Связанной коллекции нет в наборе. Добавьте её селектором, оставьте includeRefs: true и зафиксируйте её режим через refModeMap |
| В файле нет части токенов | Прочитайте диагностику: template, composition и grid не конвертируются никогда, custom попадает только в манифест, XML пропускает типы без аналога среди ресурсов |
sync сообщает, что файл состояния заблокирован | Идёт другой запуск. Подождите или удалите <state>.lock, если тот процесс умер; блокировка старше 10 минут снимается автоматически |
| Ничего не пересобирается, хотя токены изменились | Запустите с --explain, чтобы увидеть причины, или с --force для полной пересборки. Проверьте, что изменённый файл подходит под source.include |
| Файлы пересобираются при каждом запуске | Какой-то скрипт переписывает сгенерированные файлы после sync. Запускайте его до sync или пишите в другое место |
SCSS: Undefined variable | Файлы тем берут переменные из ближайшего root.scss. Держите их рядом с root-файлом и подключайте root-файл раньше файлов компонентов |
| Имена не такие, как в Dev Mode | Задайте Code Syntax у токенов или codeSyntax у output; проверьте prefix и suffix |
CSS сохраняет var(--...), а нужны значения | Поставьте resolveAliases: true у output |