Перейти к содержимому

Пять целевых платформ

Каждый target реализует интерфейс CodegenTarget ({ name, outDir, generate(input): GeneratedFile[] }) и последовательно вызывается конвейером.

Общий шаблон построения тёмных артефактов

Заголовок раздела «Общий шаблон построения тёмных артефактов»

Четыре платформенных target’а одним и тем же способом превращают tokenSet.dark (Map<path, value>) в ResolvedToken[]:

const darkTokens = [...tokenSet.dark.entries()]
.map(([path, value]) => {
const base = tokenSet.byPath.get(path)
return {
path,
type: base?.type ?? ('color' as const), // Тип берётся у одноимённого светлого ключа, иначе запасной вариант — color
value,
description: base?.description,
}
})
.sort((a, b) => a.path.localeCompare(b.path))

Затем по отсортированным path проходит общее дерево emit. Светлые артефакты несут комментарии с description; spread (расширение) на всех платформах игнорируется с пояснением в комментарии.


Артефакты: dist/android/top/lhgb/ui/tokens/LhTokens.kt + LhDarkTokens.kt

Константа: PACKAGE = 'top.lhgb.ui.tokens' (семейное соглашение об обратном домене, генеральный план D20)

Возвращает { literal, features }, где features — множество использованных runtime-типов, определяющее список import:

Тип Литерал features
color ComposeColor(0xAARRGGBB) color
dimension (px) ${d.value}.${usesSp(path) ? 'sp' : 'dp'} dimension
dimension (%) 0.50f // 50% (доля, пересчитывается от размера контейнера) (после (v/100).toFixed(3) отбрасывается один ноль в конце) —
duration 200 // 200ms (миллисекунды) —
cubicBezier CubicBezierEasing(0.4f, 0f, 0.2f, 1f) bezier
shadow Shadow(color = ..., offset = Offset(xf, yf), blurRadius = bf) color, shadow
number / string Как есть / JSON.stringify —

Рекурсивно выводит вложенные object PascalCase { ... }, элементы val name = literal, description — комментарий в конце строки. features накапливается при обходе.

buildKotlinBody(tokenSet, rootName, features) / buildDarkKotlinBody(tokenSet, features)

Заголовок раздела «buildKotlinBody(tokenSet, rootName, features) / buildDarkKotlinBody(tokenSet, features)»

Имя корневого object светлой темы передаётся параметром (LhTokens), тёмный корень фиксирован — LhDarkTokens.

Список import определяется фактически использованными возможностями — неиспользуемые не импортируются:

feature import
color androidx.compose.ui.graphics.Color as ComposeColor
dimension androidx.compose.ui.unit.dp + .sp
shadow androidx.compose.ui.graphics.Shadow + androidx.compose.ui.geometry.Offset
bezier androidx.compose.animation.core.CubicBezierEasing

Собирает полный .kt-файл: заголовочный баннер «не редактировать вручную», блок import, сгенерированный по features, и body.


Артефакты: dist/ios/Sources/LhUITokens/LhTokens.swift + LhDarkTokens.swift; при наличии токенов теней дополнительно генерируется LhShadow.swift

Тип Литерал
color (a=1) SwiftUI.Color(red: 0.169, green: 0.298, blue: 0.486)
color (a<1) То же + .opacity(0.4)
dimension CGFloat(40); для % выводится доля + комментарий
duration 0.200 // 0.2s (секунды) — анимации SwiftUI работают в секундах, преобразование на этапе сборки (миллисекунды ÷ 1000, 3 знака после запятой)
cubicBezier (x1: 0.4, y1: 0, x2: 0.2, y2: 1) // Контрольные точки Безье, easing строится на стороне платформы
shadow LhShadow(color:opacity:radius:x:y:)

Если имя элемента совпадает с ключевым словом Swift, оно берётся в обратные апострофы (например, `default`), иначе артефакт не компилируется.

buildSwiftBody(tokenSet, rootName): string / buildDarkSwiftBody(tokenSet): string

Заголовок раздела «buildSwiftBody(tokenSet, rootName): string / buildDarkSwiftBody(tokenSet): string»

Собирает корневую структуру public enum: имя светлого корня передаётся параметром (LhTokens), тёмный корень фиксирован — LhDarkTokens; внутренности рекурсивно заполняются через emitTree. Заголовочный баннер файла собирается прямо в iosTarget.generate.

Идиома Swift для пространств имён без экземпляров: вложенные public enum PascalCase, элементы public static let name = ....


Артефакты: dist/harmony/tokens/LhTokens.ets + LhDarkTokens.ets; при наличии токенов теней дополнительно генерируется LhShadow.ets

Тип Литерал
color '#FF2B4C7C' (ArkUI принимает строки #AARRGGBB, используется toArgbHash)
dimension Число в vp без единицы измерения; для % выводится доля + комментарий
duration 200 // 200ms (миллисекунды)
cubicBezier [0.4, 0, 0.2, 1] // Контрольные точки Безье; на стороне компонента используются через curves.cubicBezierCurve
shadow { color: '#...', radius: 8, offsetX: 0, offsetY: 2 }

Единственная платформа с аннотациями типов (ArkTS строго типизирован):

Тип Аннотация
color ResourceColor
dimension Dimension | number
duration / number number
shadow LhShadow
cubicBezier number[]
string string

Корень — export class, вложенные классы без export (в теле класса ArkTS не разрешены export-объявления), элементы static readonly name: T = literal.

buildEtsBody(tokenSet, rootName): string / buildDarkEtsBody(tokenSet): string

Заголовок раздела «buildEtsBody(tokenSet, rootName): string / buildDarkEtsBody(tokenSet): string»

Собирает корневую структуру export class: имя светлого корня передаётся параметром (LhTokens), тёмный корень фиксирован — LhDarkTokens; внутренности рекурсивно заполняются через emitTree. Заголовочный баннер файла собирается прямо в harmonyTarget.generate.


Артефакты: dist/windows/Tokens/LhTokens.cs + LhDarkTokens.cs; при наличии токенов теней дополнительно генерируется LhShadow.cs, при наличии токенов Безье — LhEasing.cs

Пространство имён Top.Lhgb.Ui.Tokens (обратный домен, генеральный план D20).

Цвет превращается в вызов фабрики WinUI: Windows.UI.Color.FromArgb(0xFF, 0x2B, 0x4C, 0x7C).

К целым числам добавляется суффикс .0 (8 → 8.0) во избежание неоднозначности неявного преобразования типов.

Тип Литерал C# Тип поля
color Windows.UI.Color.FromArgb(0xFF, 0x2B, 0x4C, 0x7C) Windows.UI.Color
dimension (целое) 40.0 double
dimension (дробное) 0.5 double
dimension (%) 0.500 // 50% (доля, пересчитывается от размера контейнера) double
duration 200 // 200ms (миллисекунды) int
cubicBezier new LhEasing(0.4, 0, 0.2, 1) LhEasing
shadow new LhShadow(0xFF..., 0.0, 2.0, 8.0) LhShadow
number 400 / 1.8 int / double
string "Source Code Pro, ..." string

note (пояснение по единицам / преобразованиям) объединяется с description в один строчный комментарий; точка с запятой должна стоять между literal и комментарием, иначе литералы с комментариями (доли, длительности, Безье, тени) поглотят её, и компилятор сообщит об ошибке CS1002.

Преобразует имя в PascalCase: default → Default, extraLarge → ExtraLarge. Заодно это обходит контекстные ключевые слова C# вроде default / base (имена групп также проходят через pascalCase).

Вложенные public static class выражают иерархию токенов (статический класс в C# — идиома пространства имён), элементы public static readonly T Name = literal;.

Имя светлого корня передаётся параметром (LhTokens), тёмный корень фиксирован — LhDarkTokens; внутренности рекурсивно заполняются через emitTree. Заголовочный баннер файла в windowsTarget.generate рендерится шаблоном windows/LhTokens.cs.hbs.

export { csTokenPath } — преобразует точечный путь в LhTokens.Color.Primary.Default для использования в тестах и сторонним потребителями.


docs.ts — артефакты документации (без шаблонов)

Заголовок раздела «docs.ts — артефакты документации (без шаблонов)»

Артефакты: dist/docs/tokens.md + tokens.light.json + tokens.dark.json

Единственный target, не использующий handlebars-шаблоны — строки собираются прямо в коде.

Для color выводится исходное значение → #rrggbb (при a<1 → → rgba(r, g, b, a)), для остальных — просто String(value).

Выводит markdown-таблицы «Токен | Тип | Светлая | Тёмная | Описание», сгруппированные по пространствам имён верхнего уровня по path; для nativeOnly ставится отметка «добавлено для native»; при отсутствии переопределения в столбце тёмной темы ставится —.

Таблица переопределений тёмной темы сортируется и сериализуется через JSON.stringify(..., null, 2).

О назначении трёх артефактов см. Артефакты docs.

Copyright © 2024-2026 FengMo WenXue