@sxl-studio/token-transformer
v3.1.2
Published
Transform DTCG design tokens to CSS/SCSS, Swift, Kotlin, Android XML, and JSON manifests
Downloads
1,025
Maintainers
Readme
SXL Token Transformer
YAML-first утилита для преобразования дизайн-токенов в:
- CSS custom properties
- SCSS variables
- Swift-константы (SwiftUI)
- Kotlin-константы (Compose)
- Android XML ресурсы (
colors.xml,dimens.xml,strings.xml,bools.xml,integers.xml)
Установка
npm install @sxl-studio/token-transformerКоманды
# трансформация в smart incremental режиме (по умолчанию)
npx tsx src/cli.ts sync --config ./sxl-transform.config.yaml
# принудительный полный rebuild
npx tsx src/cli.ts sync --config ./sxl-transform.config.yaml --force
# только выбранные outputs
npx tsx src/cli.ts sync --config ./sxl-transform.config.yaml --only-output css-root --only-output swift-root
# только выбранные файлы (glob)
npx tsx src/cli.ts sync --config ./sxl-transform.config.yaml --only-file "modes/dark.css"
npx tsx src/cli.ts sync --config ./sxl-transform.config.yaml --only-file "css-root:modes/dark.css"
# валидация конфига
npx tsx src/cli.ts validate-config --config ./sxl-transform.config.yaml
# генерация стартового конфига
npx tsx src/cli.ts init --path ./sxl-transform.config.yaml
# help по конкретной команде
npx tsx src/cli.ts help sync
npx tsx src/cli.ts validate-config --helpSmart vs force
smart(по умолчанию): пересобирает только затронутые output-файлы на основе изменённых/удалённых source-файлов и state предыдущего запуска.force: пересобирает все output-файлы из конфига безусловно.
Дополнительные флаги:
--mode smart|force--force(сокращение для--mode force)--state-file <path>(переопределить путь state-файла, по умолчанию<config-name>.state.json)--only-output <id>(можно повторять, выбор output id)--only-file <glob>(можно повторять, выбор output-файлов по шаблону)
Формат конфига
Поддерживается только YAML. JSON-конфиг умышленно не поддерживается.
Минимальная структура:
version: 1
source:
tokenDir: ../../Plugin/test/tokens_project_new
configFile: config.json
include: ["**/*.json"]
exclude: ["config.json", "**/diff-id*.json"]
options:
remBase: 16
collisionStrategy: error # error | suffix | namespace-by-file | namespace-by-mode
unsupportedTypes:
default: error
types:
template: skip
composition: skip
grid: skip
tokenSets:
- id: root
selectors:
- collection: Core
mode: Default
- collection: Themes
mode: Dark
refModeMap:
Projects: AUI
Core: Default
- files: ["projects/aui/*.json"]
outputs:
- id: css-root
platform: css # css | scss | swift | uikit | kotlin | xml | manifest
outputDir: ./dist/css/adminui
prefix: ds
suffix: v2
resolveAliases: false
splitEffects: true
showDescriptions: true
files:
- tokenSet: root
output: root.css
# prefix/suffix можно переопределить для конкретного mapping:
# prefix: cmp
# suffix: beta
- id: tokens-manifest
platform: manifest
outputDir: ./dist/manifest/adminui
files:
- tokenSet: root
output: tokens-manifest.json
options:
schemaVersion: "1.0"
includeResolvedValue: true
includeOriginalValue: true
includeReferences: true
includeSource: true
includeExtensions: false
includePrivate: true
groupBy: flat # flat | collection | mode | fileКаждый output должен содержать минимум один generation-блок: files, bundles, bundlesFromCollections или indexes.
CSS / SCSS Output Contract
CSS-файлы по умолчанию оборачиваются в :root. Wrapper selector можно переопределить для статичного файла, split-файла или bundle:
outputs:
- id: css-app
platform: css
outputDir: ./dist/css
files:
- tokenSet: root
output: root.css
selector: ":root"
- tokenSet: dark
output: themes/dark.css
selector: "[data-theme='dark']"
- tokenSet: components
splitBySourceFile:
include: ["components/**/*.style.json"]
outputPattern: "{component}/{component}.css"
selector:
- ":root"
- "[data-component='{component}']"selector работает только для CSS. Если указать его для scss, swift, uikit, kotlin, xml или manifest, Transformer выдаст warning и проигнорирует настройку.
CSS/SCSS entrypoint-файлы генерируются через indexes:
outputs:
- id: css-app
platform: css
outputDir: ./dist/css
files:
- tokenSet: root
output: root.css
indexes:
- output: index.css
imports:
- ./root.css
includeGenerated:
- components/**/*.css
exclude:
- index.css
- "**/*.internal.css"
sort: generated-order # generated-order | alpha
skipMissing: true
strict: false- CSS indexes генерируют
@import "...";. - SCSS indexes генерируют
@use "..." as *;. - Indexes поддержаны только для
cssиscss. - Явные
importsрезолвятся относительно index-файла. includeGeneratedматчится по файлам того же output, включая файлы из state в smart-mode.- Index-файлы хранятся в state и участвуют в stale cleanup как обычные generated files.
bundles нужны, когда несколько source token files нужно собрать в один output без создания отдельного tokenSet под каждую группу:
outputs:
- id: css-components
platform: css
outputDir: ./dist/components
files:
- tokenSet: components
splitBySourceFile:
include: ["components/**/*.style.json"]
exclude: ["components/WBanner/**/*.style.json"]
outputPattern: "{component}/{component}.css"
bundles:
- tokenSet: components
output: WBanner/WBanner.css
include:
- components/WBanner/**/*.style.json
selector: ":root"Bundles используют тот же token graph, filtering, unsupported-type policy, aliases, custom formatters и platform emitters, что и обычные files. selector внутри bundle всё равно применяется только к CSS.
bundlesFromCollections нужен, когда группы bundle уже описаны в source.configFile или top-level projectConfig:
outputs:
- id: css-components
platform: css
outputDir: ./dist/components
resolveAliases: false
files:
- tokenSet: components
splitBySourceFile:
include: ["components/**/*.style.json"]
excludeBundledSources: true
outputPattern: "{fileName}.css"
bundlesFromCollections:
- tokenSet: components
mode: Default
include:
- WBanner
- WInput
fileInclude:
- components/**/*.style.json
fileExclude:
- "**/__draft__/*.json"
outputPattern: "{collection}.css"
selector: ":root"
strict: true
indexes:
- output: index.css
includeGenerated:
- "**/*.css"
exclude:
- index.cssbundlesFromCollections читает enabled mode.files из config.json / projectConfig, фильтрует их через fileInclude / fileExclude и генерирует один bundle на matched collection. Поддержанные placeholders: {collection}, {mode}, {collectionKebab}, {collectionSnake}, {collectionLower}, {modeKebab}, {modeSnake}, {modeLower}.
Чтобы избежать двойной генерации, используйте splitBySourceFile.excludeBundledSources: true. Для явного контроля без генерации bundles используйте splitBySourceFile.excludeFromCollections.
Quality gates
npm run eslint
npm run typecheck
npm run test
npm run build
npm run checkВажно
- Запрещённые к трансформации типы:
template,composition,grid(рекомендуемый профиль:template/composition/grid -> skip, остальные unsupported ->error). - Поддержан
figma.modify, включая chain и alias-based параметры модификаторов. - Поддержана безопасная математика (
+ - * / %,round,floor,ceil,clamp, и др.) безeval. - Для selector по
collection/modeможно управлять зависимостями:includeRefs: true|false— подключать лиref-цепочки из tokenconfig.jsonв resolver scope.refModeMap— принудительно выбрать mode для зависимых коллекций (напримерProjects: AUI).
- Аффиксы имен настраиваются в YAML:
outputs[].prefix/outputs[].suffix— для всего outputoutputs[].files[].prefix/outputs[].files[].suffix— переопределение на уровне mappingsplitBySourceFile.prefixPattern/splitBySourceFile.suffixPattern— для split-генерации по файлам
- В режиме
--only-fileутилита не делает stale-cleanup вне выбранных файлов: точечная генерация безопасна для остального output. - SCSS-выгрузка ориентирована на token-first подключение: root-файлы нужно подключать до component-файлов в entrypoint, чтобы межфайловые
$token-ссылки резолвились детерминированно. - CSS
selectorподдерживает статичные селекторы и split placeholders вроде{component}. CSS/SCSSindexesгенерируют entrypoints и участвуют в smart state/stale cleanup.bundlesиbundlesFromCollectionsгруппируют source-файлы в один output через тот же emitter pipeline, а не через конкатенацию готовых строк. - Android XML: если
colorтокен содержит CSSlinear-gradient(...), transformer генерирует нативныйdrawable/*.xml, а не невалидное значение вcolors.xml. platform: manifestгенерирует JSON-артефакт из того же resolved token graph, что и CSS/native outputs. Manifest содержитname,cssVar,path,type,value,resolvedValue,originalValue,references,sourceFileи source metadata для документации, Storybook/devtools и аудита миграций.- Manifest
levelиweightчитаются из token extensions (level,sxl.level,sxl.weight), если они есть; они не выводятся из path или названия collection. - Manifest options можно задавать на уровне
outputs[].optionsили конкретногоoutputs[].files[].options. ПоддержаныcssVarPrefix,cssVarSuffix,includePrivate,includeSource,includeExtensions,groupBy,schemaVersion.
Матрица Поддержки Типов (CSS / SCSS / Swift / UIKit / Kotlin)
Поддерживаются:
color,gradient,img,fill,opacitydimension,number,spacing,sizingborder,borderWidth,borderRadius,strokeStyletypography,fontFamily,fontWeight,fontStyle,fontSize,lineHeight,letterSpacing,paragraphSpacing,paragraphIndent,textCase,textDecorationshadow,backdrop-blur,blur,glass,effectsboolean,texttransition,duration,cubicBezier(как токены значений/spec, без auto-apply к UI-элементам)
Исключены из трансформации:
template,composition,grid
Manifest поддерживает те же разрешённые типы токенов и намеренно не эмитит запрещённые template, composition, grid.
Политика no-hallucination:
- Если токен невалиден для платформы, он не “додумывается”: фиксируется
error, токен не эмитится. - Если алиас неразрешим, поведение строго по
unresolvedAliases(error | warn | ignore). - Для прод-профиля рекомендуется
unsupportedTypes.default: error.
