@sxl-studio/token-transformer 1.0.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. package/README.en.md +77 -608
  2. package/README.md +77 -685
  3. package/config/sxl-transform.config.yaml +120 -0
  4. package/dist/cli.d.ts +1 -1
  5. package/dist/cli.js +411 -141
  6. package/dist/cli.js.map +1 -1
  7. package/dist/config/loader.d.ts +6 -0
  8. package/dist/config/loader.js +160 -0
  9. package/dist/config/loader.js.map +1 -0
  10. package/dist/config/schema.d.ts +847 -0
  11. package/dist/config/schema.js +123 -0
  12. package/dist/config/schema.js.map +1 -0
  13. package/dist/core/color-modifiers.d.ts +31 -0
  14. package/dist/core/color-modifiers.js +289 -0
  15. package/dist/core/color-modifiers.js.map +1 -0
  16. package/dist/core/color-parser.d.ts +24 -0
  17. package/dist/core/color-parser.js +281 -0
  18. package/dist/core/color-parser.js.map +1 -0
  19. package/dist/core/debug-report.d.ts +11 -0
  20. package/dist/core/debug-report.js +161 -0
  21. package/dist/core/debug-report.js.map +1 -0
  22. package/dist/core/incremental.d.ts +18 -0
  23. package/dist/core/incremental.js +105 -0
  24. package/dist/core/incremental.js.map +1 -0
  25. package/dist/core/math.d.ts +3 -0
  26. package/dist/core/math.js +261 -0
  27. package/dist/core/math.js.map +1 -0
  28. package/dist/core/parser.d.ts +4 -3
  29. package/dist/core/parser.js +68 -172
  30. package/dist/core/parser.js.map +1 -1
  31. package/dist/core/resolver.d.ts +26 -0
  32. package/dist/core/resolver.js +431 -0
  33. package/dist/core/resolver.js.map +1 -0
  34. package/dist/core/token-loader.d.ts +11 -0
  35. package/dist/core/token-loader.js +380 -0
  36. package/dist/core/token-loader.js.map +1 -0
  37. package/dist/core/token-parser.d.ts +9 -0
  38. package/dist/core/token-parser.js +138 -0
  39. package/dist/core/token-parser.js.map +1 -0
  40. package/dist/core/token-types.d.ts +7 -0
  41. package/dist/core/token-types.js +132 -0
  42. package/dist/core/token-types.js.map +1 -0
  43. package/dist/core/types.d.ts +154 -63
  44. package/dist/core/writer.d.ts +18 -5
  45. package/dist/core/writer.js +545 -91
  46. package/dist/core/writer.js.map +1 -1
  47. package/dist/emit/css.d.ts +2 -0
  48. package/dist/emit/css.js +538 -0
  49. package/dist/emit/css.js.map +1 -0
  50. package/dist/emit/kotlin.d.ts +2 -0
  51. package/dist/emit/kotlin.js +406 -0
  52. package/dist/emit/kotlin.js.map +1 -0
  53. package/dist/emit/shared.d.ts +13 -0
  54. package/dist/emit/shared.js +127 -0
  55. package/dist/emit/shared.js.map +1 -0
  56. package/dist/emit/swift.d.ts +2 -0
  57. package/dist/emit/swift.js +432 -0
  58. package/dist/emit/swift.js.map +1 -0
  59. package/dist/emit/typography.d.ts +17 -0
  60. package/dist/emit/typography.js +132 -0
  61. package/dist/emit/typography.js.map +1 -0
  62. package/dist/emit/xml.d.ts +2 -0
  63. package/dist/emit/xml.js +311 -0
  64. package/dist/emit/xml.js.map +1 -0
  65. package/dist/index.d.ts +15 -6
  66. package/dist/index.js +13 -5
  67. package/dist/index.js.map +1 -1
  68. package/dist/transformers/css.d.ts +1 -1
  69. package/dist/transformers/css.js +13 -482
  70. package/dist/transformers/css.js.map +1 -1
  71. package/dist/transformers/kotlin.d.ts +2 -2
  72. package/dist/transformers/kotlin.js +14 -442
  73. package/dist/transformers/kotlin.js.map +1 -1
  74. package/dist/transformers/swiftui.d.ts +2 -2
  75. package/dist/transformers/swiftui.js +14 -433
  76. package/dist/transformers/swiftui.js.map +1 -1
  77. package/dist/utils/color.d.ts +7 -5
  78. package/dist/utils/color.js +90 -86
  79. package/dist/utils/color.js.map +1 -1
  80. package/dist/utils/dimension.d.ts +8 -5
  81. package/dist/utils/dimension.js +54 -52
  82. package/dist/utils/dimension.js.map +1 -1
  83. package/dist/utils/naming.d.ts +10 -12
  84. package/dist/utils/naming.js +102 -44
  85. package/dist/utils/naming.js.map +1 -1
  86. package/package.json +30 -10
  87. package/config.example.json +0 -45
  88. package/dist/core/loader.d.ts +0 -8
  89. package/dist/core/loader.js +0 -105
  90. package/dist/core/loader.js.map +0 -1
  91. package/dist/transformers/vue3.d.ts +0 -28
  92. package/dist/transformers/vue3.js +0 -534
  93. package/dist/transformers/vue3.js.map +0 -1
package/README.en.md CHANGED
@@ -1,638 +1,107 @@
1
- # SXL Studio — Token Transformer
1
+ # SXL Token Transformer
2
2
 
3
- A tool for transforming design tokens from JSON (DTCG format) into valid code for three platforms:
4
- - **Web** — CSS Custom Properties
5
- - **iOS** SwiftUI
6
- - **Android** — Kotlin Compose
7
- - **Vue 3** Composition JSON Vue 3 SFC (`.vue`)
3
+ YAML-first token transformer for generating:
4
+ - CSS custom properties
5
+ - Swift (SwiftUI-ready constants)
6
+ - Kotlin (Compose-ready constants)
7
+ - Android XML resources (`colors.xml`, `dimens.xml`, `strings.xml`, `bools.xml`, `integers.xml`)
8
8
 
9
- ---
10
-
11
- ## Quick Start
9
+ ## Install
12
10
 
13
11
  ```bash
14
- # Install dependencies
15
12
  npm install
16
-
17
- # Run with default config
18
- npx tsx src/cli.ts
19
-
20
- # Run with specific config
21
- npx tsx src/cli.ts --config=config.example.json
22
-
23
- # Generate a default config
24
- npx tsx src/cli.ts --init
25
-
26
- # Transform composition → Vue 3
27
- npx tsx src/cli.ts --composition=path/to/composition.json --output=./output/dir
28
- ```
29
-
30
- ---
31
-
32
- ## Project Structure
33
-
34
- ```
35
- Transformer/
36
- ├── src/
37
- │ ├── cli.ts # CLI entry point
38
- │ ├── core/
39
- │ │ ├── types.ts # Types and interfaces
40
- │ │ ├── parser.ts # JSON parsing, alias resolution, math expressions
41
- │ │ ├── loader.ts # Token loading from files
42
- │ │ └── writer.ts # Transformation and file writing
43
- │ ├── transformers/
44
- │ │ ├── css.ts # CSS Custom Properties generator
45
- │ │ ├── swiftui.ts # SwiftUI code generator
46
- │ │ ├── kotlin.ts # Kotlin Compose code generator
47
- │ │ └── vue3.ts # Composition JSON → Vue 3 SFC
48
- │ └── utils/
49
- │ ├── naming.ts # Naming utilities (kebab, camel, pascal)
50
- │ ├── color.ts # Color parsing and formatting
51
- │ └── dimension.ts # Dimension parsing and formatting
52
- ├── config.example.json # Config for test tokens
53
- ├── config.admin-ui.json # Config for admin-ui project
54
- ├── config.site-ui.json # Config for site-ui project
55
- ├── project-style/ # Output files (generated)
56
- └── package.json
57
- ```
58
-
59
- ---
60
-
61
- ## Configuration
62
-
63
- The config is a JSON file with the following structure:
64
-
65
- ### Full Schema
66
-
67
- ```json
68
- {
69
- "source": {
70
- "tokenDir": "../Plugin/tokens",
71
- "configFile": "config.json",
72
- "include": ["example/*.json", "core/*.json"],
73
- "exclude": ["config.json", "**/diff-id*.json", "**/composition*"]
74
- },
75
- "platforms": {
76
- "css": {
77
- "outputDir": "./project-style/example",
78
- "prefix": "ds",
79
- "resolveAliases": false,
80
- "splitEffects": true,
81
- "showDescriptions": true,
82
- "codeSyntax": {
83
- "colorFormat": "hex",
84
- "prefix": "ds"
85
- },
86
- "fileMapping": [
87
- {
88
- "sources": ["example/*.json"],
89
- "output": "all-tokens.css",
90
- "filter": {
91
- "types": ["color", "dimension"],
92
- "paths": ["color.", "spacing."],
93
- "excludePaths": ["color.internal."]
94
- }
95
- }
96
- ]
97
- },
98
- "swiftui": {
99
- "outputDir": "./project-style/example",
100
- "prefix": "DS",
101
- "resolveAliases": true,
102
- "showDescriptions": true,
103
- "fileMapping": [
104
- {
105
- "sources": ["example/*.json"],
106
- "output": "AllTokens.swift"
107
- }
108
- ]
109
- },
110
- "kotlin": {
111
- "outputDir": "./project-style/example",
112
- "prefix": "DS",
113
- "resolveAliases": true,
114
- "showDescriptions": true,
115
- "fileMapping": [
116
- {
117
- "sources": ["example/*.json"],
118
- "output": "AllTokens.kt"
119
- }
120
- ]
121
- }
122
- },
123
- "settings": {
124
- "remBase": 16,
125
- "verbose": false
126
- }
127
- }
128
- ```
129
-
130
- ---
131
-
132
- ### Field Descriptions
133
-
134
- #### `source` — Token Source
135
-
136
- | Field | Type | Default | Description |
137
- |-------|------|---------|-------------|
138
- | `tokenDir` | `string` | required | Path to the JSON token directory |
139
- | `configFile` | `string` | `"config.json"` | Path to plugin config.json (inside tokenDir) |
140
- | `include` | `string[]` | all `**/*.json` | Glob patterns for files to include |
141
- | `exclude` | `string[]` | `["config.json"]` | Glob patterns for files to exclude |
142
-
143
- #### `platforms` — Platform Settings
144
-
145
- Each platform (`css`, `swiftui`, `kotlin`) is configured independently:
146
-
147
- | Field | Type | Default | Description |
148
- |-------|------|---------|-------------|
149
- | `outputDir` | `string` | required | Output directory for generated files |
150
- | `prefix` | `string` | `""` | Prefix for variable names |
151
- | `resolveAliases` | `boolean` | CSS: `false`, Swift/Kotlin: `true` | Whether to resolve aliases to final values |
152
- | `splitEffects` | `boolean` | `true` | Split effects into separate variables (CSS only) |
153
- | `showDescriptions` | `boolean` | `true` | Include `$description` as comments in output |
154
- | `codeSyntax` | `object` | — | Code format settings |
155
- | `fileMapping` | `array` | — | Rules for merging tokens into files |
156
-
157
- #### `resolveAliases` — Alias Control
158
-
159
- The key setting that determines how alias references (`{token.path}`) are handled in composite tokens.
160
-
161
- **CSS (`resolveAliases: false` — default):**
162
- ```css
163
- /* Aliases preserved as var() */
164
- --typography-heading-xl: var(--font-weight-bold) var(--font-size-3xl)/var(--line-height-relaxed) var(--font-family-sans);
165
- --shadow-sm: var(--number-fx-offset-xs) var(--number-fx-offset-none) var(--number-fx-blur-xs) var(--number-fx-offset-none) var(--color-hex-alpha);
166
- ```
167
-
168
- **CSS (`resolveAliases: true`):**
169
- ```css
170
- /* All values fully resolved */
171
- --ds-typography-heading-xl: 700 30px/24px Inter;
172
- --ds-shadow-sm: 2px 0px 8px 0px rgba(255, 85, 0, 0.502);
173
- ```
174
-
175
- **SwiftUI/Kotlin (`resolveAliases: false`):**
176
- - Pure aliases (entire token = reference) → same-scope constant reference
177
- - Composite tokens with aliased fields → `/// @ref` / `/** @ref */` comments with paths
178
-
179
- ```swift
180
- /// @ref {color.hex-full}
181
- static let primary = hexFull // ← direct reference
182
-
183
- /// @ref {fontFamily.sans}, {fontWeight.bold}, {fontSize.3xl}, {lineHeight.relaxed}
184
- static let headingXl: Font = .system(size: 30, weight: .bold) // ← values + ref comment
185
- ```
186
-
187
- **SwiftUI/Kotlin (`resolveAliases: true` — default):**
188
- ```swift
189
- static let primary = Color(red: 1, green: 0.3333, blue: 0, opacity: 1) // ← fully resolved
190
- ```
191
-
192
- #### `splitEffects` — Effects Splitting (CSS only)
193
-
194
- Controls how mixed effects (shadow + blur + backdrop-blur) are handled.
195
-
196
- **`splitEffects: true` (default):**
197
-
198
- Mixed effects are split into separate variables with suffixes. Each variable maps to a specific CSS property:
199
-
200
- ```css
201
- /* box-shadow */
202
- --effects-full-mix: 0 2px 8px rgba(0,0,0,0.08), inset 0 1px 2px rgba(255,255,255,0.5);
203
- /* filter */
204
- --effects-full-mix-blur: blur(2px);
205
- /* backdrop-filter */
206
- --effects-full-mix-backdrop-blur: blur(20px);
207
- ```
208
-
209
- Usage:
210
- ```css
211
- .card {
212
- box-shadow: var(--effects-full-mix);
213
- filter: var(--effects-full-mix-blur);
214
- backdrop-filter: var(--effects-full-mix-backdrop-blur);
215
- }
216
- ```
217
-
218
- **`splitEffects: false`:**
219
-
220
- All effects are output as a single variable, shadow part only:
221
-
222
- ```css
223
- --effects-full-mix: 0 2px 8px rgba(0,0,0,0.08), inset 0 1px 2px rgba(255,255,255,0.5);
224
- ```
225
-
226
- #### `showDescriptions` — Token Descriptions
227
-
228
- Controls whether `$description` from JSON is included as comments. Works on all platforms.
229
-
230
- **`showDescriptions: true` (default):**
231
- ```css
232
- /* Heading XL — all values raw */
233
- --typography-heading-xl: 700 30px/24px Inter;
234
- ```
235
-
236
- ```swift
237
- /// Heading XL — all values raw
238
- static let headingXl: Font = .system(size: 30, weight: .bold)
239
- ```
240
-
241
- ```kotlin
242
- /** Heading XL — all values raw */
243
- val HeadingXl = TextStyle(...)
244
- ```
245
-
246
- **`showDescriptions: false`:**
247
- ```css
248
- --typography-heading-xl: 700 30px/24px Inter;
249
- ```
250
-
251
- ```swift
252
- static let headingXl: Font = .system(size: 30, weight: .bold)
253
- ```
254
-
255
- ```kotlin
256
- val HeadingXl = TextStyle(...)
257
- ```
258
-
259
- #### `fileMapping` — File Merging Rules
260
-
261
- Allows combining tokens from multiple JSON files into a single output file.
262
-
263
- ```json
264
- {
265
- "sources": ["core/palette.json", "projects/aui/colors.json", "projects/modes/themes/light.json"],
266
- "output": "core.css",
267
- "filter": {
268
- "types": ["color", "dimension"],
269
- "paths": ["color.primary"],
270
- "excludePaths": ["color.internal"]
271
- }
272
- }
273
13
  ```
274
14
 
275
- | Field | Description |
276
- |-------|-------------|
277
- | `sources` | Array of JSON file paths (supports `*` wildcards) |
278
- | `output` | Output file name (relative to `outputDir`) |
279
- | `filter.types` | Filter by token type |
280
- | `filter.paths` | Include only paths starting with specified prefixes |
281
- | `filter.excludePaths` | Exclude paths starting with specified prefixes |
15
+ ## Commands
282
16
 
283
- #### `settings` — Global Settings
284
-
285
- | Field | Type | Default | Description |
286
- |-------|------|---------|-------------|
287
- | `remBase` | `number` | `16` | Base value for rem calculations |
288
- | `verbose` | `boolean` | `false` | Verbose logging output |
289
-
290
- ---
291
-
292
- ## Supported Token Types
293
-
294
- ### Simple Types
295
-
296
- | Type | CSS | SwiftUI | Kotlin |
297
- |------|-----|---------|--------|
298
- | `color` | `#hex` / `rgba()` | `Color(red:green:blue:opacity:)` | `Color(0xAARRGGBB)` |
299
- | `dimension` | `16px` / `1rem` | `CGFloat` | `Dp` |
300
- | `spacing`, `sizing` | `16px` | `CGFloat` | `Dp` |
301
- | `borderRadius`, `borderWidth` | `4px` | `CGFloat` | `Dp` |
302
- | `opacity` | `0.5` | `Double` | `Float` |
303
- | `number` | `42` | numeric | numeric |
304
- | `fontFamily` | `"Inter"` | `String` | `String` |
305
- | `fontWeight` | `700` | `Font.Weight` | `FontWeight` |
306
- | `fontSize`, `lineHeight` | `16px` | `CGFloat` | `TextUnit (sp)` |
307
- | `letterSpacing` | `0.5px` | `CGFloat` | `TextUnit (sp)` |
308
- | `duration` | `200ms` | `String` | `String` |
309
- | `cubicBezier` | `cubic-bezier(...)` | `String` | `String` |
310
- | `boolean` | `true` | `Bool` | `Boolean` |
311
- | `text`, `string` | `"value"` | `String` | `String` |
312
- | `textCase` | `uppercase` | `String` | `String` |
313
- | `textDecoration` | `underline` | `String` | `String` |
314
- | `strokeStyle` | `dashed /* ... */` | `String` | `String` |
315
-
316
- ### Composite Types
317
-
318
- | Type | CSS | SwiftUI | Kotlin |
319
- |------|-----|---------|--------|
320
- | `typography` | `700 16px/24px Inter` | `Font.system(size:weight:)` | `TextStyle(...)` |
321
- | `shadow` | `0 4px 8px rgba(...)` | `.shadow(color:radius:x:y:)` | `elevation (Dp)` |
322
- | `border` | `1px solid #000` | `(color:width:style:)` | `BorderStroke(...)` |
323
- | `fill` | `#hex` / `gradient(...)` | `Color(...)` / `Gradient(...)` | `Color(...)` / `Brush(...)` |
324
- | `gradient` | `linear-gradient(...)` | `LinearGradient(...)` | `Brush.linearGradient(...)` |
325
- | `effects` | see below | `.shadow(...)` | `elevation (Dp)` |
326
- | `blur` | `blur(4px)` | `.blur(radius:)` | `Modifier.blur(...)` |
327
- | `backdrop-blur` | `blur(16px)` | `.blur(radius:)` | `Modifier.blur(...)` |
328
- | `transition` | `all 200ms ease` | `String` | `String` |
329
- | `grid` | `repeat(12, 1fr)` | `String` | `String` |
330
-
331
- ---
332
-
333
- ## Effects Handling (CSS)
334
-
335
- In CSS, different effect types map to different CSS properties. The transformer automatically creates separate variables for each type:
336
-
337
- ### Single Effect Type
338
-
339
- ```css
340
- /* box-shadow */
341
- --effects-card: 0 4px 8px rgba(0,0,0,0.1);
342
-
343
- /* filter */
344
- --blur-sm: blur(4px);
345
-
346
- /* backdrop-filter */
347
- --backdrop-blur-md: blur(12px);
348
- ```
349
-
350
- ### Mixed Effects (shadow + blur + backdrop-blur)
351
-
352
- When a token contains multiple effect types, they are split into separate variables with suffixes:
353
-
354
- ```css
355
- /* box-shadow */
356
- --effects-full-mix: 0 2px 8px rgba(0,0,0,0.08), inset 0 1px 2px rgba(255,255,255,0.5);
357
- /* filter */
358
- --effects-full-mix-blur: blur(2px);
359
- /* backdrop-filter */
360
- --effects-full-mix-backdrop-blur: blur(20px);
361
- ```
362
-
363
- Usage in CSS:
364
- ```css
365
- .card {
366
- box-shadow: var(--effects-full-mix);
367
- filter: var(--effects-full-mix-blur);
368
- backdrop-filter: var(--effects-full-mix-backdrop-blur);
369
- }
370
- ```
371
-
372
- ---
373
-
374
- ## Aliases in Composite Tokens
375
-
376
- All composite token types (typography, shadow, border, fill, gradient, effects) support alias references `{token.path}` in any sub-property.
377
-
378
- ### JSON Example
379
-
380
- ```json
381
- {
382
- "heading": {
383
- "xl": {
384
- "$type": "typography",
385
- "$value": {
386
- "fontFamily": "{fontFamily.sans}",
387
- "fontWeight": "{fontWeight.bold}",
388
- "fontSize": "{fontSize.3xl}",
389
- "lineHeight": "{lineHeight.relaxed}"
390
- }
391
- }
392
- }
393
- }
394
- ```
395
-
396
- ### CSS Output (resolveAliases: false)
397
-
398
- ```css
399
- --typography-heading-xl: var(--font-weight-bold) var(--font-size-3xl)/var(--line-height-relaxed) var(--font-family-sans);
400
- ```
401
-
402
- ### CSS Output (resolveAliases: true)
403
-
404
- ```css
405
- --typography-heading-xl: 700 30px/24px Inter;
406
- ```
407
-
408
- ---
17
+ ```bash
18
+ # transform in smart incremental mode (default)
19
+ npx tsx src/cli.ts sync --config ./config/sxl-transform.config.yaml
409
20
 
410
- ## codeSyntax (Figma)
21
+ # force full rebuild
22
+ npx tsx src/cli.ts sync --config ./config/sxl-transform.config.yaml --force
411
23
 
412
- If a token has `$extensions.figma.codeSyntax`, the transformer uses the specified variable names:
24
+ # validate config
25
+ npx tsx src/cli.ts validate-config --config ./config/sxl-transform.config.yaml
413
26
 
414
- ```json
415
- {
416
- "primary": {
417
- "$value": "#0066FF",
418
- "$extensions": {
419
- "figma.codeSyntax": {
420
- "Web": "var(--color-primary)",
421
- "iOS": "Color.primary",
422
- "Android": "@color/primary"
423
- }
424
- }
425
- }
426
- }
27
+ # create starter config
28
+ npx tsx src/cli.ts init --path ./sxl-transform.config.yaml
427
29
  ```
428
30
 
429
- ---
430
-
431
- ## Math Expressions
432
-
433
- Tokens support mathematical expressions that are evaluated during transformation:
31
+ ### Smart vs force
434
32
 
435
- ```json
436
- {
437
- "spacing": {
438
- "sm": { "$value": "{spacing.base} * 2" },
439
- "md": { "$value": "{spacing.base} * 4" }
440
- }
441
- }
442
- ```
33
+ - `smart` (default): rebuilds only affected outputs based on changed/removed source files and previous run state.
34
+ - `force`: rebuilds all configured outputs unconditionally.
443
35
 
444
- Supported operations: `+`, `-`, `*`, `/`, `round()`.
36
+ Optional flags:
445
37
 
446
- Expressions are evaluated recursively, including sub-properties of composite tokens.
38
+ - `--mode smart|force`
39
+ - `--force` (shortcut for `--mode force`)
40
+ - `--state-file <path>` (override default state path `<config-name>.state.json`)
447
41
 
448
- ---
42
+ ## Config format
449
43
 
450
- ## Usage Examples
44
+ Only YAML is supported.
45
+ JSON config is intentionally unsupported.
451
46
 
452
- ### Transform All Projects
47
+ Minimal shape:
453
48
 
454
- ```bash
455
- # Test tokens (example)
456
- npx tsx src/cli.ts --config=config.example.json
49
+ ```yaml
50
+ version: 1
51
+ source:
52
+ tokenDir: ../../Plugin/test/tokens_project_new
53
+ configFile: config.json
54
+ include: ["**/*.json"]
55
+ exclude: ["config.json", "**/diff-id*.json"]
457
56
 
458
- # Production projects
459
- npx tsx src/cli.ts --config=config.admin-ui.json
460
- npx tsx src/cli.ts --config=config.site-ui.json
461
- ```
57
+ options:
58
+ remBase: 16
59
+ collisionStrategy: error # error | suffix | namespace-by-file | namespace-by-mode
60
+ unsupportedTypes:
61
+ default: warn
62
+ types:
63
+ template: skip
64
+ composition: skip
462
65
 
463
- ### Example Config for a New Project
66
+ tokenSets:
67
+ - id: root
68
+ selectors:
69
+ - collection: Core
70
+ mode: Default
71
+ - collection: Themes
72
+ mode: Dark
73
+ refModeMap:
74
+ Projects: AUI
75
+ Core: Default
76
+ - files: ["projects/aui/*.json"]
464
77
 
465
- ```json
466
- {
467
- "source": {
468
- "tokenDir": "./path/to/tokens",
469
- "exclude": ["config.json"]
470
- },
471
- "platforms": {
472
- "css": {
473
- "outputDir": "./dist/css",
474
- "prefix": "my",
475
- "resolveAliases": false,
476
- "splitEffects": true,
477
- "showDescriptions": true,
478
- "fileMapping": [
479
- {
480
- "sources": ["core/*.json", "themes/light.json"],
481
- "output": "core.css"
482
- },
483
- {
484
- "sources": ["themes/dark.json"],
485
- "output": "themes/dark.css"
486
- }
487
- ]
488
- },
489
- "swiftui": {
490
- "outputDir": "./dist/ios",
491
- "prefix": "My",
492
- "resolveAliases": false,
493
- "showDescriptions": true,
494
- "fileMapping": [
495
- {
496
- "sources": ["core/*.json", "themes/light.json"],
497
- "output": "MyTokens.swift"
498
- }
499
- ]
500
- },
501
- "kotlin": {
502
- "outputDir": "./dist/android",
503
- "prefix": "My",
504
- "resolveAliases": true,
505
- "showDescriptions": false,
506
- "fileMapping": [
507
- {
508
- "sources": ["core/*.json", "themes/light.json"],
509
- "output": "MyTokens.kt"
510
- }
511
- ]
512
- }
513
- },
514
- "settings": {
515
- "remBase": 16
516
- }
517
- }
78
+ outputs:
79
+ - id: css-root
80
+ platform: css # css | swift | kotlin | xml
81
+ outputDir: ./project-style/css/adminui
82
+ resolveAliases: false
83
+ splitEffects: true
84
+ showDescriptions: true
85
+ files:
86
+ - tokenSet: root
87
+ output: root.css
518
88
  ```
519
89
 
520
- ---
521
-
522
- ## Composition → Vue 3 (SFC)
523
-
524
- The transformer supports converting composition tokens from JSON into ready-to-use Vue 3 Single File Components (`.vue`).
525
-
526
- ### Usage
90
+ ## Quality gates
527
91
 
528
92
  ```bash
529
- npx tsx src/cli.ts --composition=path/to/composition.json --output=./output/dir
530
- ```
531
-
532
- | Parameter | Description |
533
- |-----------|-------------|
534
- | `--composition` | Path to the composition JSON file |
535
- | `--output` | Output directory for the `.vue` file (defaults to source directory) |
536
-
537
- ### Input JSON Format
538
-
539
- ```json
540
- {
541
- "$type": "composition",
542
- "name": "WButton",
543
- "props": {
544
- "state": ["default", "hover", "active", "disabled"],
545
- "style": ["accent", "secondary", "tertiary"],
546
- "size": ["sm", "md", "lg"]
547
- },
548
- "structure": { "tag": "FRAME", "class": "btn", "children": [...] },
549
- "componentProperties": { "label": { "type": "TEXT", "layer": "btn-label", "defaultValue": "Button" } },
550
- "styles": { "btn": { "layoutMode": "HORIZONTAL", "fills": ["{accent.medium}"], ... } },
551
- "adapters": { "style=accent": { "btn": { "fills": ["{info.medium}"] } } }
552
- }
553
- ```
554
-
555
- ### What Gets Generated
556
-
557
- A complete Vue 3 SFC with three sections:
558
-
559
- **`<template>`** — HTML structure from `structure`:
560
- - `FRAME` → `<div>` / `<button>` (inferred from component name)
561
- - `TEXT` → `<span>` with prop or slot binding
562
- - `INSTANCE` → component tag (`<WIcon>`, etc.)
563
- - `BOOLEAN` properties → `v-if` directives
564
- - `INSTANCE_SWAP` → dynamic `:name` prop
565
-
566
- **`<script setup lang="ts">`** — logic:
567
- - `defineProps<Props>()` with types from `props` and `componentProperties`
568
- - `withDefaults()` with default values
569
- - `useCssModule()` for CSS Modules
570
- - `computed()` for variant/size classes with exhaustive `Record<>` mapping
571
- - `style` prop auto-renamed to `variant` (Vue reserved word conflict)
572
- - `state=disabled` → separate `disabled` boolean prop
573
-
574
- **`<style module>`** — CSS:
575
- - Base styles from `styles` with Figma → CSS property mapping
576
- - Modifier classes from `adapters` (`.btn--accent`, `.btn--sm`, etc.)
577
- - `state=hover/active/focus` → CSS pseudo-classes (`:hover`, `:active`, `:focus`)
578
- - `state=disabled` → `.btn--disabled` + `:disabled`
579
- - Token references `{accent.medium}` → `var(--accent-medium)`
580
- - TEXT/INSTANCE layers: `fills` → `color` (not `background-color`)
581
-
582
- ### Figma → CSS Property Mapping
583
-
584
- | Figma | CSS |
585
- |-------|-----|
586
- | `layoutMode: "HORIZONTAL"` | `display: flex` |
587
- | `layoutMode: "VERTICAL"` | `display: flex; flex-direction: column` |
588
- | `primaryAxisAlignItems` | `justify-content` |
589
- | `counterAxisAlignItems` | `align-items` |
590
- | `layoutSizingHorizontal: "HUG"` | `width: fit-content` |
591
- | `layoutSizingHorizontal: "FILL"` | `flex: 1` |
592
- | `itemSpacing` | `gap` |
593
- | `padding*` | `padding` (shorthand) |
594
- | `cornerRadius` | `border-radius` |
595
- | `fills: ["{token}"]` | `background-color: var(--token)` / `color: var(--token)` |
596
- | `strokeWeight` + `strokes` | `border` |
597
- | `opacity` | `opacity` |
598
- | `fontSize` | `font-size` |
599
- | `fontWeight` | `font-weight` (name → number mapping) |
600
-
601
- ---
602
-
603
- ## Prefix Reference
604
-
605
- | Platform | Prefix | Naming Result |
606
- |----------|--------|---------------|
607
- | CSS | `""` | `--color-primary` |
608
- | CSS | `"ds"` | `--ds-color-primary` |
609
- | SwiftUI | `""` | `Color.colorPrimary`, `enum Spacing` |
610
- | SwiftUI | `"DS"` | `Color.dsColorPrimary`, `enum DSSpacing` |
611
- | Kotlin | `""` | `DSColors.ColorPrimary`, `DSSpacing.SpacingXs` |
612
- | Kotlin | `"App"` | `AppColors.ColorPrimary`, `AppSpacing.SpacingXs` |
613
-
614
- ---
615
-
616
- ## Troubleshooting
617
-
618
- ### Config file not found
93
+ npm run eslint
94
+ npm run typecheck
95
+ npm run test
96
+ npm run build
97
+ npm run check
619
98
  ```
620
- Config file not found: ...
621
- Run with --init to generate a default config.
622
- ```
623
- Use `--config=path/to/file.json` or `--init` to generate one.
624
-
625
- ### Tokens missing from output
626
- Check `fileMapping.sources` — paths are **relative to tokenDir**.
627
-
628
- ### `var()` not appearing in CSS
629
- Make sure `"resolveAliases": false` is set for the `css` platform.
630
-
631
- ### Aliases not resolving
632
- Ensure the source token exists in the included files (`include` / `sources`).
633
99
 
634
- ### Effects not splitting into separate variables
635
- Make sure `"splitEffects": true` is set for the `css` platform. Default is `true`.
100
+ ## Notes
636
101
 
637
- ### Descriptions ($description) not showing
638
- Make sure `"showDescriptions": true` is set for the desired platform. Default is `true`.
102
+ - `template` and `composition` token types are skipped by default.
103
+ - `figma.modify` is supported, including chained modifiers and alias-based modifier values.
104
+ - Math expressions are supported with safe parser (`+ - * / %`, `round`, `floor`, `ceil`, `clamp`, etc.).
105
+ - For `collection/mode` selectors you can control dependencies:
106
+ - `includeRefs: true|false` toggles using token-project `ref` chains in resolver scope.
107
+ - `refModeMap` forces specific modes for referenced collections (for example `Projects: AUI`).