Tree Layer — tree.ts
Builds a nested tree out of the flat
path -> ResolvedTokenmap, so each target can emit per namespace hierarchy.
Why a tree
Section titled “Why a tree”Tokens are nested in JSON (color.primary.default), but both the loading and resolution stages work with the flat Map<path, ResolvedToken> (convenient for duplicate detection, ref resolution, and dark merging). At the generation stage, Kotlin’s object Color { object Primary { ... } }, Swift’s enum Color { enum Primary { ... } }, and C’s section comments all need a nested structure — hence the tree-building step.
interface TokenTreeGroup { name: string tokens: { name: string; token: ResolvedToken }[] // The direct tokens under this group (kept under their original names) children: TokenTreeGroup[] // Subgroups}Conversion example:
color.primary.default => { name: 'color', children: [ { name: 'primary', children: [ { name: 'default', tokens: [{ name: 'default', token }] } ] } ] }Functions
Section titled “Functions”buildTokenTree(tokens: ResolvedToken[]): TokenTreeGroup[]
Section titled “buildTokenTree(tokens: ResolvedToken[]): TokenTreeGroup[]”The entry point. It creates a virtual root, calls insert(root, segments, token, 0) for every token, and finally returns root.children (the virtual root itself never appears).
insert(group, segments, token, depth): void
Section titled “insert(group, segments, token, depth): void”The internal recursive function:
const name = segments[depth]if (depth === segments.length - 1) { group.tokens.push({ name, token }) // Last segment: goes into tokens return}let child = group.children.find((c) => c.name === name)if (!child) { child = { name, tokens: [], children: [] } group.children.push(child) // Create the subgroup if it does not exist}insert(child, segments, token, depth + 1)A same-named subgroup is created only once; subsequent tokens with the same prefix reuse it.
pascalCase(name: string): string
Section titled “pascalCase(name: string): string”Capitalizes a group name for Kotlin object / Swift enum / ArkTS class naming:
return name.charAt(0).toUpperCase() + name.slice(1)How each target uses the tree
Section titled “How each target uses the tree”| Target | Usage |
|---|---|
| Android | emitTree recursively emits object PascalCase { ... } with members val name = literal |
| iOS | emitTree recursively emits public enum PascalCase with members public static let name = ... |
| HarmonyOS | emitTree recursively emits class PascalCase (nested classes are not exported) with members static readonly name: T = ... |
| C | Flat #define style; the tree is only used to generate section comments /* --- group.path --- */ |
See The Five Targets.
Copyright © 2024-2026 FengMo WenXue