Ir al contenido

Las cinco plataformas objetivo

Cada target implementa la interfaz CodegenTarget ({ name, outDir, generate(input): GeneratedFile[] }), invocada secuencialmente por el pipeline.

El patrón compartido de construcción de los artefactos oscuros

Sección titulada «El patrón compartido de construcción de los artefactos oscuros»

Los cuatro targets de plataforma usan el mismo patrón para convertir tokenSet.dark (Map<path, value>) en ResolvedToken[]:

const darkTokens = [...tokenSet.dark.entries()]
.map(([path, value]) => {
const base = tokenSet.byPath.get(path)
return {
path,
type: base?.type ?? ('color' as const), // si existe la clave homónima clara se toma su tipo; si no, se respalda con color
value,
description: base?.description,
}
})
.sort((a, b) => a.path.localeCompare(b.path))

A continuación se recorre el mismo árbol de emisión, ordenado por path. Los artefactos claros llevan comentarios con la description; la extensión (spread) se ignora en todas las plataformas y se deja constancia en un comentario.


Artefactos: dist/android/top/lhgb/ui/tokens/LhTokens.kt + LhDarkTokens.kt

Constante: PACKAGE = 'top.lhgb.ui.tokens' (la convención de dominio inverso de la familia, plan maestro D20)

Devuelve { literal, features } — features es el conjunto de tipos en tiempo de ejecución utilizados, y determina la lista de imports:

Tipo Literal features
color ComposeColor(0xAARRGGBB) color
dimension (px) ${d.value}.${usesSp(path) ? 'sp' : 'dp'} dimension
dimension (%) 0.50f // 50% (fracción, calculada a partir de la dimensión del contenedor) (tras (v/100).toFixed(3), se elimina un cero final) —
duration 200 // 200ms (milisegundos) —
cubicBezier CubicBezierEasing(0.4f, 0f, 0.2f, 1f) bezier
shadow Shadow(color = ..., offset = Offset(xf, yf), blurRadius = bf) color, shadow
number / string Tal cual / JSON.stringify —

Emite de forma recursiva el object PascalCase { ... } anidado, con miembros val name = literal y la description como comentario al final de la línea. features se acumula durante el recorrido.

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

Sección titulada «buildKotlinBody(tokenSet, rootName, features) / buildDarkKotlinBody(tokenSet, features)»

El nombre del object raíz claro se pasa como parámetro (LhTokens); la raíz oscura siempre es LhDarkTokens.

La lista de imports la determinan las características realmente utilizadas; las que no se usan no se importan:

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

renderKotlinFile(body, version, features): string

Sección titulada «renderKotlinFile(body, version, features): string»

Ensambla el banner de cabecera de «no modificar a mano», el bloque de imports generado según features y el body, para componer el archivo .kt completo.


Artefactos: dist/ios/Sources/LhUITokens/LhTokens.swift + LhDarkTokens.swift; cuando existen tokens de sombra se genera además LhShadow.swift

Tipo Literal
color (a=1) SwiftUI.Color(red: 0.169, green: 0.298, blue: 0.486)
color (a<1) Ídem + .opacity(0.4)
dimension CGFloat(40); % emite la fracción + comentario
duration 0.200 // 0.2s (segundos) — las animaciones de SwiftUI se expresan en segundos, conversión en tiempo de compilación (milisegundos ÷ 1000, con 3 decimales)
cubicBezier (x1: 0.4, y1: 0, x2: 0.2, y2: 1) // puntos de control de Bézier, cada plataforma construye su propio easing
shadow LhShadow(color:opacity:radius:x:y:)

Cuando un nombre de miembro coincide con una palabra clave de Swift, se envuelve entre acentos graves (por ejemplo `default`); de lo contrario, el artefacto no compila.

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

Sección titulada «buildSwiftBody(tokenSet, rootName): string / buildDarkSwiftBody(tokenSet): string»

Ensambla la estructura raíz public enum: el nombre de la raíz clara se pasa como parámetro (LhTokens), la raíz oscura siempre es LhDarkTokens, y ambas se rellenan recursivamente con emitTree. El banner de cabecera se ensambla directamente en iosTarget.generate.

La convención de Swift para los espacios de nombres sin instancias: public enum PascalCase anidados, con miembros public static let name = ....


Artefactos: dist/harmony/tokens/LhTokens.ets + LhDarkTokens.ets; cuando existen tokens de sombra se genera además LhShadow.ets

Tipo Literal
color '#FF2B4C7C' (ArkUI admite cadenas #AARRGGBB, a través de toArgbHash)
dimension Valor numérico puro en vp; % emite la fracción + comentario
duration 200 // 200ms (milisegundos)
cubicBezier [0.4, 0, 0.2, 1] // el componente lo consume con curves.cubicBezierCurve
shadow { color: '#...', radius: 8, offsetX: 0, offsetY: 2 }

La única plataforma con anotaciones de tipo (ArkTS está fuertemente tipado):

Tipo Anotación
color ResourceColor
dimension Dimension | number
duration / number number
shadow LhShadow
cubicBezier number[]
string string

La raíz es una export class, las clases anidadas no llevan export (ArkTS no permite declaraciones export dentro del cuerpo de una clase), con miembros static readonly name: T = literal.

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

Sección titulada «buildEtsBody(tokenSet, rootName): string / buildDarkEtsBody(tokenSet): string»

Ensambla la estructura raíz export class: el nombre de la raíz clara se pasa como parámetro (LhTokens), la raíz oscura siempre es LhDarkTokens, y ambas se rellenan recursivamente con emitTree. El banner de cabecera se ensambla directamente en harmonyTarget.generate.


Artefactos: dist/windows/Tokens/LhTokens.cs + LhDarkTokens.cs; cuando existen tokens de sombra se genera además LhShadow.cs, y cuando existen tokens de Bézier, LhEasing.cs

El espacio de nombres es Top.Lhgb.Ui.Tokens (dominio inverso, plan maestro D20).

Convierte el color en una llamada a la fábrica de WinUI: Windows.UI.Color.FromArgb(0xFF, 0x2B, 0x4C, 0x7C).

A los enteros se les añade el sufijo .0 (8 → 8.0), para evitar la ambigüedad de una conversión implícita de tipos.

Tipo Literal C# Tipo del campo
color Windows.UI.Color.FromArgb(0xFF, 0x2B, 0x4C, 0x7C) Windows.UI.Color
dimension (entera) 40.0 double
dimension (decimal) 0.5 double
dimension (%) 0.500 // 50% (fracción, calculada a partir de la dimensión del contenedor) double
duration 200 // 200ms (milisegundos) 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

La note (explicación de unidad / conversión) y la description se fusionan en un único comentario en línea; el punto y coma debe quedar entre el literal y el comentario, ya que de lo contrario un literal con comentario (la fracción, la duración, el Bézier o la sombra) se tragaría el punto y coma (CS1002).

Convierte a PascalCase: default → Default, extraLarge → ExtraLarge. De paso, evita las palabras clave contextuales de C# como default o base (los nombres de grupo también pasan por pascalCase).

public static class anidados para expresar la jerarquía de tokens (en C#, las clases estáticas son precisamente la convención para los espacios de nombres), con miembros public static readonly T Name = literal;.

buildCsBody(tokenSet, rootName) / buildDarkCsBody(tokenSet)

Sección titulada «buildCsBody(tokenSet, rootName) / buildDarkCsBody(tokenSet)»

El nombre de la raíz clara se pasa como parámetro (LhTokens), la raíz oscura siempre es LhDarkTokens, y ambas se rellenan recursivamente con emitTree. El banner de cabecera se renderiza en windowsTarget.generate a través de windows/LhTokens.cs.hbs.

export { csTokenPath } — convierte una ruta con puntos en LhTokens.Color.Primary.Default, para uso de las pruebas y del consumo externo.


docs.ts — los artefactos de documentación (sin plantillas)

Sección titulada «docs.ts — los artefactos de documentación (sin plantillas)»

Artefactos: dist/docs/tokens.md + tokens.light.json + tokens.dark.json

El único target que no usa plantillas handlebars; el contenido se ensambla directamente con código.

Para color se emite valor original → #rrggbb (si a<1, → rgba(r, g, b, a)); para el resto, directamente String(value).

Emite las tablas markdown «Token | Tipo | Claro | Oscuro | Descripción» agrupadas por el espacio de nombres de nivel superior del path; lo marcado con nativeOnly se anota como «nativo añadido»; la columna del modo oscuro se rellena con — cuando no hay sobrescritura.

La tabla de sobrescrituras del modo oscuro, ordenada y luego JSON.stringify(..., null, 2).

El uso de los tres artefactos se describe en los Artefactos de docs.

Copyright © 2024-2026 FengMo WenXue