dispersa 0.1.3 → 0.3.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 (53) hide show
  1. package/README.md +65 -23
  2. package/dist/builders.cjs +1923 -47
  3. package/dist/builders.cjs.map +1 -1
  4. package/dist/builders.d.cts +155 -6
  5. package/dist/builders.d.ts +155 -6
  6. package/dist/builders.js +1922 -49
  7. package/dist/builders.js.map +1 -1
  8. package/dist/cli/cli.d.ts +11 -0
  9. package/dist/cli/cli.js +201 -0
  10. package/dist/cli/cli.js.map +1 -0
  11. package/dist/cli/config.d.ts +8 -0
  12. package/dist/cli/config.js +8 -0
  13. package/dist/cli/config.js.map +1 -0
  14. package/dist/cli/index.d.ts +1 -0
  15. package/dist/cli/index.js +203 -0
  16. package/dist/cli/index.js.map +1 -0
  17. package/dist/filters.cjs +8 -7
  18. package/dist/filters.cjs.map +1 -1
  19. package/dist/filters.d.cts +13 -16
  20. package/dist/filters.d.ts +13 -16
  21. package/dist/filters.js +8 -7
  22. package/dist/filters.js.map +1 -1
  23. package/dist/{index-CPB9Ea9U.d.ts → index-BkvV7Z54.d.cts} +183 -60
  24. package/dist/{index-DKf9WMQG.d.cts → index-DJ_UHSQG.d.ts} +183 -60
  25. package/dist/index.cjs +2121 -226
  26. package/dist/index.cjs.map +1 -1
  27. package/dist/index.d.cts +8 -8
  28. package/dist/index.d.ts +8 -8
  29. package/dist/index.js +2120 -228
  30. package/dist/index.js.map +1 -1
  31. package/dist/preprocessors.cjs.map +1 -1
  32. package/dist/preprocessors.d.cts +2 -2
  33. package/dist/preprocessors.d.ts +2 -2
  34. package/dist/preprocessors.js.map +1 -1
  35. package/dist/renderers.cjs.map +1 -1
  36. package/dist/renderers.d.cts +6 -6
  37. package/dist/renderers.d.ts +6 -6
  38. package/dist/renderers.js.map +1 -1
  39. package/dist/transforms.cjs +5 -5
  40. package/dist/transforms.cjs.map +1 -1
  41. package/dist/transforms.d.cts +2 -2
  42. package/dist/transforms.d.ts +2 -2
  43. package/dist/transforms.js +5 -5
  44. package/dist/transforms.js.map +1 -1
  45. package/dist/{types-DM5faYUn.d.cts → types-BAv39mum.d.cts} +1 -1
  46. package/dist/{types-C1GpiJ6q.d.ts → types-Bc0kA7De.d.cts} +10 -10
  47. package/dist/{types-C1GpiJ6q.d.cts → types-Bc0kA7De.d.ts} +10 -10
  48. package/dist/{types-Cl-1UYGD.d.ts → types-BzNcG-rI.d.ts} +1 -1
  49. package/dist/{types-DJH6_4U9.d.ts → types-CZb19kiq.d.ts} +1 -1
  50. package/dist/{types-DbufGPrb.d.cts → types-CussyWwe.d.cts} +1 -1
  51. package/dist/{types-DdPWYkgh.d.ts → types-CzHa7YkW.d.ts} +1 -1
  52. package/dist/{types-BDY1xBmD.d.cts → types-DWKq-eJj.d.cts} +1 -1
  53. package/package.json +18 -1
package/dist/index.js CHANGED
@@ -4,7 +4,7 @@ import { constants } from 'fs';
4
4
  import { mkdir, writeFile, access, readFile } from 'fs/promises';
5
5
  import * as path from 'path';
6
6
  import { JsonPointer } from 'json-ptr';
7
- import { formatHex8, formatHex } from 'culori';
7
+ import { converter, formatHex8, formatHex } from 'culori';
8
8
  import prettier from 'prettier';
9
9
 
10
10
  var __defProp = Object.defineProperty;
@@ -159,10 +159,55 @@ var init_errors = __esm({
159
159
  }
160
160
  });
161
161
 
162
- // src/lib/validation/dtcg-schemas/2025.10/format/group.json
162
+ // src/shared/utils/validation-handler.ts
163
+ var ValidationHandler;
164
+ var init_validation_handler = __esm({
165
+ "src/shared/utils/validation-handler.ts"() {
166
+ ValidationHandler = class {
167
+ mode;
168
+ constructor(options) {
169
+ this.mode = options?.mode ?? "error";
170
+ }
171
+ /**
172
+ * Whether validation checks should run (mode is not 'off')
173
+ */
174
+ shouldValidate() {
175
+ return this.mode !== "off";
176
+ }
177
+ /**
178
+ * Whether the current mode is 'error' (strictest)
179
+ */
180
+ isStrict() {
181
+ return this.mode === "error";
182
+ }
183
+ /**
184
+ * Handle a validation issue: throw in 'error' mode, warn in 'warn' mode, ignore in 'off' mode
185
+ */
186
+ handleIssue(error) {
187
+ if (this.mode === "error") {
188
+ throw error;
189
+ }
190
+ if (this.mode === "warn") {
191
+ console.warn(error.message);
192
+ }
193
+ }
194
+ /**
195
+ * Emit a warning (in 'error' and 'warn' modes, skip in 'off')
196
+ */
197
+ warn(message) {
198
+ if (this.mode === "off") {
199
+ return;
200
+ }
201
+ console.warn(message);
202
+ }
203
+ };
204
+ }
205
+ });
206
+
207
+ // src/validation/dtcg-schemas/2025.10/format/group.json
163
208
  var group_default;
164
209
  var init_group = __esm({
165
- "src/lib/validation/dtcg-schemas/2025.10/format/group.json"() {
210
+ "src/validation/dtcg-schemas/2025.10/format/group.json"() {
166
211
  group_default = {
167
212
  $schema: "http://json-schema.org/draft-07/schema#",
168
213
  $id: "https://www.designtokens.org/schemas/2025.10/format/group.json",
@@ -220,10 +265,10 @@ var init_group = __esm({
220
265
  }
221
266
  });
222
267
 
223
- // src/lib/validation/dtcg-schemas/2025.10/format/groupOrToken.json
268
+ // src/validation/dtcg-schemas/2025.10/format/groupOrToken.json
224
269
  var groupOrToken_default;
225
270
  var init_groupOrToken = __esm({
226
- "src/lib/validation/dtcg-schemas/2025.10/format/groupOrToken.json"() {
271
+ "src/validation/dtcg-schemas/2025.10/format/groupOrToken.json"() {
227
272
  groupOrToken_default = {
228
273
  $schema: "http://json-schema.org/draft-07/schema#",
229
274
  $id: "https://www.designtokens.org/schemas/2025.10/format/groupOrToken.json",
@@ -241,10 +286,10 @@ var init_groupOrToken = __esm({
241
286
  }
242
287
  });
243
288
 
244
- // src/lib/validation/dtcg-schemas/2025.10/format/token.json
289
+ // src/validation/dtcg-schemas/2025.10/format/token.json
245
290
  var token_default;
246
291
  var init_token = __esm({
247
- "src/lib/validation/dtcg-schemas/2025.10/format/token.json"() {
292
+ "src/validation/dtcg-schemas/2025.10/format/token.json"() {
248
293
  token_default = {
249
294
  $schema: "http://json-schema.org/draft-07/schema#",
250
295
  $id: "https://www.designtokens.org/schemas/2025.10/format/token.json",
@@ -672,10 +717,10 @@ var init_token = __esm({
672
717
  }
673
718
  });
674
719
 
675
- // src/lib/validation/dtcg-schemas/2025.10/format/tokenType.json
720
+ // src/validation/dtcg-schemas/2025.10/format/tokenType.json
676
721
  var tokenType_default;
677
722
  var init_tokenType = __esm({
678
- "src/lib/validation/dtcg-schemas/2025.10/format/tokenType.json"() {
723
+ "src/validation/dtcg-schemas/2025.10/format/tokenType.json"() {
679
724
  tokenType_default = {
680
725
  $schema: "http://json-schema.org/draft-07/schema#",
681
726
  $id: "https://www.designtokens.org/schemas/2025.10/format/tokenType.json",
@@ -701,10 +746,10 @@ var init_tokenType = __esm({
701
746
  }
702
747
  });
703
748
 
704
- // src/lib/validation/dtcg-schemas/2025.10/format/values/border.json
749
+ // src/validation/dtcg-schemas/2025.10/format/values/border.json
705
750
  var border_default;
706
751
  var init_border = __esm({
707
- "src/lib/validation/dtcg-schemas/2025.10/format/values/border.json"() {
752
+ "src/validation/dtcg-schemas/2025.10/format/values/border.json"() {
708
753
  border_default = {
709
754
  $schema: "http://json-schema.org/draft-07/schema#",
710
755
  $id: "https://www.designtokens.org/schemas/2025.10/format/values/border.json",
@@ -752,10 +797,10 @@ var init_border = __esm({
752
797
  }
753
798
  });
754
799
 
755
- // src/lib/validation/dtcg-schemas/2025.10/format/values/color.json
800
+ // src/validation/dtcg-schemas/2025.10/format/values/color.json
756
801
  var color_default;
757
802
  var init_color = __esm({
758
- "src/lib/validation/dtcg-schemas/2025.10/format/values/color.json"() {
803
+ "src/validation/dtcg-schemas/2025.10/format/values/color.json"() {
759
804
  color_default = {
760
805
  $schema: "http://json-schema.org/draft-07/schema#",
761
806
  $id: "https://www.designtokens.org/schemas/2025.10/format/values/color.json",
@@ -1273,10 +1318,10 @@ var init_color = __esm({
1273
1318
  }
1274
1319
  });
1275
1320
 
1276
- // src/lib/validation/dtcg-schemas/2025.10/format/values/cubicBezier.json
1321
+ // src/validation/dtcg-schemas/2025.10/format/values/cubicBezier.json
1277
1322
  var cubicBezier_default;
1278
1323
  var init_cubicBezier = __esm({
1279
- "src/lib/validation/dtcg-schemas/2025.10/format/values/cubicBezier.json"() {
1324
+ "src/validation/dtcg-schemas/2025.10/format/values/cubicBezier.json"() {
1280
1325
  cubicBezier_default = {
1281
1326
  $schema: "http://json-schema.org/draft-07/schema#",
1282
1327
  $id: "https://www.designtokens.org/schemas/2025.10/format/values/cubicBezier.json",
@@ -1336,10 +1381,10 @@ var init_cubicBezier = __esm({
1336
1381
  }
1337
1382
  });
1338
1383
 
1339
- // src/lib/validation/dtcg-schemas/2025.10/format/values/dimension.json
1384
+ // src/validation/dtcg-schemas/2025.10/format/values/dimension.json
1340
1385
  var dimension_default;
1341
1386
  var init_dimension = __esm({
1342
- "src/lib/validation/dtcg-schemas/2025.10/format/values/dimension.json"() {
1387
+ "src/validation/dtcg-schemas/2025.10/format/values/dimension.json"() {
1343
1388
  dimension_default = {
1344
1389
  $schema: "http://json-schema.org/draft-07/schema#",
1345
1390
  $id: "https://www.designtokens.org/schemas/2025.10/format/values/dimension.json",
@@ -1377,10 +1422,10 @@ var init_dimension = __esm({
1377
1422
  }
1378
1423
  });
1379
1424
 
1380
- // src/lib/validation/dtcg-schemas/2025.10/format/values/duration.json
1425
+ // src/validation/dtcg-schemas/2025.10/format/values/duration.json
1381
1426
  var duration_default;
1382
1427
  var init_duration = __esm({
1383
- "src/lib/validation/dtcg-schemas/2025.10/format/values/duration.json"() {
1428
+ "src/validation/dtcg-schemas/2025.10/format/values/duration.json"() {
1384
1429
  duration_default = {
1385
1430
  $schema: "http://json-schema.org/draft-07/schema#",
1386
1431
  $id: "https://www.designtokens.org/schemas/2025.10/format/values/duration.json",
@@ -1418,10 +1463,10 @@ var init_duration = __esm({
1418
1463
  }
1419
1464
  });
1420
1465
 
1421
- // src/lib/validation/dtcg-schemas/2025.10/format/values/fontFamily.json
1466
+ // src/validation/dtcg-schemas/2025.10/format/values/fontFamily.json
1422
1467
  var fontFamily_default;
1423
1468
  var init_fontFamily = __esm({
1424
- "src/lib/validation/dtcg-schemas/2025.10/format/values/fontFamily.json"() {
1469
+ "src/validation/dtcg-schemas/2025.10/format/values/fontFamily.json"() {
1425
1470
  fontFamily_default = {
1426
1471
  $schema: "http://json-schema.org/draft-07/schema#",
1427
1472
  $id: "https://www.designtokens.org/schemas/2025.10/format/values/fontFamily.json",
@@ -1458,10 +1503,10 @@ var init_fontFamily = __esm({
1458
1503
  }
1459
1504
  });
1460
1505
 
1461
- // src/lib/validation/dtcg-schemas/2025.10/format/values/fontWeight.json
1506
+ // src/validation/dtcg-schemas/2025.10/format/values/fontWeight.json
1462
1507
  var fontWeight_default;
1463
1508
  var init_fontWeight = __esm({
1464
- "src/lib/validation/dtcg-schemas/2025.10/format/values/fontWeight.json"() {
1509
+ "src/validation/dtcg-schemas/2025.10/format/values/fontWeight.json"() {
1465
1510
  fontWeight_default = {
1466
1511
  $schema: "http://json-schema.org/draft-07/schema#",
1467
1512
  $id: "https://www.designtokens.org/schemas/2025.10/format/values/fontWeight.json",
@@ -1503,10 +1548,10 @@ var init_fontWeight = __esm({
1503
1548
  }
1504
1549
  });
1505
1550
 
1506
- // src/lib/validation/dtcg-schemas/2025.10/format/values/gradient.json
1551
+ // src/validation/dtcg-schemas/2025.10/format/values/gradient.json
1507
1552
  var gradient_default;
1508
1553
  var init_gradient = __esm({
1509
- "src/lib/validation/dtcg-schemas/2025.10/format/values/gradient.json"() {
1554
+ "src/validation/dtcg-schemas/2025.10/format/values/gradient.json"() {
1510
1555
  gradient_default = {
1511
1556
  $schema: "http://json-schema.org/draft-07/schema#",
1512
1557
  $id: "https://www.designtokens.org/schemas/2025.10/format/values/gradient.json",
@@ -1562,10 +1607,10 @@ var init_gradient = __esm({
1562
1607
  }
1563
1608
  });
1564
1609
 
1565
- // src/lib/validation/dtcg-schemas/2025.10/format/values/number.json
1610
+ // src/validation/dtcg-schemas/2025.10/format/values/number.json
1566
1611
  var number_default;
1567
1612
  var init_number = __esm({
1568
- "src/lib/validation/dtcg-schemas/2025.10/format/values/number.json"() {
1613
+ "src/validation/dtcg-schemas/2025.10/format/values/number.json"() {
1569
1614
  number_default = {
1570
1615
  $schema: "http://json-schema.org/draft-07/schema#",
1571
1616
  $id: "https://www.designtokens.org/schemas/2025.10/format/values/number.json",
@@ -1576,10 +1621,10 @@ var init_number = __esm({
1576
1621
  }
1577
1622
  });
1578
1623
 
1579
- // src/lib/validation/dtcg-schemas/2025.10/format/values/shadow.json
1624
+ // src/validation/dtcg-schemas/2025.10/format/values/shadow.json
1580
1625
  var shadow_default;
1581
1626
  var init_shadow = __esm({
1582
- "src/lib/validation/dtcg-schemas/2025.10/format/values/shadow.json"() {
1627
+ "src/validation/dtcg-schemas/2025.10/format/values/shadow.json"() {
1583
1628
  shadow_default = {
1584
1629
  $schema: "http://json-schema.org/draft-07/schema#",
1585
1630
  $id: "https://www.designtokens.org/schemas/2025.10/format/values/shadow.json",
@@ -1685,10 +1730,10 @@ var init_shadow = __esm({
1685
1730
  }
1686
1731
  });
1687
1732
 
1688
- // src/lib/validation/dtcg-schemas/2025.10/format/values/strokeStyle.json
1733
+ // src/validation/dtcg-schemas/2025.10/format/values/strokeStyle.json
1689
1734
  var strokeStyle_default;
1690
1735
  var init_strokeStyle = __esm({
1691
- "src/lib/validation/dtcg-schemas/2025.10/format/values/strokeStyle.json"() {
1736
+ "src/validation/dtcg-schemas/2025.10/format/values/strokeStyle.json"() {
1692
1737
  strokeStyle_default = {
1693
1738
  $schema: "http://json-schema.org/draft-07/schema#",
1694
1739
  $id: "https://www.designtokens.org/schemas/2025.10/format/values/strokeStyle.json",
@@ -1746,10 +1791,10 @@ var init_strokeStyle = __esm({
1746
1791
  }
1747
1792
  });
1748
1793
 
1749
- // src/lib/validation/dtcg-schemas/2025.10/format/values/transition.json
1794
+ // src/validation/dtcg-schemas/2025.10/format/values/transition.json
1750
1795
  var transition_default;
1751
1796
  var init_transition = __esm({
1752
- "src/lib/validation/dtcg-schemas/2025.10/format/values/transition.json"() {
1797
+ "src/validation/dtcg-schemas/2025.10/format/values/transition.json"() {
1753
1798
  transition_default = {
1754
1799
  $schema: "http://json-schema.org/draft-07/schema#",
1755
1800
  $id: "https://www.designtokens.org/schemas/2025.10/format/values/transition.json",
@@ -1797,10 +1842,10 @@ var init_transition = __esm({
1797
1842
  }
1798
1843
  });
1799
1844
 
1800
- // src/lib/validation/dtcg-schemas/2025.10/format/values/typography.json
1845
+ // src/validation/dtcg-schemas/2025.10/format/values/typography.json
1801
1846
  var typography_default;
1802
1847
  var init_typography = __esm({
1803
- "src/lib/validation/dtcg-schemas/2025.10/format/values/typography.json"() {
1848
+ "src/validation/dtcg-schemas/2025.10/format/values/typography.json"() {
1804
1849
  typography_default = {
1805
1850
  $schema: "http://json-schema.org/draft-07/schema#",
1806
1851
  $id: "https://www.designtokens.org/schemas/2025.10/format/values/typography.json",
@@ -1870,10 +1915,10 @@ var init_typography = __esm({
1870
1915
  }
1871
1916
  });
1872
1917
 
1873
- // src/lib/validation/dtcg-schemas/2025.10/format.json
1918
+ // src/validation/dtcg-schemas/2025.10/format.json
1874
1919
  var format_default;
1875
1920
  var init_format = __esm({
1876
- "src/lib/validation/dtcg-schemas/2025.10/format.json"() {
1921
+ "src/validation/dtcg-schemas/2025.10/format.json"() {
1877
1922
  format_default = {
1878
1923
  $schema: "http://json-schema.org/draft-07/schema#",
1879
1924
  $id: "https://www.designtokens.org/schemas/2025.10/format.json",
@@ -1982,10 +2027,10 @@ var init_format = __esm({
1982
2027
  }
1983
2028
  });
1984
2029
 
1985
- // src/lib/validation/dtcg-schemas/2025.10/resolver/modifier.json
2030
+ // src/validation/dtcg-schemas/2025.10/resolver/modifier.json
1986
2031
  var modifier_default;
1987
2032
  var init_modifier = __esm({
1988
- "src/lib/validation/dtcg-schemas/2025.10/resolver/modifier.json"() {
2033
+ "src/validation/dtcg-schemas/2025.10/resolver/modifier.json"() {
1989
2034
  modifier_default = {
1990
2035
  $schema: "http://json-schema.org/draft-07/schema#",
1991
2036
  $id: "https://www.designtokens.org/schemas/2025.10/resolver/modifier.json",
@@ -2073,10 +2118,10 @@ var init_modifier = __esm({
2073
2118
  }
2074
2119
  });
2075
2120
 
2076
- // src/lib/validation/dtcg-schemas/2025.10/resolver/resolutionOrder.json
2121
+ // src/validation/dtcg-schemas/2025.10/resolver/resolutionOrder.json
2077
2122
  var resolutionOrder_default;
2078
2123
  var init_resolutionOrder = __esm({
2079
- "src/lib/validation/dtcg-schemas/2025.10/resolver/resolutionOrder.json"() {
2124
+ "src/validation/dtcg-schemas/2025.10/resolver/resolutionOrder.json"() {
2080
2125
  resolutionOrder_default = {
2081
2126
  $schema: "http://json-schema.org/draft-07/schema#",
2082
2127
  $id: "https://www.designtokens.org/schemas/2025.10/resolver/resolutionOrder.json",
@@ -2196,10 +2241,10 @@ var init_resolutionOrder = __esm({
2196
2241
  }
2197
2242
  });
2198
2243
 
2199
- // src/lib/validation/dtcg-schemas/2025.10/resolver/set.json
2244
+ // src/validation/dtcg-schemas/2025.10/resolver/set.json
2200
2245
  var set_default;
2201
2246
  var init_set = __esm({
2202
- "src/lib/validation/dtcg-schemas/2025.10/resolver/set.json"() {
2247
+ "src/validation/dtcg-schemas/2025.10/resolver/set.json"() {
2203
2248
  set_default = {
2204
2249
  $schema: "http://json-schema.org/draft-07/schema#",
2205
2250
  $id: "https://www.designtokens.org/schemas/2025.10/resolver/set.json",
@@ -2273,10 +2318,10 @@ var init_set = __esm({
2273
2318
  }
2274
2319
  });
2275
2320
 
2276
- // src/lib/validation/dtcg-schemas/2025.10/resolver.json
2321
+ // src/validation/dtcg-schemas/2025.10/resolver.json
2277
2322
  var resolver_default;
2278
2323
  var init_resolver = __esm({
2279
- "src/lib/validation/dtcg-schemas/2025.10/resolver.json"() {
2324
+ "src/validation/dtcg-schemas/2025.10/resolver.json"() {
2280
2325
  resolver_default = {
2281
2326
  $schema: "http://json-schema.org/draft-07/schema#",
2282
2327
  $id: "https://www.designtokens.org/schemas/2025.10/resolver.json",
@@ -2341,10 +2386,10 @@ var init_resolver = __esm({
2341
2386
  }
2342
2387
  });
2343
2388
 
2344
- // src/lib/validation/schemas.ts
2389
+ // src/validation/schemas.ts
2345
2390
  var formatSchema, tokenSchema, tokenTypeSchema, groupSchema, groupOrTokenSchema, colorValueSchema, dimensionValueSchema, fontFamilyValueSchema, fontWeightValueSchema, durationValueSchema, cubicBezierValueSchema, numberValueSchema, strokeStyleValueSchema, borderValueSchema, transitionValueSchema, shadowValueSchema, gradientValueSchema, typographyValueSchema, resolverSchema, resolverSetSchema, resolverModifierSchema, resolverResolutionOrderSchema, dtcgSchemaRegistry;
2346
2391
  var init_schemas = __esm({
2347
- "src/lib/validation/schemas.ts"() {
2392
+ "src/validation/schemas.ts"() {
2348
2393
  init_group();
2349
2394
  init_groupOrToken();
2350
2395
  init_token();
@@ -2416,10 +2461,10 @@ var init_schemas = __esm({
2416
2461
  }
2417
2462
  });
2418
2463
 
2419
- // src/lib/validation/config-schemas.ts
2464
+ // src/validation/config-schemas.ts
2420
2465
  var resolverSchemaRef, basePluginProperties, commonRendererOptionsProperties, transformPluginSchema, rendererPluginSchema, filterPluginSchema, preprocessorPluginSchema, outputConfigSchema, dispersaOptionsSchema, buildConfigSchema;
2421
2466
  var init_config_schemas = __esm({
2422
- "src/lib/validation/config-schemas.ts"() {
2467
+ "src/validation/config-schemas.ts"() {
2423
2468
  init_schemas();
2424
2469
  resolverSchemaRef = resolverSchema;
2425
2470
  basePluginProperties = {
@@ -2473,6 +2518,25 @@ var init_config_schemas = __esm({
2473
2518
  additionalProperties: true
2474
2519
  // Allow custom properties for extended renderers
2475
2520
  });
2521
+ ({
2522
+ $schema: "http://json-schema.org/draft-07/schema#",
2523
+ type: "object",
2524
+ properties: {
2525
+ preset: { type: "string", enum: ["bundle", "standalone"] },
2526
+ includeImport: {
2527
+ type: "boolean",
2528
+ description: 'Prepend @import "tailwindcss" to the output'
2529
+ },
2530
+ namespace: {
2531
+ type: "string",
2532
+ description: "Optional Tailwind namespace prefix for @theme"
2533
+ },
2534
+ selector: { type: "string" },
2535
+ mediaQuery: { type: "string" },
2536
+ ...commonRendererOptionsProperties
2537
+ },
2538
+ additionalProperties: true
2539
+ });
2476
2540
  transformPluginSchema = {
2477
2541
  $schema: "http://json-schema.org/draft-07/schema#",
2478
2542
  type: "object",
@@ -2678,7 +2742,7 @@ var init_config_schemas = __esm({
2678
2742
  });
2679
2743
  var SchemaValidator;
2680
2744
  var init_validator = __esm({
2681
- "src/lib/validation/validator.ts"() {
2745
+ "src/validation/validator.ts"() {
2682
2746
  init_errors();
2683
2747
  init_token_utils();
2684
2748
  init_config_schemas();
@@ -2930,63 +2994,18 @@ var init_validator = __esm({
2930
2994
  }
2931
2995
  });
2932
2996
 
2933
- // src/lib/validation/index.ts
2997
+ // src/validation/index.ts
2934
2998
  var init_validation = __esm({
2935
- "src/lib/validation/index.ts"() {
2999
+ "src/validation/index.ts"() {
2936
3000
  init_validator();
2937
3001
  }
2938
3002
  });
2939
-
2940
- // src/shared/utils/validation-handler.ts
2941
- var ValidationHandler;
2942
- var init_validation_handler = __esm({
2943
- "src/shared/utils/validation-handler.ts"() {
2944
- ValidationHandler = class {
2945
- mode;
2946
- constructor(options) {
2947
- this.mode = options?.mode ?? "error";
2948
- }
2949
- /**
2950
- * Whether validation checks should run (mode is not 'off')
2951
- */
2952
- shouldValidate() {
2953
- return this.mode !== "off";
2954
- }
2955
- /**
2956
- * Whether the current mode is 'error' (strictest)
2957
- */
2958
- isStrict() {
2959
- return this.mode === "error";
2960
- }
2961
- /**
2962
- * Handle a validation issue: throw in 'error' mode, warn in 'warn' mode, ignore in 'off' mode
2963
- */
2964
- handleIssue(error) {
2965
- if (this.mode === "error") {
2966
- throw error;
2967
- }
2968
- if (this.mode === "warn") {
2969
- console.warn(error.message);
2970
- }
2971
- }
2972
- /**
2973
- * Emit a warning (in 'error' and 'warn' modes, skip in 'off')
2974
- */
2975
- warn(message) {
2976
- if (this.mode === "off") {
2977
- return;
2978
- }
2979
- console.warn(message);
2980
- }
2981
- };
2982
- }
2983
- });
2984
3003
  var ResolverParser;
2985
3004
  var init_resolver_parser = __esm({
2986
3005
  "src/adapters/filesystem/resolver-parser.ts"() {
2987
- init_validation();
2988
3006
  init_errors();
2989
3007
  init_validation_handler();
3008
+ init_validation();
2990
3009
  ResolverParser = class {
2991
3010
  validator;
2992
3011
  options;
@@ -3374,32 +3393,55 @@ function filterTokensBySource(tokens, expectedSource) {
3374
3393
  }
3375
3394
  return filtered;
3376
3395
  }
3377
- function interpolatePattern(pattern, modifierInputs, modifierName, context) {
3378
- let result = pattern;
3379
- if (modifierName !== void 0) {
3380
- result = result.replace(/\{modifierName\}/g, modifierName);
3396
+ function filterTokensFromSets(tokens) {
3397
+ const filtered = {};
3398
+ for (const [name, token] of Object.entries(tokens)) {
3399
+ const hasModifierSource = typeof token._sourceModifier === "string" && token._sourceModifier !== "";
3400
+ if (!hasModifierSource) {
3401
+ filtered[name] = token;
3402
+ }
3381
3403
  }
3382
- if (context !== void 0) {
3383
- result = result.replace(/\{context\}/g, context);
3404
+ return filtered;
3405
+ }
3406
+ function resolveBaseFileName(fileName, defaults) {
3407
+ if (typeof fileName === "function") {
3408
+ return fileName({ ...defaults, _base: "true" });
3409
+ }
3410
+ if (/\{.+?\}/.test(fileName)) {
3411
+ const baseInputs = Object.fromEntries(Object.keys(defaults).map((k) => [k, "base"]));
3412
+ return collapseBaseSegments(interpolatePattern(fileName, baseInputs));
3413
+ }
3414
+ const extMatch = fileName.match(/(\.[^.]+)$/);
3415
+ const extension = extMatch ? extMatch[1] : "";
3416
+ const baseName = extension ? fileName.slice(0, -extension.length) : fileName;
3417
+ return `${baseName}-base${extension}`;
3418
+ }
3419
+ function collapseBaseSegments(value) {
3420
+ let result = value;
3421
+ let previous = "";
3422
+ while (result !== previous) {
3423
+ previous = result;
3424
+ result = result.replace(/\bbase([/-])base\b/, "base");
3384
3425
  }
3426
+ return result;
3427
+ }
3428
+ function interpolatePattern(pattern, modifierInputs) {
3429
+ let result = pattern;
3385
3430
  for (const [key, value] of Object.entries(modifierInputs)) {
3386
3431
  result = result.replaceAll(`{${key}}`, value);
3387
3432
  }
3388
3433
  return result;
3389
3434
  }
3390
- function resolveFileName(fileName, modifierInputs, modifierName, context) {
3435
+ function resolveFileName(fileName, modifierInputs) {
3391
3436
  if (typeof fileName === "function") {
3392
3437
  return fileName(modifierInputs);
3393
3438
  }
3394
3439
  if (/\{.+?\}/.test(fileName)) {
3395
- return interpolatePattern(fileName, modifierInputs, modifierName, context);
3440
+ return interpolatePattern(fileName, modifierInputs);
3396
3441
  }
3397
3442
  const extMatch = fileName.match(/(\.[^.]+)$/);
3398
3443
  const extension = extMatch ? extMatch[1] : "";
3399
3444
  const baseName = extension ? fileName.slice(0, -extension.length) : fileName;
3400
- if (modifierName !== void 0 && context !== void 0) {
3401
- return `${baseName}-${modifierName}-${context}${extension}`;
3402
- }
3403
3445
  const modifierSuffix = Object.entries(modifierInputs).sort(([keyA], [keyB]) => keyA.localeCompare(keyB)).map(([key, value]) => `${key}-${value}`).join("-");
3404
3446
  if (modifierSuffix) {
3405
3447
  return `${baseName}-${modifierSuffix}${extension}`;
@@ -3568,7 +3610,7 @@ var init_json = __esm({
3568
3610
  }
3569
3611
  });
3570
3612
 
3571
- // src/lib/codegen/type-generator.ts
3613
+ // src/codegen/type-generator.ts
3572
3614
  var TypeGenerator = class {
3573
3615
  /**
3574
3616
  * Generates complete TypeScript type definitions from resolved tokens
@@ -3700,8 +3742,8 @@ var TypeGenerator = class {
3700
3742
  /**
3701
3743
  * Add structure properties to lines
3702
3744
  */
3703
- addStructureProperties(lines, structure, indent) {
3704
- const indentStr = " ".repeat(indent);
3745
+ addStructureProperties(lines, structure, indent2) {
3746
+ const indentStr = " ".repeat(indent2);
3705
3747
  for (const [key, value] of Object.entries(structure)) {
3706
3748
  if (this.isToken(value)) {
3707
3749
  const token = value;
@@ -3712,7 +3754,7 @@ var TypeGenerator = class {
3712
3754
  lines.push(`${indentStr}${this.quoteKey(key)}: ${valueType}`);
3713
3755
  } else {
3714
3756
  lines.push(`${indentStr}${this.quoteKey(key)}: {`);
3715
- this.addStructureProperties(lines, value, indent + 1);
3757
+ this.addStructureProperties(lines, value, indent2 + 1);
3716
3758
  lines.push(`${indentStr}}`);
3717
3759
  }
3718
3760
  }
@@ -4057,7 +4099,7 @@ async function writeOutputFile(fileName, content, encoding = "utf-8") {
4057
4099
  }
4058
4100
  }
4059
4101
 
4060
- // src/lib/processing/token-modifier.ts
4102
+ // src/processing/token-modifier.ts
4061
4103
  function applyTransforms(tokens, transformList) {
4062
4104
  const result = {};
4063
4105
  for (const [name, token] of Object.entries(tokens)) {
@@ -4189,7 +4231,7 @@ init_validation_handler();
4189
4231
  // src/shared/constants.ts
4190
4232
  var DEFAULT_MAX_ALIAS_DEPTH = 10;
4191
4233
 
4192
- // src/lib/resolution/alias-resolver.ts
4234
+ // src/resolution/alias-resolver.ts
4193
4235
  init_errors();
4194
4236
 
4195
4237
  // src/shared/utils/string-similarity.ts
@@ -4240,7 +4282,7 @@ function findSimilar(target, candidates, maxDistance, maxResults = 3) {
4240
4282
  return scored.slice(0, maxResults).map((entry) => entry.value);
4241
4283
  }
4242
4284
 
4243
- // src/lib/resolution/alias-resolver.ts
4285
+ // src/resolution/alias-resolver.ts
4244
4286
  init_token_utils();
4245
4287
  init_validation_handler();
4246
4288
  var AliasResolver = class _AliasResolver {
@@ -4498,7 +4540,7 @@ var AliasResolver = class _AliasResolver {
4498
4540
  }
4499
4541
  };
4500
4542
 
4501
- // src/lib/resolution/reference-resolver.ts
4543
+ // src/resolution/reference-resolver.ts
4502
4544
  init_errors();
4503
4545
  init_validation_handler();
4504
4546
  var ReferenceResolver = class _ReferenceResolver {
@@ -4805,7 +4847,7 @@ var ReferenceResolver = class _ReferenceResolver {
4805
4847
  }
4806
4848
  };
4807
4849
 
4808
- // src/lib/resolution/resolution-engine.ts
4850
+ // src/resolution/resolution-engine.ts
4809
4851
  init_errors();
4810
4852
 
4811
4853
  // src/shared/utils/case-insensitive-map.ts
@@ -4907,10 +4949,10 @@ var CaseInsensitiveMap = class {
4907
4949
  }
4908
4950
  };
4909
4951
 
4910
- // src/lib/resolution/resolution-engine.ts
4952
+ // src/resolution/resolution-engine.ts
4911
4953
  init_validation_handler();
4912
4954
 
4913
- // src/lib/resolution/modifier-input-processor.ts
4955
+ // src/resolution/modifier-input-processor.ts
4914
4956
  init_errors();
4915
4957
  var ModifierInputProcessor = class {
4916
4958
  modifiers;
@@ -5093,7 +5135,7 @@ var ModifierInputProcessor = class {
5093
5135
  }
5094
5136
  };
5095
5137
 
5096
- // src/lib/resolution/resolution-engine.ts
5138
+ // src/resolution/resolution-engine.ts
5097
5139
  var JSON_POINTER_SETS_PREFIX = "#/sets/";
5098
5140
  var JSON_POINTER_MODIFIERS_PREFIX = "#/modifiers/";
5099
5141
  var ResolutionEngine = class {
@@ -5378,7 +5420,6 @@ var ResolutionEngine = class {
5378
5420
  return typeof obj === "object" && obj !== null && "contexts" in obj && typeof obj.contexts === "object";
5379
5421
  }
5380
5422
  };
5381
- init_validator();
5382
5423
  init_errors();
5383
5424
 
5384
5425
  // src/shared/utils/path-utils.ts
@@ -5389,11 +5430,12 @@ function formatTokenPath(parentPath, name) {
5389
5430
  return parentPath.length > 0 ? `${parentPath.join(".")}.${name}` : name;
5390
5431
  }
5391
5432
 
5392
- // src/lib/tokens/token-parser.ts
5433
+ // src/tokens/token-parser.ts
5393
5434
  init_token_utils();
5394
5435
  init_validation_handler();
5436
+ init_validator();
5395
5437
 
5396
- // src/lib/tokens/group-extension-resolver.ts
5438
+ // src/tokens/group-extension-resolver.ts
5397
5439
  init_errors();
5398
5440
  init_token_utils();
5399
5441
  var GroupExtensionResolver = class {
@@ -5555,7 +5597,7 @@ var GroupExtensionResolver = class {
5555
5597
  }
5556
5598
  };
5557
5599
 
5558
- // src/lib/tokens/token-parser.ts
5600
+ // src/tokens/token-parser.ts
5559
5601
  var INVALID_NAME_CHARS_REGEX = /[{}.]/;
5560
5602
  var TokenParser = class {
5561
5603
  validator;
@@ -6068,9 +6110,9 @@ var TokenPipeline = class {
6068
6110
  };
6069
6111
 
6070
6112
  // src/dispersa.ts
6071
- init_validator();
6072
6113
  init_errors();
6073
6114
  init_token_utils();
6115
+ init_validator();
6074
6116
  var Dispersa = class {
6075
6117
  validator;
6076
6118
  pipeline;
@@ -6352,7 +6394,7 @@ var Dispersa = class {
6352
6394
  }
6353
6395
  };
6354
6396
 
6355
- // src/lib/tokens/types.ts
6397
+ // src/tokens/types.ts
6356
6398
  function isColorToken(token) {
6357
6399
  return token.$type === "color";
6358
6400
  }
@@ -6434,7 +6476,7 @@ function colorObjectToHex(color) {
6434
6476
  return formatHex(culoriColor);
6435
6477
  }
6436
6478
 
6437
- // src/lib/processing/processors/transforms/built-in/dimension-converter.ts
6479
+ // src/processing/processors/transforms/built-in/dimension-converter.ts
6438
6480
  function isDimensionObject(value) {
6439
6481
  return typeof value === "object" && value !== null && "value" in value && "unit" in value;
6440
6482
  }
@@ -6442,98 +6484,839 @@ function dimensionObjectToString(dimension) {
6442
6484
  return `${dimension.value}${dimension.unit}`;
6443
6485
  }
6444
6486
 
6445
- // src/renderers/css.ts
6487
+ // src/renderers/android.ts
6446
6488
  init_errors();
6447
6489
  init_token_utils();
6448
-
6449
- // src/renderers/bundlers/css.ts
6450
- init_errors();
6451
6490
  init_utils();
6452
- var getSourceSet = (token) => {
6453
- if (typeof token !== "object" || token === null) {
6454
- return void 0;
6455
- }
6456
- const maybe = token;
6457
- return typeof maybe._sourceSet === "string" ? maybe._sourceSet : void 0;
6491
+ var toSRGB = converter("rgb");
6492
+ var toP3 = converter("p3");
6493
+ var KOTLIN_KEYWORDS = /* @__PURE__ */ new Set([
6494
+ "val",
6495
+ "var",
6496
+ "fun",
6497
+ "class",
6498
+ "object",
6499
+ "when",
6500
+ "is",
6501
+ "in",
6502
+ "return",
6503
+ "break",
6504
+ "continue",
6505
+ "do",
6506
+ "while",
6507
+ "for",
6508
+ "if",
6509
+ "else",
6510
+ "try",
6511
+ "catch",
6512
+ "throw",
6513
+ "as",
6514
+ "this",
6515
+ "super",
6516
+ "null",
6517
+ "true",
6518
+ "false"
6519
+ ]);
6520
+ var KOTLIN_TYPE_GROUP_MAP = {
6521
+ color: "Colors",
6522
+ dimension: "Spacing",
6523
+ fontFamily: "Fonts",
6524
+ fontWeight: "FontWeights",
6525
+ duration: "Durations",
6526
+ shadow: "Shadows",
6527
+ typography: "Typography",
6528
+ number: "Numbers",
6529
+ cubicBezier: "Animations",
6530
+ border: "Borders"
6458
6531
  };
6459
- var getSourceModifier = (token) => {
6460
- if (typeof token !== "object" || token === null) {
6461
- return void 0;
6532
+ function resolveColorFormat(format) {
6533
+ if (format === "argb_floats" || format === "argb_float") {
6534
+ return "argb_float";
6462
6535
  }
6463
- const maybe = token;
6464
- return typeof maybe._sourceModifier === "string" ? maybe._sourceModifier : void 0;
6465
- };
6466
- async function bundleAsCss(bundleData, resolver, options, formatTokens) {
6467
- const baseItem = bundleData.find((item) => item.isBase);
6468
- if (!baseItem) {
6469
- throw new BasePermutationError("Base permutation not found in bundle data");
6536
+ return "argb_hex";
6537
+ }
6538
+ function indent(width, level) {
6539
+ return " ".repeat(width * level);
6540
+ }
6541
+ function escapeKotlinString(str) {
6542
+ return str.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\$/g, "\\$");
6543
+ }
6544
+ function escapeKDoc(str) {
6545
+ return str.replace(/\*\//g, "* /").replace(/\r?\n/g, " ").trim();
6546
+ }
6547
+ function formatKotlinNumber(value) {
6548
+ return Number.isInteger(value) ? `${value}.0` : String(value);
6549
+ }
6550
+ function roundComponent(value) {
6551
+ return Math.round(value * 1e3) / 1e3;
6552
+ }
6553
+ function toResourceName(family) {
6554
+ return family.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_|_$/g, "");
6555
+ }
6556
+ function toPascalCase(name) {
6557
+ const pascal = name.replace(/[-._]+(.)/g, (_, c) => c.toUpperCase()).replace(/[-._]+$/g, "").replace(/^[-._]+/g, "");
6558
+ const result = pascal.charAt(0).toUpperCase() + pascal.slice(1);
6559
+ if (/^\d/.test(result)) {
6560
+ return `_${result}`;
6470
6561
  }
6471
- if (!formatTokens) {
6472
- throw new ConfigurationError("CSS formatter was not provided");
6562
+ return KOTLIN_KEYWORDS.has(result.charAt(0).toLowerCase() + result.slice(1)) ? `\`${result}\`` : result;
6563
+ }
6564
+ function toKotlinIdentifier(name) {
6565
+ const camel = name.replace(/[-._]+(.)/g, (_, c) => c.toUpperCase()).replace(/[-._]+$/g, "").replace(/^[-._]+/g, "");
6566
+ const identifier = camel.charAt(0).toLowerCase() + camel.slice(1);
6567
+ if (/^\d/.test(identifier)) {
6568
+ return `_${identifier}`;
6473
6569
  }
6474
- const orderedBundleData = orderBundleData(bundleData, resolver, baseItem);
6475
- const cssBlocks = [];
6476
- for (const item of orderedBundleData) {
6477
- if (item.isBase) {
6478
- const blocks = await formatBasePermutation(item, resolver, options, formatTokens);
6479
- cssBlocks.push(...blocks);
6480
- continue;
6570
+ return KOTLIN_KEYWORDS.has(identifier) ? `\`${identifier}\`` : identifier;
6571
+ }
6572
+ var AndroidRenderer = class {
6573
+ async format(context, options) {
6574
+ if (!options?.packageName) {
6575
+ throw new ConfigurationError(
6576
+ `Output "${context.output.name}": packageName is required for Android output`
6577
+ );
6481
6578
  }
6482
- const block = await formatModifierPermutation(item, baseItem, options, formatTokens);
6483
- if (block) {
6484
- cssBlocks.push(block);
6579
+ const opts = {
6580
+ preset: options?.preset ?? "standalone",
6581
+ packageName: options.packageName,
6582
+ objectName: options?.objectName ?? "DesignTokens",
6583
+ colorFormat: resolveColorFormat(options?.colorFormat),
6584
+ colorSpace: options?.colorSpace ?? "sRGB",
6585
+ structure: options?.structure ?? "nested",
6586
+ visibility: options?.visibility,
6587
+ indent: options?.indent ?? 4
6588
+ };
6589
+ if (opts.preset === "bundle") {
6590
+ return await this.formatBundle(context, opts);
6485
6591
  }
6592
+ return await this.formatStandalone(context, opts);
6486
6593
  }
6487
- return cssBlocks.join("\n\n");
6488
- }
6489
- async function formatBasePermutation({ tokens, modifierInputs }, resolver, options, formatTokens) {
6490
- const firstModifierName = resolver.modifiers ? Object.keys(resolver.modifiers)[0] : "";
6491
- const modifier = firstModifierName ?? "";
6492
- const context = modifierInputs[modifier] ?? "";
6493
- const selector = resolveSelector(options?.selector, modifier, context, true, modifierInputs);
6494
- const mediaQuery = resolveMediaQuery(options?.mediaQuery, modifier, context, true, modifierInputs);
6495
- const referenceTokens = stripInternalMetadata(tokens);
6496
- const defaultBlocks = buildDefaultLayerBlocks(tokens, modifierInputs, resolver);
6497
- const cssBlocks = [];
6498
- for (const block of defaultBlocks) {
6499
- const cleanTokens = stripInternalMetadata(block.tokens);
6500
- const css2 = await formatTokens(cleanTokens, {
6501
- selector,
6502
- mediaQuery,
6503
- minify: options?.minify,
6504
- referenceTokens
6594
+ // -----------------------------------------------------------------------
6595
+ // Token tree (nested mode)
6596
+ // -----------------------------------------------------------------------
6597
+ buildTokenTree(tokens) {
6598
+ const root = { children: /* @__PURE__ */ new Map() };
6599
+ for (const [, token] of getSortedTokenEntries(tokens)) {
6600
+ let current = root;
6601
+ const segments = token.path;
6602
+ for (let i = 0; i < segments.length - 1; i++) {
6603
+ const seg = segments[i];
6604
+ if (!current.children.has(seg)) {
6605
+ current.children.set(seg, { children: /* @__PURE__ */ new Map() });
6606
+ }
6607
+ current = current.children.get(seg);
6608
+ }
6609
+ const leafName = segments[segments.length - 1] ?? token.name;
6610
+ const leaf = current.children.get(leafName) ?? { children: /* @__PURE__ */ new Map() };
6611
+ leaf.token = token;
6612
+ current.children.set(leafName, leaf);
6613
+ }
6614
+ return root;
6615
+ }
6616
+ // -----------------------------------------------------------------------
6617
+ // Flat structure grouping
6618
+ // -----------------------------------------------------------------------
6619
+ groupTokensByType(tokens) {
6620
+ const groupMap = /* @__PURE__ */ new Map();
6621
+ for (const [, token] of getSortedTokenEntries(tokens)) {
6622
+ const groupName = KOTLIN_TYPE_GROUP_MAP[token.$type ?? ""] ?? "Other";
6623
+ const existing = groupMap.get(groupName) ?? [];
6624
+ existing.push(token);
6625
+ groupMap.set(groupName, existing);
6626
+ }
6627
+ return Array.from(groupMap.entries()).map(([name, groupTokens]) => ({
6628
+ name,
6629
+ tokens: groupTokens
6630
+ }));
6631
+ }
6632
+ /**
6633
+ * Builds a flattened camelCase name from a token's path, stripping the
6634
+ * type prefix segment (which is already represented by the group object).
6635
+ */
6636
+ buildFlatKotlinName(token) {
6637
+ const path7 = token.path;
6638
+ const withoutTypePrefix = path7.length > 1 ? path7.slice(1) : path7;
6639
+ const joined = withoutTypePrefix.join("_");
6640
+ return toKotlinIdentifier(joined);
6641
+ }
6642
+ // -----------------------------------------------------------------------
6643
+ // Rendering
6644
+ // -----------------------------------------------------------------------
6645
+ formatTokens(tokens, options) {
6646
+ if (options.structure === "flat") {
6647
+ return this.formatAsFlat(tokens, options);
6648
+ }
6649
+ return this.formatAsNested(tokens, options);
6650
+ }
6651
+ formatAsNested(tokens, options) {
6652
+ const tree = this.buildTokenTree(tokens);
6653
+ const tokenTypes = /* @__PURE__ */ new Set();
6654
+ this.collectTokenTypes(tree, tokenTypes);
6655
+ return this.buildFile(tokenTypes, options, (lines, vis) => {
6656
+ lines.push(`@Suppress("unused")`);
6657
+ lines.push(`${vis}object ${options.objectName} {`);
6658
+ this.renderTreeChildren(lines, tree, 1, options);
6659
+ lines.push("}");
6505
6660
  });
6506
- const header = block.description ? `/* ${block.key} */
6507
- /* ${block.description} */` : `/* ${block.key} */`;
6508
- cssBlocks.push(`${header}
6509
- ${css2}`);
6510
6661
  }
6511
- return cssBlocks;
6512
- }
6513
- async function formatModifierPermutation({ tokens, modifierInputs }, baseItem, options, formatTokens) {
6514
- const differenceCount = countModifierDifferences(modifierInputs, baseItem.modifierInputs);
6515
- if (differenceCount > 1) {
6516
- return void 0;
6662
+ formatAsFlat(tokens, options) {
6663
+ const groups = this.groupTokensByType(tokens);
6664
+ const tokenTypes = this.collectTokenTypesFromEntries(tokens);
6665
+ return this.buildFile(tokenTypes, options, (lines, vis) => {
6666
+ lines.push(`@Suppress("unused")`);
6667
+ lines.push(`${vis}object ${options.objectName} {`);
6668
+ this.renderFlatGroups(lines, groups, 1, options);
6669
+ lines.push("}");
6670
+ });
6517
6671
  }
6518
- const expectedSource = getExpectedSource(modifierInputs, baseItem.modifierInputs);
6519
- let tokensToInclude = filterTokensBySource(tokens, expectedSource);
6520
- const hasSourceMetadata = Object.values(tokens).some(
6521
- (token) => token != null && getSourceModifier(token) !== void 0
6522
- );
6523
- if (Object.keys(tokensToInclude).length === 0 && !hasSourceMetadata) {
6524
- tokensToInclude = tokens;
6672
+ /**
6673
+ * Shared file preamble: header, package, imports, optional ShadowToken class.
6674
+ * The `renderBody` callback appends the main object(s) to `lines`.
6675
+ */
6676
+ buildFile(tokenTypes, options, renderBody) {
6677
+ const imports = this.collectImports(tokenTypes, options);
6678
+ const vis = options.visibility ? `${options.visibility} ` : "";
6679
+ const lines = [];
6680
+ lines.push(this.buildFileHeader());
6681
+ lines.push("");
6682
+ lines.push(`package ${options.packageName}`);
6683
+ lines.push("");
6684
+ for (const imp of imports) {
6685
+ lines.push(`import ${imp}`);
6686
+ }
6687
+ if (imports.length > 0) {
6688
+ lines.push("");
6689
+ }
6690
+ if (tokenTypes.has("shadow")) {
6691
+ lines.push(...this.buildShadowTokenClass(vis, options));
6692
+ lines.push("");
6693
+ }
6694
+ renderBody(lines, vis);
6695
+ lines.push("");
6696
+ return lines.join("\n");
6525
6697
  }
6526
- if (Object.keys(tokensToInclude).length === 0) {
6527
- return void 0;
6698
+ renderFlatGroups(lines, groups, baseDepth, options) {
6699
+ const vis = options.visibility ? `${options.visibility} ` : "";
6700
+ const groupIndent = indent(options.indent, baseDepth);
6701
+ const valIndent = indent(options.indent, baseDepth + 1);
6702
+ for (const group of groups) {
6703
+ lines.push(`${groupIndent}${vis}object ${group.name} {`);
6704
+ for (const token of group.tokens) {
6705
+ const kotlinName = this.buildFlatKotlinName(token);
6706
+ const kotlinValue = this.formatKotlinValue(token, options, baseDepth + 1);
6707
+ const annotation = this.typeAnnotationSuffix(token);
6708
+ if (token.$description) {
6709
+ lines.push(`${valIndent}/** ${escapeKDoc(token.$description)} */`);
6710
+ }
6711
+ lines.push(`${valIndent}${vis}val ${kotlinName}${annotation} = ${kotlinValue}`);
6712
+ }
6713
+ lines.push(`${groupIndent}}`);
6714
+ lines.push("");
6715
+ }
6528
6716
  }
6529
- const [modifier, context] = parseModifierSource(expectedSource);
6530
- const cleanTokens = stripInternalMetadata(tokensToInclude);
6531
- const referenceTokens = stripInternalMetadata(tokens);
6532
- const selector = resolveSelector(options?.selector, modifier, context, false, modifierInputs);
6533
- const mediaQuery = resolveMediaQuery(
6534
- options?.mediaQuery,
6535
- modifier,
6536
- context,
6717
+ renderTreeChildren(lines, node, depth, options) {
6718
+ const vis = options.visibility ? `${options.visibility} ` : "";
6719
+ const pad = indent(options.indent, depth);
6720
+ const entries = Array.from(node.children.entries());
6721
+ for (let idx = 0; idx < entries.length; idx++) {
6722
+ const [key, child] = entries[idx];
6723
+ if (child.token && child.children.size === 0) {
6724
+ this.renderLeaf(lines, key, child.token, depth, options);
6725
+ } else if (child.children.size > 0 && !child.token) {
6726
+ const objectName = toPascalCase(key);
6727
+ lines.push(`${pad}${vis}object ${objectName} {`);
6728
+ this.renderTreeChildren(lines, child, depth + 1, options);
6729
+ lines.push(`${pad}}`);
6730
+ if (idx < entries.length - 1) {
6731
+ lines.push("");
6732
+ }
6733
+ } else {
6734
+ this.renderLeaf(lines, key, child.token, depth, options);
6735
+ this.renderTreeChildren(lines, child, depth, options);
6736
+ }
6737
+ }
6738
+ }
6739
+ renderLeaf(lines, key, token, depth, options) {
6740
+ const vis = options.visibility ? `${options.visibility} ` : "";
6741
+ const pad = indent(options.indent, depth);
6742
+ const kotlinName = toKotlinIdentifier(key);
6743
+ const kotlinValue = this.formatKotlinValue(token, options, depth);
6744
+ const annotation = this.typeAnnotationSuffix(token);
6745
+ if (token.$description) {
6746
+ lines.push(`${pad}/** ${escapeKDoc(token.$description)} */`);
6747
+ }
6748
+ lines.push(`${pad}${vis}val ${kotlinName}${annotation} = ${kotlinValue}`);
6749
+ }
6750
+ buildFileHeader() {
6751
+ return [
6752
+ "// Generated by Dispersa - do not edit manually",
6753
+ "// https://github.com/timges/dispersa"
6754
+ ].join("\n");
6755
+ }
6756
+ // -----------------------------------------------------------------------
6757
+ // Shadow data class
6758
+ // -----------------------------------------------------------------------
6759
+ buildShadowTokenClass(vis, options) {
6760
+ const i1 = indent(options.indent, 1);
6761
+ return [
6762
+ "@Immutable",
6763
+ `${vis}data class ShadowToken(`,
6764
+ `${i1}val color: Color,`,
6765
+ `${i1}val elevation: Dp,`,
6766
+ `${i1}val offsetX: Dp,`,
6767
+ `${i1}val offsetY: Dp,`,
6768
+ ")"
6769
+ ];
6770
+ }
6771
+ // -----------------------------------------------------------------------
6772
+ // Imports (tree-shaken)
6773
+ // -----------------------------------------------------------------------
6774
+ collectImports(tokenTypes, options) {
6775
+ const imports = /* @__PURE__ */ new Set();
6776
+ const ns = "androidx.compose";
6777
+ const hasColors = tokenTypes.has("color") || tokenTypes.has("shadow") || tokenTypes.has("border");
6778
+ if (hasColors) {
6779
+ imports.add(`${ns}.ui.graphics.Color`);
6780
+ }
6781
+ if (tokenTypes.has("dimension") || tokenTypes.has("shadow") || tokenTypes.has("border")) {
6782
+ imports.add(`${ns}.ui.unit.Dp`);
6783
+ imports.add(`${ns}.ui.unit.dp`);
6784
+ }
6785
+ if (tokenTypes.has("typography") || tokenTypes.has("fontFamily")) {
6786
+ imports.add(`${ns}.ui.text.TextStyle`);
6787
+ imports.add(`${ns}.ui.unit.sp`);
6788
+ }
6789
+ if (tokenTypes.has("typography") || tokenTypes.has("fontWeight")) {
6790
+ imports.add(`${ns}.ui.text.font.FontWeight`);
6791
+ }
6792
+ if (tokenTypes.has("fontFamily")) {
6793
+ imports.add(`${ns}.ui.text.font.FontFamily`);
6794
+ }
6795
+ if (tokenTypes.has("duration")) {
6796
+ imports.add("kotlin.time.Duration");
6797
+ imports.add("kotlin.time.Duration.Companion.milliseconds");
6798
+ imports.add("kotlin.time.Duration.Companion.seconds");
6799
+ }
6800
+ if (tokenTypes.has("cubicBezier")) {
6801
+ imports.add(`${ns}.animation.core.CubicBezierEasing`);
6802
+ }
6803
+ if (tokenTypes.has("shadow")) {
6804
+ imports.add(`${ns}.runtime.Immutable`);
6805
+ }
6806
+ if (tokenTypes.has("border")) {
6807
+ imports.add(`${ns}.foundation.BorderStroke`);
6808
+ }
6809
+ if (options.colorSpace === "displayP3" && hasColors) {
6810
+ imports.add(`${ns}.ui.graphics.colorspace.ColorSpaces`);
6811
+ }
6812
+ return Array.from(imports).sort();
6813
+ }
6814
+ collectTokenTypes(node, types) {
6815
+ if (node.token?.$type) {
6816
+ types.add(node.token.$type);
6817
+ }
6818
+ for (const child of node.children.values()) {
6819
+ this.collectTokenTypes(child, types);
6820
+ }
6821
+ }
6822
+ collectTokenTypesFromEntries(tokens) {
6823
+ const types = /* @__PURE__ */ new Set();
6824
+ for (const [, token] of Object.entries(tokens)) {
6825
+ if (token.$type) {
6826
+ types.add(token.$type);
6827
+ }
6828
+ }
6829
+ return types;
6830
+ }
6831
+ // -----------------------------------------------------------------------
6832
+ // Type annotations
6833
+ // -----------------------------------------------------------------------
6834
+ getTypeAnnotation(token) {
6835
+ switch (token.$type) {
6836
+ case "color":
6837
+ return "Color";
6838
+ case "dimension":
6839
+ return "Dp";
6840
+ case "fontFamily":
6841
+ return "FontFamily";
6842
+ case "fontWeight":
6843
+ return "FontWeight";
6844
+ case "duration":
6845
+ return "Duration";
6846
+ case "shadow":
6847
+ return "ShadowToken";
6848
+ case "cubicBezier":
6849
+ return "CubicBezierEasing";
6850
+ case "number":
6851
+ return "Double";
6852
+ case "typography":
6853
+ return "TextStyle";
6854
+ case "border":
6855
+ return "BorderStroke";
6856
+ default: {
6857
+ const value = token.$value;
6858
+ if (typeof value === "string") {
6859
+ return "String";
6860
+ }
6861
+ if (typeof value === "boolean") {
6862
+ return "Boolean";
6863
+ }
6864
+ if (typeof value === "number") {
6865
+ return "Double";
6866
+ }
6867
+ return void 0;
6868
+ }
6869
+ }
6870
+ }
6871
+ typeAnnotationSuffix(token) {
6872
+ const type = this.getTypeAnnotation(token);
6873
+ return type ? `: ${type}` : "";
6874
+ }
6875
+ // -----------------------------------------------------------------------
6876
+ // Value formatting
6877
+ // -----------------------------------------------------------------------
6878
+ formatKotlinValue(token, options, depth) {
6879
+ const value = token.$value;
6880
+ if (token.$type === "color") {
6881
+ return this.formatColorValue(value, options);
6882
+ }
6883
+ if (token.$type === "dimension") {
6884
+ return this.formatDimensionValue(value);
6885
+ }
6886
+ if (token.$type === "fontFamily") {
6887
+ return this.formatFontFamilyValue(value);
6888
+ }
6889
+ if (token.$type === "fontWeight") {
6890
+ return this.formatFontWeightValue(value);
6891
+ }
6892
+ if (token.$type === "duration") {
6893
+ return this.formatDurationValue(value);
6894
+ }
6895
+ if (token.$type === "shadow") {
6896
+ return this.formatShadowValue(value, options, depth);
6897
+ }
6898
+ if (token.$type === "typography") {
6899
+ return this.formatTypographyValue(value, options, depth);
6900
+ }
6901
+ if (token.$type === "border") {
6902
+ return this.formatBorderValue(value, options);
6903
+ }
6904
+ if (token.$type === "number") {
6905
+ return typeof value === "number" ? formatKotlinNumber(value) : String(value);
6906
+ }
6907
+ if (token.$type === "cubicBezier" && Array.isArray(value) && value.length === 4) {
6908
+ return `CubicBezierEasing(${value[0]}f, ${value[1]}f, ${value[2]}f, ${value[3]}f)`;
6909
+ }
6910
+ if (typeof value === "string") {
6911
+ return `"${escapeKotlinString(value)}"`;
6912
+ }
6913
+ if (typeof value === "number") {
6914
+ return formatKotlinNumber(value);
6915
+ }
6916
+ if (typeof value === "boolean") {
6917
+ return value ? "true" : "false";
6918
+ }
6919
+ return `"${escapeKotlinString(String(value))}"`;
6920
+ }
6921
+ formatColorValue(value, options) {
6922
+ if (!isColorObject(value)) {
6923
+ if (typeof value === "string") {
6924
+ const hex = value.replace("#", "");
6925
+ if (/^[0-9a-fA-F]{6,8}$/.test(hex)) {
6926
+ const argb = hex.length === 8 ? hex : `FF${hex}`;
6927
+ return `Color(0x${argb.toUpperCase()})`;
6928
+ }
6929
+ }
6930
+ return "Color.Unspecified";
6931
+ }
6932
+ const colorObj = value;
6933
+ const alpha = colorObj.alpha ?? 1;
6934
+ if (options.colorFormat === "argb_float" || options.colorSpace === "displayP3") {
6935
+ return this.formatFloatColor(colorObj, alpha, options);
6936
+ }
6937
+ return this.formatHexColor(colorObj, alpha);
6938
+ }
6939
+ formatFloatColor(colorObj, alpha, options) {
6940
+ if (options.colorSpace === "displayP3") {
6941
+ const p3 = toP3(dtcgObjectToCulori(colorObj));
6942
+ const r2 = roundComponent(p3?.r ?? 0);
6943
+ const g2 = roundComponent(p3?.g ?? 0);
6944
+ const b2 = roundComponent(p3?.b ?? 0);
6945
+ return `Color(${r2}f, ${g2}f, ${b2}f, ${roundComponent(alpha)}f, ColorSpaces.DisplayP3)`;
6946
+ }
6947
+ const rgb = toSRGB(dtcgObjectToCulori(colorObj));
6948
+ const r = roundComponent(rgb?.r ?? 0);
6949
+ const g = roundComponent(rgb?.g ?? 0);
6950
+ const b = roundComponent(rgb?.b ?? 0);
6951
+ return `Color(${r}f, ${g}f, ${b}f, ${roundComponent(alpha)}f)`;
6952
+ }
6953
+ formatHexColor(colorObj, alpha) {
6954
+ const hex = colorObjectToHex(colorObj);
6955
+ const hexClean = hex.replace("#", "");
6956
+ if (hexClean.length === 8) {
6957
+ const rrggbb = hexClean.slice(0, 6);
6958
+ const aa = hexClean.slice(6, 8);
6959
+ return `Color(0x${aa.toUpperCase()}${rrggbb.toUpperCase()})`;
6960
+ }
6961
+ const alphaHex = alpha < 1 ? Math.round(alpha * 255).toString(16).padStart(2, "0").toUpperCase() : "FF";
6962
+ return `Color(0x${alphaHex}${hexClean.toUpperCase()})`;
6963
+ }
6964
+ formatDimensionValue(value) {
6965
+ if (isDimensionObject(value)) {
6966
+ const dim = value;
6967
+ const dpValue = dim.unit === "rem" ? dim.value * 16 : dim.value;
6968
+ return `${dpValue}.dp`;
6969
+ }
6970
+ return typeof value === "number" ? `${value}.dp` : `0.dp`;
6971
+ }
6972
+ formatFontFamilyValue(value) {
6973
+ if (Array.isArray(value)) {
6974
+ const primary = value[0];
6975
+ if (typeof primary === "string") {
6976
+ return this.mapKotlinFontFamily(primary);
6977
+ }
6978
+ return "FontFamily.Default";
6979
+ }
6980
+ return typeof value === "string" ? this.mapKotlinFontFamily(value) : "FontFamily.Default";
6981
+ }
6982
+ mapKotlinFontFamily(family) {
6983
+ const normalized = family.toLowerCase().replace(/['"]/g, "").trim();
6984
+ const builtIn = {
6985
+ "sans-serif": "FontFamily.SansSerif",
6986
+ serif: "FontFamily.Serif",
6987
+ monospace: "FontFamily.Monospace",
6988
+ cursive: "FontFamily.Cursive"
6989
+ };
6990
+ return builtIn[normalized] ?? `FontFamily.Default // TODO: load "${family}" via Font(R.font.${toResourceName(family)})`;
6991
+ }
6992
+ formatFontWeightValue(value) {
6993
+ if (typeof value === "number") {
6994
+ return this.numericFontWeight(value);
6995
+ }
6996
+ if (typeof value === "string") {
6997
+ return this.namedFontWeight(value) ?? "FontWeight.Normal";
6998
+ }
6999
+ return "FontWeight.Normal";
7000
+ }
7001
+ numericFontWeight(weight) {
7002
+ if (weight <= 100) {
7003
+ return "FontWeight.Thin";
7004
+ }
7005
+ if (weight <= 200) {
7006
+ return "FontWeight.ExtraLight";
7007
+ }
7008
+ if (weight <= 300) {
7009
+ return "FontWeight.Light";
7010
+ }
7011
+ if (weight <= 400) {
7012
+ return "FontWeight.Normal";
7013
+ }
7014
+ if (weight <= 500) {
7015
+ return "FontWeight.Medium";
7016
+ }
7017
+ if (weight <= 600) {
7018
+ return "FontWeight.SemiBold";
7019
+ }
7020
+ if (weight <= 700) {
7021
+ return "FontWeight.Bold";
7022
+ }
7023
+ if (weight <= 800) {
7024
+ return "FontWeight.ExtraBold";
7025
+ }
7026
+ return "FontWeight.Black";
7027
+ }
7028
+ namedFontWeight(name) {
7029
+ const map = {
7030
+ thin: "FontWeight.Thin",
7031
+ extralight: "FontWeight.ExtraLight",
7032
+ ultralight: "FontWeight.ExtraLight",
7033
+ light: "FontWeight.Light",
7034
+ regular: "FontWeight.Normal",
7035
+ normal: "FontWeight.Normal",
7036
+ medium: "FontWeight.Medium",
7037
+ semibold: "FontWeight.SemiBold",
7038
+ demibold: "FontWeight.SemiBold",
7039
+ bold: "FontWeight.Bold",
7040
+ extrabold: "FontWeight.ExtraBold",
7041
+ heavy: "FontWeight.ExtraBold",
7042
+ black: "FontWeight.Black",
7043
+ ultrabold: "FontWeight.Black"
7044
+ };
7045
+ return map[name.toLowerCase()];
7046
+ }
7047
+ formatDurationValue(value) {
7048
+ if (typeof value === "object" && value !== null && "value" in value && "unit" in value) {
7049
+ const dur = value;
7050
+ return dur.unit === "ms" ? `${dur.value}.milliseconds` : `${dur.value}.seconds`;
7051
+ }
7052
+ return typeof value === "number" ? `${value}.milliseconds` : "0.milliseconds";
7053
+ }
7054
+ formatShadowValue(value, options, depth) {
7055
+ if (Array.isArray(value) && value.length > 0) {
7056
+ return this.formatSingleShadow(value[0], options, depth);
7057
+ }
7058
+ if (typeof value === "object" && value !== null) {
7059
+ return this.formatSingleShadow(value, options, depth);
7060
+ }
7061
+ return "ShadowToken(color = Color.Unspecified, elevation = 0.dp, offsetX = 0.dp, offsetY = 0.dp)";
7062
+ }
7063
+ formatSingleShadow(shadow, options, depth) {
7064
+ const color = isColorObject(shadow.color) ? this.formatColorValue(shadow.color, options) : "Color.Black";
7065
+ const elevation = isDimensionObject(shadow.blur) ? this.formatDimensionValue(shadow.blur) : "0.dp";
7066
+ const offsetX = isDimensionObject(shadow.offsetX) ? this.formatDimensionValue(shadow.offsetX) : "0.dp";
7067
+ const offsetY = isDimensionObject(shadow.offsetY) ? this.formatDimensionValue(shadow.offsetY) : "0.dp";
7068
+ const propIndent = indent(options.indent, depth + 1);
7069
+ const closeIndent = indent(options.indent, depth);
7070
+ return [
7071
+ "ShadowToken(",
7072
+ `${propIndent}color = ${color},`,
7073
+ `${propIndent}elevation = ${elevation},`,
7074
+ `${propIndent}offsetX = ${offsetX},`,
7075
+ `${propIndent}offsetY = ${offsetY},`,
7076
+ `${closeIndent})`
7077
+ ].join("\n");
7078
+ }
7079
+ formatBorderValue(value, options) {
7080
+ if (typeof value !== "object" || value === null) {
7081
+ return "BorderStroke(0.dp, Color.Unspecified)";
7082
+ }
7083
+ const border = value;
7084
+ const width = isDimensionObject(border.width) ? this.formatDimensionValue(border.width) : "0.dp";
7085
+ const color = isColorObject(border.color) ? this.formatColorValue(border.color, options) : "Color.Unspecified";
7086
+ return `BorderStroke(${width}, ${color})`;
7087
+ }
7088
+ formatTypographyValue(value, options, depth) {
7089
+ if (typeof value !== "object" || value === null) {
7090
+ return "TextStyle()";
7091
+ }
7092
+ const typo = value;
7093
+ const parts = [];
7094
+ if (isDimensionObject(typo.fontSize)) {
7095
+ const dim = typo.fontSize;
7096
+ const spValue = dim.unit === "rem" ? dim.value * 16 : dim.value;
7097
+ parts.push(`fontSize = ${spValue}.sp`);
7098
+ }
7099
+ if (typo.fontWeight != null) {
7100
+ parts.push(`fontWeight = ${this.formatFontWeightValue(typo.fontWeight)}`);
7101
+ }
7102
+ if (typo.lineHeight != null && typeof typo.lineHeight === "number") {
7103
+ if (isDimensionObject(typo.fontSize)) {
7104
+ const dim = typo.fontSize;
7105
+ const spValue = dim.unit === "rem" ? dim.value * 16 : dim.value;
7106
+ const lineHeightSp = Math.round(spValue * typo.lineHeight * 100) / 100;
7107
+ parts.push(`lineHeight = ${lineHeightSp}.sp`);
7108
+ }
7109
+ }
7110
+ if (isDimensionObject(typo.letterSpacing)) {
7111
+ const dim = typo.letterSpacing;
7112
+ const spValue = dim.unit === "rem" ? dim.value * 16 : dim.value;
7113
+ parts.push(`letterSpacing = ${spValue}.sp`);
7114
+ }
7115
+ if (parts.length === 0) {
7116
+ return "TextStyle()";
7117
+ }
7118
+ const propIndent = indent(options.indent, depth + 1);
7119
+ const closeIndent = indent(options.indent, depth);
7120
+ return `TextStyle(
7121
+ ${parts.map((p) => `${propIndent}${p}`).join(",\n")},
7122
+ ${closeIndent})`;
7123
+ }
7124
+ // -----------------------------------------------------------------------
7125
+ // Output: standalone
7126
+ // -----------------------------------------------------------------------
7127
+ async formatStandalone(context, options) {
7128
+ const requiresFile = context.buildPath !== void 0 && context.buildPath !== "";
7129
+ if (!context.output.file && requiresFile) {
7130
+ throw new ConfigurationError(
7131
+ `Output "${context.output.name}": file is required for standalone Android output`
7132
+ );
7133
+ }
7134
+ const files = {};
7135
+ for (const { tokens, modifierInputs } of context.permutations) {
7136
+ const processedTokens = stripInternalMetadata(tokens);
7137
+ const content = this.formatTokens(processedTokens, options);
7138
+ const fileName = context.output.file ? resolveFileName(context.output.file, modifierInputs) : buildInMemoryOutputKey({
7139
+ outputName: context.output.name,
7140
+ extension: "kt",
7141
+ modifierInputs,
7142
+ resolver: context.resolver,
7143
+ defaults: context.meta.defaults
7144
+ });
7145
+ files[fileName] = content;
7146
+ }
7147
+ return outputTree(files);
7148
+ }
7149
+ // -----------------------------------------------------------------------
7150
+ // Output: bundle
7151
+ // -----------------------------------------------------------------------
7152
+ async formatBundle(context, options) {
7153
+ const requiresFile = context.buildPath !== void 0 && context.buildPath !== "";
7154
+ if (!context.output.file && requiresFile) {
7155
+ throw new ConfigurationError(
7156
+ `Output "${context.output.name}": file is required for bundle Android output`
7157
+ );
7158
+ }
7159
+ const content = this.formatBundleContent(context, options);
7160
+ const fileName = context.output.file ? resolveFileName(context.output.file, context.meta.basePermutation) : buildInMemoryOutputKey({
7161
+ outputName: context.output.name,
7162
+ extension: "kt",
7163
+ modifierInputs: context.meta.basePermutation,
7164
+ resolver: context.resolver,
7165
+ defaults: context.meta.defaults
7166
+ });
7167
+ return outputTree({ [fileName]: content });
7168
+ }
7169
+ formatBundleContent(context, options) {
7170
+ const allTokenTypes = this.collectAllPermutationTypes(context);
7171
+ return this.buildFile(allTokenTypes, options, (lines, vis) => {
7172
+ const i1 = indent(options.indent, 1);
7173
+ lines.push(`@Suppress("unused")`);
7174
+ lines.push(`${vis}object ${options.objectName} {`);
7175
+ for (let idx = 0; idx < context.permutations.length; idx++) {
7176
+ const { tokens, modifierInputs } = context.permutations[idx];
7177
+ const processedTokens = stripInternalMetadata(tokens);
7178
+ const permName = this.buildPermutationName(modifierInputs);
7179
+ lines.push(`${i1}${vis}object ${permName} {`);
7180
+ this.renderBundleTokens(lines, processedTokens, options, 2);
7181
+ lines.push(`${i1}}`);
7182
+ if (idx < context.permutations.length - 1) {
7183
+ lines.push("");
7184
+ }
7185
+ }
7186
+ lines.push("}");
7187
+ });
7188
+ }
7189
+ collectAllPermutationTypes(context) {
7190
+ const allTokenTypes = /* @__PURE__ */ new Set();
7191
+ for (const { tokens } of context.permutations) {
7192
+ const processed = stripInternalMetadata(tokens);
7193
+ for (const [, token] of Object.entries(processed)) {
7194
+ if (token.$type) {
7195
+ allTokenTypes.add(token.$type);
7196
+ }
7197
+ }
7198
+ }
7199
+ return allTokenTypes;
7200
+ }
7201
+ renderBundleTokens(lines, tokens, options, baseDepth) {
7202
+ if (options.structure === "flat") {
7203
+ const groups = this.groupTokensByType(tokens);
7204
+ this.renderFlatGroups(lines, groups, baseDepth, options);
7205
+ return;
7206
+ }
7207
+ const tree = this.buildTokenTree(tokens);
7208
+ this.renderTreeChildren(lines, tree, baseDepth, options);
7209
+ }
7210
+ buildPermutationName(modifierInputs) {
7211
+ const values = Object.values(modifierInputs);
7212
+ if (values.length === 0) {
7213
+ return "Default";
7214
+ }
7215
+ return values.map((v) => toPascalCase(v)).join("");
7216
+ }
7217
+ };
7218
+ function androidRenderer() {
7219
+ const rendererInstance = new AndroidRenderer();
7220
+ return {
7221
+ format: (context, options) => rendererInstance.format(
7222
+ context,
7223
+ options ?? context.output.options
7224
+ )
7225
+ };
7226
+ }
7227
+
7228
+ // src/renderers/css.ts
7229
+ init_errors();
7230
+ init_token_utils();
7231
+
7232
+ // src/renderers/bundlers/css.ts
7233
+ init_errors();
7234
+ init_utils();
7235
+ var getSourceSet = (token) => {
7236
+ if (typeof token !== "object" || token === null) {
7237
+ return void 0;
7238
+ }
7239
+ const maybe = token;
7240
+ return typeof maybe._sourceSet === "string" ? maybe._sourceSet : void 0;
7241
+ };
7242
+ var getSourceModifier = (token) => {
7243
+ if (typeof token !== "object" || token === null) {
7244
+ return void 0;
7245
+ }
7246
+ const maybe = token;
7247
+ return typeof maybe._sourceModifier === "string" ? maybe._sourceModifier : void 0;
7248
+ };
7249
+ async function bundleAsCss(bundleData, resolver, options, formatTokens) {
7250
+ const baseItem = bundleData.find((item) => item.isBase);
7251
+ if (!baseItem) {
7252
+ throw new BasePermutationError("Base permutation not found in bundle data");
7253
+ }
7254
+ if (!formatTokens) {
7255
+ throw new ConfigurationError("CSS formatter was not provided");
7256
+ }
7257
+ const orderedBundleData = orderBundleData(bundleData, resolver, baseItem);
7258
+ const cssBlocks = [];
7259
+ for (const item of orderedBundleData) {
7260
+ if (item.isBase) {
7261
+ const blocks = await formatBasePermutation(item, resolver, options, formatTokens);
7262
+ cssBlocks.push(...blocks);
7263
+ continue;
7264
+ }
7265
+ const block = await formatModifierPermutation(item, baseItem, options, formatTokens);
7266
+ if (block) {
7267
+ cssBlocks.push(block);
7268
+ }
7269
+ }
7270
+ return cssBlocks.join("\n\n");
7271
+ }
7272
+ async function formatBasePermutation({ tokens, modifierInputs }, resolver, options, formatTokens) {
7273
+ const firstModifierName = resolver.modifiers ? Object.keys(resolver.modifiers)[0] : "";
7274
+ const modifier = firstModifierName ?? "";
7275
+ const context = modifierInputs[modifier] ?? "";
7276
+ const selector = resolveSelector(options?.selector, modifier, context, true, modifierInputs);
7277
+ const mediaQuery = resolveMediaQuery(options?.mediaQuery, modifier, context, true, modifierInputs);
7278
+ const referenceTokens = stripInternalMetadata(tokens);
7279
+ const defaultBlocks = buildDefaultLayerBlocks(tokens, modifierInputs, resolver);
7280
+ const cssBlocks = [];
7281
+ for (const block of defaultBlocks) {
7282
+ const cleanTokens = stripInternalMetadata(block.tokens);
7283
+ const css2 = await formatTokens(cleanTokens, {
7284
+ selector,
7285
+ mediaQuery,
7286
+ minify: options?.minify,
7287
+ referenceTokens
7288
+ });
7289
+ const header = block.description ? `/* ${block.key} */
7290
+ /* ${block.description} */` : `/* ${block.key} */`;
7291
+ cssBlocks.push(`${header}
7292
+ ${css2}`);
7293
+ }
7294
+ return cssBlocks;
7295
+ }
7296
+ async function formatModifierPermutation({ tokens, modifierInputs }, baseItem, options, formatTokens) {
7297
+ const differenceCount = countModifierDifferences(modifierInputs, baseItem.modifierInputs);
7298
+ if (differenceCount > 1) {
7299
+ return void 0;
7300
+ }
7301
+ const expectedSource = getExpectedSource(modifierInputs, baseItem.modifierInputs);
7302
+ let tokensToInclude = filterTokensBySource(tokens, expectedSource);
7303
+ const hasSourceMetadata = Object.values(tokens).some(
7304
+ (token) => token != null && getSourceModifier(token) !== void 0
7305
+ );
7306
+ if (Object.keys(tokensToInclude).length === 0 && !hasSourceMetadata) {
7307
+ tokensToInclude = tokens;
7308
+ }
7309
+ if (Object.keys(tokensToInclude).length === 0) {
7310
+ return void 0;
7311
+ }
7312
+ const [modifier, context] = parseModifierSource(expectedSource);
7313
+ const cleanTokens = stripInternalMetadata(tokensToInclude);
7314
+ const referenceTokens = stripInternalMetadata(tokens);
7315
+ const selector = resolveSelector(options?.selector, modifier, context, false, modifierInputs);
7316
+ const mediaQuery = resolveMediaQuery(
7317
+ options?.mediaQuery,
7318
+ modifier,
7319
+ context,
6537
7320
  false,
6538
7321
  modifierInputs
6539
7322
  );
@@ -6573,6 +7356,33 @@ function collectRemainder(tokens, included) {
6573
7356
  }
6574
7357
  return result;
6575
7358
  }
7359
+ function buildSetLayerBlocks(tokens, resolver) {
7360
+ const blocks = [];
7361
+ const included = /* @__PURE__ */ new Set();
7362
+ const addBlock = (key, blockTokens, description) => {
7363
+ if (Object.keys(blockTokens).length === 0) {
7364
+ return;
7365
+ }
7366
+ for (const k of Object.keys(blockTokens)) {
7367
+ included.add(k);
7368
+ }
7369
+ blocks.push({ key, description, tokens: blockTokens });
7370
+ };
7371
+ for (const item of resolver.resolutionOrder) {
7372
+ const ref = item.$ref;
7373
+ if (typeof ref !== "string" || !ref.startsWith("#/sets/")) {
7374
+ continue;
7375
+ }
7376
+ const setName = ref.slice("#/sets/".length);
7377
+ addBlock(
7378
+ `Set: ${setName}`,
7379
+ collectSetTokens(tokens, setName, included),
7380
+ resolver.sets?.[setName]?.description
7381
+ );
7382
+ }
7383
+ addBlock("Unattributed", collectRemainder(tokens, included));
7384
+ return blocks;
7385
+ }
6576
7386
  function buildDefaultLayerBlocks(tokens, baseModifierInputs, resolver) {
6577
7387
  const blocks = [];
6578
7388
  const included = /* @__PURE__ */ new Set();
@@ -6768,14 +7578,14 @@ var CssRenderer = class _CssRenderer {
6768
7578
  return opts.minify ? cssString : await this.formatWithPrettier(cssString);
6769
7579
  }
6770
7580
  buildCssBlock(lines, groupTokens, selector, tokens, referenceTokens, opts) {
6771
- const indent = opts.minify ? "" : " ";
7581
+ const indent2 = opts.minify ? "" : " ";
6772
7582
  const newline = opts.minify ? "" : "\n";
6773
7583
  const space = opts.minify ? "" : " ";
6774
7584
  const hasMediaQuery = opts.mediaQuery != null && opts.mediaQuery !== "";
6775
- const tokenIndent = hasMediaQuery ? indent + indent : indent;
7585
+ const tokenIndent = hasMediaQuery ? indent2 + indent2 : indent2;
6776
7586
  if (hasMediaQuery) {
6777
7587
  lines.push(`@media ${opts.mediaQuery}${space}{${newline}`);
6778
- lines.push(`${indent}${selector}${space}{${newline}`);
7588
+ lines.push(`${indent2}${selector}${space}{${newline}`);
6779
7589
  } else {
6780
7590
  lines.push(`${selector}${space}{${newline}`);
6781
7591
  }
@@ -6792,21 +7602,21 @@ var CssRenderer = class _CssRenderer {
6792
7602
  );
6793
7603
  }
6794
7604
  if (hasMediaQuery) {
6795
- lines.push(`${indent}}${newline}`);
7605
+ lines.push(`${indent2}}${newline}`);
6796
7606
  }
6797
7607
  lines.push(`}${newline}${newline}`);
6798
7608
  }
6799
- pushTokenLines(lines, token, tokens, referenceTokens, preserveReferences, indent, newline, space) {
7609
+ pushTokenLines(lines, token, tokens, referenceTokens, preserveReferences, indent2, newline, space) {
6800
7610
  const entries = this.buildCssEntries(token, tokens, referenceTokens, preserveReferences);
6801
7611
  if (token.$deprecated != null && token.$deprecated !== false) {
6802
7612
  const deprecationMsg = formatDeprecationMessage(token, "", "comment");
6803
- lines.push(`${indent}/* ${this.sanitizeCssCommentText(deprecationMsg)} */${newline}`);
7613
+ lines.push(`${indent2}/* ${this.sanitizeCssCommentText(deprecationMsg)} */${newline}`);
6804
7614
  }
6805
7615
  if (token.$description && token.$description !== "") {
6806
- lines.push(`${indent}/* ${this.sanitizeCssCommentText(token.$description)} */${newline}`);
7616
+ lines.push(`${indent2}/* ${this.sanitizeCssCommentText(token.$description)} */${newline}`);
6807
7617
  }
6808
7618
  for (const entry of entries) {
6809
- lines.push(`${indent}--${entry.name}:${space}${entry.value};${newline}`);
7619
+ lines.push(`${indent2}--${entry.name}:${space}${entry.value};${newline}`);
6810
7620
  }
6811
7621
  }
6812
7622
  async formatWithPrettier(css2) {
@@ -7293,6 +8103,10 @@ var CssRenderer = class _CssRenderer {
7293
8103
  throw new ConfigurationError("Modifier preset requires modifiers to be defined in resolver");
7294
8104
  }
7295
8105
  const files = {};
8106
+ const baseResult = await this.buildModifierBaseFile(context, options);
8107
+ if (baseResult) {
8108
+ files[baseResult.fileName] = baseResult.content;
8109
+ }
7296
8110
  for (const [modifierName, modifierDef] of Object.entries(context.resolver.modifiers)) {
7297
8111
  for (const contextValue of Object.keys(modifierDef.contexts)) {
7298
8112
  const result = await this.buildModifierContextFile(
@@ -7308,6 +8122,59 @@ var CssRenderer = class _CssRenderer {
7308
8122
  }
7309
8123
  return { kind: "outputTree", files };
7310
8124
  }
8125
+ async buildModifierBaseFile(context, options) {
8126
+ const basePermutation = context.permutations.find(
8127
+ ({ modifierInputs }) => this.isBasePermutation(modifierInputs, context.meta.defaults)
8128
+ );
8129
+ if (!basePermutation) {
8130
+ return void 0;
8131
+ }
8132
+ const setTokens = filterTokensFromSets(basePermutation.tokens);
8133
+ if (Object.keys(setTokens).length === 0) {
8134
+ return void 0;
8135
+ }
8136
+ const setBlocks = buildSetLayerBlocks(setTokens, context.resolver);
8137
+ if (setBlocks.length === 0) {
8138
+ return void 0;
8139
+ }
8140
+ const modifiers = context.resolver.modifiers;
8141
+ const firstModifierName = Object.keys(modifiers)[0] ?? "";
8142
+ const firstModifierContext = context.meta.defaults[firstModifierName] ?? "";
8143
+ const baseModifierInputs = { ...context.meta.defaults };
8144
+ const selector = resolveSelector(
8145
+ options.selector,
8146
+ firstModifierName,
8147
+ firstModifierContext,
8148
+ true,
8149
+ baseModifierInputs
8150
+ );
8151
+ const mediaQuery = resolveMediaQuery(
8152
+ options.mediaQuery,
8153
+ firstModifierName,
8154
+ firstModifierContext,
8155
+ true,
8156
+ baseModifierInputs
8157
+ );
8158
+ const referenceTokens = basePermutation.tokens;
8159
+ const cssBlocks = [];
8160
+ for (const block of setBlocks) {
8161
+ const cleanTokens = stripInternalMetadata(block.tokens);
8162
+ const css2 = await this.formatTokens(cleanTokens, {
8163
+ selector,
8164
+ mediaQuery,
8165
+ minify: options.minify ?? false,
8166
+ preserveReferences: options.preserveReferences ?? false,
8167
+ referenceTokens
8168
+ });
8169
+ const header = block.description ? `/* ${block.key} */
8170
+ /* ${block.description} */` : `/* ${block.key} */`;
8171
+ cssBlocks.push(`${header}
8172
+ ${css2}`);
8173
+ }
8174
+ const content = cssBlocks.join("\n");
8175
+ const fileName = context.output.file ? resolveBaseFileName(context.output.file, context.meta.defaults) : `${context.output.name}-base.css`;
8176
+ return { fileName, content };
8177
+ }
7311
8178
  collectTokensForModifierContext(modifierName, contextValue, permutations) {
7312
8179
  const expectedSource = `${modifierName}-${contextValue}`;
7313
8180
  let tokensFromSource = {};
@@ -7354,7 +8221,7 @@ var CssRenderer = class _CssRenderer {
7354
8221
  preserveReferences: options.preserveReferences ?? false,
7355
8222
  referenceTokens
7356
8223
  });
7357
- const fileName = context.output.file ? resolveFileName(context.output.file, modifierInputs, modifierName, contextValue) : buildInMemoryOutputKey({
8224
+ const fileName = context.output.file ? resolveFileName(context.output.file, modifierInputs) : buildInMemoryOutputKey({
7358
8225
  outputName: context.output.name,
7359
8226
  extension: "css",
7360
8227
  modifierInputs,
@@ -7381,18 +8248,641 @@ var CssRenderer = class _CssRenderer {
7381
8248
  return { modifierName: name, modifierContext: value };
7382
8249
  }
7383
8250
  }
7384
- return { modifierName: "", modifierContext: "" };
7385
- }
7386
- isBasePermutation(modifierInputs, defaults) {
7387
- const normalizedInputs = normalizeModifierInputs(modifierInputs);
7388
- const normalizedDefaults = normalizeModifierInputs(defaults);
7389
- return Object.entries(normalizedDefaults).every(
7390
- ([key, value]) => normalizedInputs[key] === value
7391
- );
8251
+ return { modifierName: "", modifierContext: "" };
8252
+ }
8253
+ isBasePermutation(modifierInputs, defaults) {
8254
+ const normalizedInputs = normalizeModifierInputs(modifierInputs);
8255
+ const normalizedDefaults = normalizeModifierInputs(defaults);
8256
+ return Object.entries(normalizedDefaults).every(
8257
+ ([key, value]) => normalizedInputs[key] === value
8258
+ );
8259
+ }
8260
+ };
8261
+ function cssRenderer() {
8262
+ const rendererInstance = new CssRenderer();
8263
+ return {
8264
+ format: (context, options) => rendererInstance.format(
8265
+ context,
8266
+ options ?? context.output.options
8267
+ )
8268
+ };
8269
+ }
8270
+
8271
+ // src/renderers/ios.ts
8272
+ init_errors();
8273
+ init_token_utils();
8274
+ init_utils();
8275
+ var toSRGB2 = converter("rgb");
8276
+ var toP32 = converter("p3");
8277
+ var SWIFT_TYPE_GROUP_MAP = {
8278
+ color: "Colors",
8279
+ dimension: "Spacing",
8280
+ fontFamily: "Fonts",
8281
+ fontWeight: "FontWeights",
8282
+ duration: "Durations",
8283
+ shadow: "Shadows",
8284
+ typography: "Typography",
8285
+ number: "Numbers",
8286
+ cubicBezier: "Animations",
8287
+ border: "Borders",
8288
+ gradient: "Gradients"
8289
+ };
8290
+ var SWIFT_KEYWORDS = /* @__PURE__ */ new Set([
8291
+ "associatedtype",
8292
+ "class",
8293
+ "deinit",
8294
+ "enum",
8295
+ "extension",
8296
+ "fileprivate",
8297
+ "func",
8298
+ "import",
8299
+ "init",
8300
+ "inout",
8301
+ "internal",
8302
+ "let",
8303
+ "open",
8304
+ "operator",
8305
+ "private",
8306
+ "protocol",
8307
+ "public",
8308
+ "rethrows",
8309
+ "static",
8310
+ "struct",
8311
+ "subscript",
8312
+ "typealias",
8313
+ "var",
8314
+ "break",
8315
+ "case",
8316
+ "continue",
8317
+ "default",
8318
+ "defer",
8319
+ "do",
8320
+ "else",
8321
+ "fallthrough",
8322
+ "for",
8323
+ "guard",
8324
+ "if",
8325
+ "in",
8326
+ "repeat",
8327
+ "return",
8328
+ "switch",
8329
+ "where",
8330
+ "while",
8331
+ "as",
8332
+ "catch",
8333
+ "false",
8334
+ "is",
8335
+ "nil",
8336
+ "super",
8337
+ "self",
8338
+ "Self",
8339
+ "throw",
8340
+ "throws",
8341
+ "true",
8342
+ "try",
8343
+ "Type",
8344
+ "Protocol"
8345
+ ]);
8346
+ var IosRenderer = class {
8347
+ async format(context, options) {
8348
+ const opts = {
8349
+ preset: options?.preset ?? "standalone",
8350
+ accessLevel: options?.accessLevel ?? "public",
8351
+ structure: options?.structure ?? "enum",
8352
+ enumName: options?.enumName ?? "DesignTokens",
8353
+ extensionNamespace: options?.extensionNamespace ?? "DesignTokens",
8354
+ colorSpace: options?.colorSpace ?? "sRGB",
8355
+ swiftVersion: options?.swiftVersion ?? "5.9",
8356
+ indent: options?.indent ?? 4,
8357
+ frozen: options?.frozen ?? false
8358
+ };
8359
+ return await this.formatStandalone(context, opts);
8360
+ }
8361
+ formatTokens(tokens, options) {
8362
+ if (options.structure === "grouped") {
8363
+ return this.formatAsGrouped(tokens, options);
8364
+ }
8365
+ return this.formatAsEnum(tokens, options);
8366
+ }
8367
+ formatAsEnum(tokens, options) {
8368
+ const access3 = options.accessLevel;
8369
+ const groups = this.groupTokensByType(tokens);
8370
+ const imports = this.collectImports(tokens);
8371
+ const i1 = this.indentStr(options.indent, 1);
8372
+ const i2 = this.indentStr(options.indent, 2);
8373
+ const staticPrefix = this.staticLetPrefix(options);
8374
+ const frozen = this.frozenPrefix(options);
8375
+ const lines = [];
8376
+ lines.push(this.buildFileHeader());
8377
+ lines.push("");
8378
+ for (const imp of imports) {
8379
+ lines.push(`import ${imp}`);
8380
+ }
8381
+ lines.push(...this.buildStructDefinitions(tokens, access3, options));
8382
+ lines.push("");
8383
+ lines.push(`${frozen}${access3} enum ${options.enumName} {`);
8384
+ for (const group of groups) {
8385
+ lines.push(`${i1}${frozen}${access3} enum ${group.name} {`);
8386
+ for (const token of group.tokens) {
8387
+ const swiftName = this.buildQualifiedSwiftName(token);
8388
+ const swiftValue = this.formatSwiftValue(token, options);
8389
+ const typeAnnotation = this.getTypeAnnotation(token);
8390
+ const annotation = typeAnnotation ? `: ${typeAnnotation}` : "";
8391
+ const docComment = this.buildDocComment(token, i2);
8392
+ if (docComment) {
8393
+ lines.push(docComment);
8394
+ }
8395
+ lines.push(`${i2}${access3} ${staticPrefix}${swiftName}${annotation} = ${swiftValue}`);
8396
+ }
8397
+ lines.push(`${i1}}`);
8398
+ lines.push("");
8399
+ }
8400
+ lines.push("}");
8401
+ lines.push(...this.buildViewExtensions(tokens, access3, options));
8402
+ lines.push("");
8403
+ return lines.join("\n");
8404
+ }
8405
+ formatAsGrouped(tokens, options) {
8406
+ const access3 = options.accessLevel;
8407
+ const namespace = options.extensionNamespace;
8408
+ const groups = this.groupTokensByType(tokens);
8409
+ const imports = this.collectImports(tokens);
8410
+ const i1 = this.indentStr(options.indent, 1);
8411
+ const i2 = this.indentStr(options.indent, 2);
8412
+ const staticPrefix = this.staticLetPrefix(options);
8413
+ const frozen = this.frozenPrefix(options);
8414
+ const lines = [];
8415
+ lines.push(this.buildFileHeader());
8416
+ lines.push("");
8417
+ for (const imp of imports) {
8418
+ lines.push(`import ${imp}`);
8419
+ }
8420
+ lines.push(...this.buildStructDefinitions(tokens, access3, options));
8421
+ lines.push("");
8422
+ lines.push(`${frozen}${access3} enum ${namespace} {}`);
8423
+ lines.push("");
8424
+ for (const group of groups) {
8425
+ lines.push(`${access3} extension ${namespace} {`);
8426
+ lines.push(`${i1}${frozen}enum ${group.name} {`);
8427
+ for (const token of group.tokens) {
8428
+ const swiftName = this.buildQualifiedSwiftName(token);
8429
+ const swiftValue = this.formatSwiftValue(token, options);
8430
+ const typeAnnotation = this.getTypeAnnotation(token);
8431
+ const annotation = typeAnnotation ? `: ${typeAnnotation}` : "";
8432
+ const docComment = this.buildDocComment(token, i2);
8433
+ if (docComment) {
8434
+ lines.push(docComment);
8435
+ }
8436
+ lines.push(`${i2}${access3} ${staticPrefix}${swiftName}${annotation} = ${swiftValue}`);
8437
+ }
8438
+ lines.push(`${i1}}`);
8439
+ lines.push("}");
8440
+ lines.push("");
8441
+ }
8442
+ lines.push(...this.buildViewExtensions(tokens, access3, options));
8443
+ return lines.join("\n");
8444
+ }
8445
+ buildFileHeader() {
8446
+ return [
8447
+ "// Generated by Dispersa - do not edit manually",
8448
+ "// https://github.com/timges/dispersa"
8449
+ ].join("\n");
8450
+ }
8451
+ collectImports(tokens) {
8452
+ const imports = /* @__PURE__ */ new Set();
8453
+ imports.add("SwiftUI");
8454
+ for (const [, token] of Object.entries(tokens)) {
8455
+ if (token.$type === "duration") {
8456
+ imports.add("Foundation");
8457
+ }
8458
+ }
8459
+ return Array.from(imports).sort();
8460
+ }
8461
+ /**
8462
+ * Builds a `///` doc comment from a token's `$description`, if present.
8463
+ */
8464
+ buildDocComment(token, indent2) {
8465
+ if (!token.$description) {
8466
+ return void 0;
8467
+ }
8468
+ return `${indent2}/// ${token.$description}`;
8469
+ }
8470
+ groupTokensByType(tokens) {
8471
+ const groupMap = /* @__PURE__ */ new Map();
8472
+ for (const [, token] of getSortedTokenEntries(tokens)) {
8473
+ const groupName = SWIFT_TYPE_GROUP_MAP[token.$type ?? ""] ?? "Other";
8474
+ const existing = groupMap.get(groupName) ?? [];
8475
+ existing.push(token);
8476
+ groupMap.set(groupName, existing);
8477
+ }
8478
+ return Array.from(groupMap.entries()).map(([name, groupTokens]) => ({
8479
+ name,
8480
+ tokens: groupTokens
8481
+ }));
8482
+ }
8483
+ /**
8484
+ * Builds a qualified Swift name from a token's path, preserving parent
8485
+ * hierarchy segments to avoid duplicate identifiers.
8486
+ *
8487
+ * For example, `color.blue.400` in the `Colors` group becomes `blue400`
8488
+ * instead of just `_400`.
8489
+ */
8490
+ buildQualifiedSwiftName(token) {
8491
+ const path7 = token.path;
8492
+ const withoutTypePrefix = path7.length > 1 ? path7.slice(1) : path7;
8493
+ const joined = withoutTypePrefix.join("_");
8494
+ return this.toSwiftIdentifier(joined);
8495
+ }
8496
+ formatSwiftValue(token, options) {
8497
+ const value = token.$value;
8498
+ if (token.$type === "color") {
8499
+ return this.formatColorValue(value, options);
8500
+ }
8501
+ if (token.$type === "dimension") {
8502
+ return this.formatDimensionValue(value);
8503
+ }
8504
+ if (token.$type === "fontFamily") {
8505
+ return this.formatFontFamilyValue(value);
8506
+ }
8507
+ if (token.$type === "fontWeight") {
8508
+ return this.formatFontWeightValue(value);
8509
+ }
8510
+ if (token.$type === "duration") {
8511
+ return this.formatDurationValue(value);
8512
+ }
8513
+ if (token.$type === "shadow") {
8514
+ return this.formatShadowValue(value, options);
8515
+ }
8516
+ if (token.$type === "typography") {
8517
+ return this.formatTypographyValue(value);
8518
+ }
8519
+ if (token.$type === "border") {
8520
+ return this.formatBorderValue(value, options);
8521
+ }
8522
+ if (token.$type === "gradient") {
8523
+ return this.formatGradientValue(value, options);
8524
+ }
8525
+ if (token.$type === "number") {
8526
+ return String(value);
8527
+ }
8528
+ if (token.$type === "cubicBezier" && Array.isArray(value) && value.length === 4) {
8529
+ return `UnitCurve.bezier(startControlPoint: UnitPoint(x: ${value[0]}, y: ${value[1]}), endControlPoint: UnitPoint(x: ${value[2]}, y: ${value[3]}))`;
8530
+ }
8531
+ if (typeof value === "string") {
8532
+ return `"${this.escapeSwiftString(value)}"`;
8533
+ }
8534
+ if (typeof value === "number") {
8535
+ return String(value);
8536
+ }
8537
+ if (typeof value === "boolean") {
8538
+ return value ? "true" : "false";
8539
+ }
8540
+ return `"${this.escapeSwiftString(String(value))}"`;
8541
+ }
8542
+ formatColorValue(value, options) {
8543
+ if (!isColorObject(value)) {
8544
+ return typeof value === "string" ? `Color("${this.escapeSwiftString(value)}")` : "Color.clear";
8545
+ }
8546
+ const colorObj = value;
8547
+ const alpha = colorObj.alpha ?? 1;
8548
+ if (options.colorSpace === "displayP3") {
8549
+ const p3 = toP32(dtcgObjectToCulori(colorObj));
8550
+ const r2 = this.roundComponent(p3?.r ?? 0);
8551
+ const g2 = this.roundComponent(p3?.g ?? 0);
8552
+ const b2 = this.roundComponent(p3?.b ?? 0);
8553
+ return alpha < 1 ? `Color(.displayP3, red: ${r2}, green: ${g2}, blue: ${b2}, opacity: ${this.roundComponent(alpha)})` : `Color(.displayP3, red: ${r2}, green: ${g2}, blue: ${b2})`;
8554
+ }
8555
+ const rgb = toSRGB2(dtcgObjectToCulori(colorObj));
8556
+ const r = this.roundComponent(rgb?.r ?? 0);
8557
+ const g = this.roundComponent(rgb?.g ?? 0);
8558
+ const b = this.roundComponent(rgb?.b ?? 0);
8559
+ return alpha < 1 ? `Color(red: ${r}, green: ${g}, blue: ${b}, opacity: ${this.roundComponent(alpha)})` : `Color(red: ${r}, green: ${g}, blue: ${b})`;
8560
+ }
8561
+ formatDimensionValue(value) {
8562
+ if (isDimensionObject(value)) {
8563
+ const dim = value;
8564
+ const ptValue = dim.unit === "rem" ? dim.value * 16 : dim.value;
8565
+ return String(ptValue);
8566
+ }
8567
+ return String(value);
8568
+ }
8569
+ formatFontFamilyValue(value) {
8570
+ if (Array.isArray(value)) {
8571
+ const primary = value[0];
8572
+ return typeof primary === "string" ? `"${this.escapeSwiftString(primary)}"` : '"system"';
8573
+ }
8574
+ return typeof value === "string" ? `"${this.escapeSwiftString(value)}"` : '"system"';
8575
+ }
8576
+ formatFontWeightValue(value) {
8577
+ if (typeof value === "number") {
8578
+ return this.numericFontWeight(value);
8579
+ }
8580
+ if (typeof value === "string") {
8581
+ return this.namedFontWeight(value) ?? "Font.Weight.regular";
8582
+ }
8583
+ return "Font.Weight.regular";
8584
+ }
8585
+ numericFontWeight(weight) {
8586
+ if (weight <= 100) {
8587
+ return "Font.Weight.ultraLight";
8588
+ }
8589
+ if (weight <= 200) {
8590
+ return "Font.Weight.thin";
8591
+ }
8592
+ if (weight <= 300) {
8593
+ return "Font.Weight.light";
8594
+ }
8595
+ if (weight <= 400) {
8596
+ return "Font.Weight.regular";
8597
+ }
8598
+ if (weight <= 500) {
8599
+ return "Font.Weight.medium";
8600
+ }
8601
+ if (weight <= 600) {
8602
+ return "Font.Weight.semibold";
8603
+ }
8604
+ if (weight <= 700) {
8605
+ return "Font.Weight.bold";
8606
+ }
8607
+ if (weight <= 800) {
8608
+ return "Font.Weight.heavy";
8609
+ }
8610
+ return "Font.Weight.black";
8611
+ }
8612
+ namedFontWeight(name) {
8613
+ const map = {
8614
+ thin: "Font.Weight.thin",
8615
+ ultralight: "Font.Weight.ultraLight",
8616
+ extralight: "Font.Weight.ultraLight",
8617
+ light: "Font.Weight.light",
8618
+ regular: "Font.Weight.regular",
8619
+ normal: "Font.Weight.regular",
8620
+ medium: "Font.Weight.medium",
8621
+ semibold: "Font.Weight.semibold",
8622
+ demibold: "Font.Weight.semibold",
8623
+ bold: "Font.Weight.bold",
8624
+ heavy: "Font.Weight.heavy",
8625
+ extrabold: "Font.Weight.heavy",
8626
+ black: "Font.Weight.black",
8627
+ ultrabold: "Font.Weight.black"
8628
+ };
8629
+ return map[name.toLowerCase()];
8630
+ }
8631
+ formatDurationValue(value) {
8632
+ if (typeof value === "object" && value !== null && "value" in value && "unit" in value) {
8633
+ const dur = value;
8634
+ const seconds = dur.unit === "ms" ? dur.value / 1e3 : dur.value;
8635
+ return String(seconds);
8636
+ }
8637
+ return typeof value === "number" ? String(value) : "0";
8638
+ }
8639
+ formatShadowValue(value, options) {
8640
+ if (Array.isArray(value) && value.length > 0) {
8641
+ return this.formatSingleShadow(value[0], options);
8642
+ }
8643
+ if (typeof value === "object" && value !== null) {
8644
+ return this.formatSingleShadow(value, options);
8645
+ }
8646
+ return "ShadowStyle(color: .clear, radius: 0, x: 0, y: 0, spread: 0)";
8647
+ }
8648
+ formatSingleShadow(shadow, options) {
8649
+ const color = isColorObject(shadow.color) ? this.formatColorValue(shadow.color, options) : "Color.black.opacity(0.25)";
8650
+ const radius = isDimensionObject(shadow.blur) ? this.dimensionToCGFloat(shadow.blur) : "8";
8651
+ const x = isDimensionObject(shadow.offsetX) ? this.dimensionToCGFloat(shadow.offsetX) : "0";
8652
+ const y = isDimensionObject(shadow.offsetY) ? this.dimensionToCGFloat(shadow.offsetY) : "0";
8653
+ const spread = isDimensionObject(shadow.spread) ? this.dimensionToCGFloat(shadow.spread) : "0";
8654
+ return `ShadowStyle(color: ${color}, radius: ${radius}, x: ${x}, y: ${y}, spread: ${spread})`;
8655
+ }
8656
+ formatTypographyValue(value) {
8657
+ if (typeof value !== "object" || value === null) {
8658
+ return "TypographyStyle(font: Font.body, tracking: 0, lineSpacing: 0)";
8659
+ }
8660
+ const typo = value;
8661
+ const size = isDimensionObject(typo.fontSize) ? this.dimensionToPoints(typo.fontSize) : "16";
8662
+ const weight = typo.fontWeight != null ? this.formatFontWeightValue(typo.fontWeight) : "Font.Weight.regular";
8663
+ const fontExpr = this.buildFontExpression(typo, size, weight);
8664
+ const tracking = this.extractTracking(typo);
8665
+ const lineSpacing = this.extractLineSpacing(typo);
8666
+ return `TypographyStyle(font: ${fontExpr}, tracking: ${tracking}, lineSpacing: ${lineSpacing})`;
8667
+ }
8668
+ buildFontExpression(typo, size, weight) {
8669
+ if (typo.fontFamily != null) {
8670
+ const family = Array.isArray(typo.fontFamily) ? typo.fontFamily[0] : typo.fontFamily;
8671
+ if (typeof family === "string") {
8672
+ return `Font.custom("${this.escapeSwiftString(family)}", size: ${size}).weight(${weight})`;
8673
+ }
8674
+ }
8675
+ return `Font.system(size: ${size}, weight: ${weight})`;
8676
+ }
8677
+ extractTracking(typo) {
8678
+ if (!isDimensionObject(typo.letterSpacing)) {
8679
+ return "0";
8680
+ }
8681
+ const dim = typo.letterSpacing;
8682
+ const ptValue = dim.unit === "rem" ? dim.value * 16 : dim.value;
8683
+ return String(ptValue);
8684
+ }
8685
+ extractLineSpacing(typo) {
8686
+ if (typo.lineHeight == null || typeof typo.lineHeight !== "number") {
8687
+ return "0";
8688
+ }
8689
+ if (!isDimensionObject(typo.fontSize)) {
8690
+ return "0";
8691
+ }
8692
+ const dim = typo.fontSize;
8693
+ const basePt = dim.unit === "rem" ? dim.value * 16 : dim.value;
8694
+ const lineHeightPt = Math.round(basePt * typo.lineHeight * 100) / 100;
8695
+ return String(lineHeightPt - basePt);
8696
+ }
8697
+ dimensionToPoints(dim) {
8698
+ const ptValue = dim.unit === "rem" ? dim.value * 16 : dim.value;
8699
+ return String(ptValue);
8700
+ }
8701
+ /** Formats a dimension as a CGFloat literal (appends `.0` for integers). */
8702
+ dimensionToCGFloat(dim) {
8703
+ const ptValue = dim.unit === "rem" ? dim.value * 16 : dim.value;
8704
+ return Number.isInteger(ptValue) ? `${ptValue}.0` : String(ptValue);
8705
+ }
8706
+ getTypeAnnotation(token) {
8707
+ switch (token.$type) {
8708
+ case "dimension":
8709
+ return "CGFloat";
8710
+ case "duration":
8711
+ return "TimeInterval";
8712
+ case "number":
8713
+ return "Double";
8714
+ case "fontWeight":
8715
+ return "Font.Weight";
8716
+ case "fontFamily":
8717
+ return "String";
8718
+ default:
8719
+ return void 0;
8720
+ }
8721
+ }
8722
+ toSwiftIdentifier(name) {
8723
+ const camel = name.replace(/[-._]+(.)/g, (_, c) => c.toUpperCase()).replace(/[-._]+$/g, "").replace(/^[-._]+/g, "");
8724
+ const identifier = camel.charAt(0).toLowerCase() + camel.slice(1);
8725
+ const safe = /^\d/.test(identifier) ? `_${identifier}` : identifier;
8726
+ return SWIFT_KEYWORDS.has(safe) ? `\`${safe}\`` : safe;
8727
+ }
8728
+ escapeSwiftString(str) {
8729
+ return str.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n");
8730
+ }
8731
+ roundComponent(value) {
8732
+ return Math.round(value * 1e4) / 1e4;
8733
+ }
8734
+ indentStr(width, level) {
8735
+ return " ".repeat(width * level);
8736
+ }
8737
+ /**
8738
+ * Returns the prefix for `static let` declarations.
8739
+ * Swift 6 requires `nonisolated(unsafe)` on global stored properties.
8740
+ */
8741
+ staticLetPrefix(options) {
8742
+ return options.swiftVersion === "6.0" ? "nonisolated(unsafe) static let " : "static let ";
8743
+ }
8744
+ /** Returns `@frozen ` when the frozen option is enabled, empty string otherwise. */
8745
+ frozenPrefix(options) {
8746
+ return options.frozen ? "@frozen " : "";
8747
+ }
8748
+ /** Returns `: Sendable` when targeting Swift 6, empty string otherwise. */
8749
+ structConformances(options) {
8750
+ return options.swiftVersion === "6.0" ? ": Sendable" : "";
8751
+ }
8752
+ hasShadowTokens(tokens) {
8753
+ return Object.values(tokens).some((t) => t.$type === "shadow");
8754
+ }
8755
+ hasTypographyTokens(tokens) {
8756
+ return Object.values(tokens).some((t) => t.$type === "typography");
8757
+ }
8758
+ hasBorderTokens(tokens) {
8759
+ return Object.values(tokens).some((t) => t.$type === "border");
8760
+ }
8761
+ /** Emits all struct definitions needed by the token set. */
8762
+ buildStructDefinitions(tokens, access3, options) {
8763
+ const lines = [];
8764
+ if (this.hasShadowTokens(tokens)) {
8765
+ lines.push("");
8766
+ lines.push(...this.buildShadowStyleStruct(access3, options));
8767
+ }
8768
+ if (this.hasTypographyTokens(tokens)) {
8769
+ lines.push("");
8770
+ lines.push(...this.buildTypographyStyleStruct(access3, options));
8771
+ }
8772
+ if (this.hasBorderTokens(tokens)) {
8773
+ lines.push("");
8774
+ lines.push(...this.buildBorderStyleStruct(access3, options));
8775
+ }
8776
+ return lines;
8777
+ }
8778
+ buildShadowStyleStruct(access3, options) {
8779
+ const i1 = this.indentStr(options.indent, 1);
8780
+ const conformances = this.structConformances(options);
8781
+ const frozen = this.frozenPrefix(options);
8782
+ return [
8783
+ `${frozen}${access3} struct ShadowStyle${conformances} {`,
8784
+ `${i1}${access3} let color: Color`,
8785
+ `${i1}${access3} let radius: CGFloat`,
8786
+ `${i1}${access3} let x: CGFloat`,
8787
+ `${i1}${access3} let y: CGFloat`,
8788
+ `${i1}${access3} let spread: CGFloat`,
8789
+ "}"
8790
+ ];
8791
+ }
8792
+ buildTypographyStyleStruct(access3, options) {
8793
+ const i1 = this.indentStr(options.indent, 1);
8794
+ const conformances = this.structConformances(options);
8795
+ const frozen = this.frozenPrefix(options);
8796
+ return [
8797
+ `${frozen}${access3} struct TypographyStyle${conformances} {`,
8798
+ `${i1}${access3} let font: Font`,
8799
+ `${i1}${access3} let tracking: CGFloat`,
8800
+ `${i1}${access3} let lineSpacing: CGFloat`,
8801
+ "}"
8802
+ ];
8803
+ }
8804
+ buildBorderStyleStruct(access3, options) {
8805
+ const i1 = this.indentStr(options.indent, 1);
8806
+ const conformances = this.structConformances(options);
8807
+ const frozen = this.frozenPrefix(options);
8808
+ return [
8809
+ `${frozen}${access3} struct BorderStyle${conformances} {`,
8810
+ `${i1}${access3} let color: Color`,
8811
+ `${i1}${access3} let width: CGFloat`,
8812
+ "}"
8813
+ ];
8814
+ }
8815
+ /** Emits convenience View extensions for shadow and typography application. */
8816
+ buildViewExtensions(tokens, access3, options) {
8817
+ const lines = [];
8818
+ const i1 = this.indentStr(options.indent, 1);
8819
+ const i2 = this.indentStr(options.indent, 2);
8820
+ if (this.hasShadowTokens(tokens)) {
8821
+ lines.push("");
8822
+ lines.push(`${access3} extension View {`);
8823
+ lines.push(`${i1}func shadowStyle(_ style: ShadowStyle) -> some View {`);
8824
+ lines.push(
8825
+ `${i2}self.shadow(color: style.color, radius: style.radius, x: style.x, y: style.y)`
8826
+ );
8827
+ lines.push(`${i1}}`);
8828
+ lines.push("}");
8829
+ }
8830
+ if (this.hasTypographyTokens(tokens)) {
8831
+ lines.push("");
8832
+ lines.push(`${access3} extension View {`);
8833
+ lines.push(`${i1}func typographyStyle(_ style: TypographyStyle) -> some View {`);
8834
+ lines.push(
8835
+ `${i2}self.font(style.font).tracking(style.tracking).lineSpacing(style.lineSpacing)`
8836
+ );
8837
+ lines.push(`${i1}}`);
8838
+ lines.push("}");
8839
+ }
8840
+ return lines;
8841
+ }
8842
+ formatBorderValue(value, options) {
8843
+ if (typeof value !== "object" || value === null) {
8844
+ return "BorderStyle(color: .clear, width: 0)";
8845
+ }
8846
+ const border = value;
8847
+ const color = isColorObject(border.color) ? this.formatColorValue(border.color, options) : "Color.clear";
8848
+ const width = isDimensionObject(border.width) ? this.dimensionToCGFloat(border.width) : "1.0";
8849
+ return `BorderStyle(color: ${color}, width: ${width})`;
8850
+ }
8851
+ formatGradientValue(value, options) {
8852
+ if (!Array.isArray(value) || value.length === 0) {
8853
+ return "Gradient(stops: [])";
8854
+ }
8855
+ const stops = value.map((stop) => {
8856
+ const color = isColorObject(stop.color) ? this.formatColorValue(stop.color, options) : "Color.clear";
8857
+ return `.init(color: ${color}, location: ${stop.position})`;
8858
+ });
8859
+ return `Gradient(stops: [${stops.join(", ")}])`;
8860
+ }
8861
+ async formatStandalone(context, options) {
8862
+ const requiresFile = context.buildPath !== void 0 && context.buildPath !== "";
8863
+ if (!context.output.file && requiresFile) {
8864
+ throw new ConfigurationError(
8865
+ `Output "${context.output.name}": file is required for standalone iOS output`
8866
+ );
8867
+ }
8868
+ const files = {};
8869
+ for (const { tokens, modifierInputs } of context.permutations) {
8870
+ const processedTokens = stripInternalMetadata(tokens);
8871
+ const content = this.formatTokens(processedTokens, options);
8872
+ const fileName = context.output.file ? resolveFileName(context.output.file, modifierInputs) : buildInMemoryOutputKey({
8873
+ outputName: context.output.name,
8874
+ extension: "swift",
8875
+ modifierInputs,
8876
+ resolver: context.resolver,
8877
+ defaults: context.meta.defaults
8878
+ });
8879
+ files[fileName] = content;
8880
+ }
8881
+ return outputTree(files);
7392
8882
  }
7393
8883
  };
7394
- function cssRenderer() {
7395
- const rendererInstance = new CssRenderer();
8884
+ function iosRenderer() {
8885
+ const rendererInstance = new IosRenderer();
7396
8886
  return {
7397
8887
  format: (context, options) => rendererInstance.format(
7398
8888
  context,
@@ -7518,8 +9008,8 @@ var JsModuleRenderer = class {
7518
9008
  /**
7519
9009
  * Add object properties to lines
7520
9010
  */
7521
- addObjectProperties(lines, obj, indent) {
7522
- const indentStr = " ".repeat(indent);
9011
+ addObjectProperties(lines, obj, indent2) {
9012
+ const indentStr = " ".repeat(indent2);
7523
9013
  const entries = Object.entries(obj).sort(([keyA], [keyB]) => keyA.localeCompare(keyB));
7524
9014
  for (let i = 0; i < entries.length; i++) {
7525
9015
  const entry = entries[i];
@@ -7530,7 +9020,7 @@ var JsModuleRenderer = class {
7530
9020
  const isLast = i === entries.length - 1;
7531
9021
  if (typeof value === "object" && value !== null && !Array.isArray(value)) {
7532
9022
  lines.push(`${indentStr}${this.quoteKey(key)}: {`);
7533
- this.addObjectProperties(lines, value, indent + 1);
9023
+ this.addObjectProperties(lines, value, indent2 + 1);
7534
9024
  lines.push(`${indentStr}}${isLast ? "" : ","}`);
7535
9025
  } else {
7536
9026
  const valueStr = JSON.stringify(value);
@@ -7722,6 +9212,349 @@ function jsonRenderer() {
7722
9212
  };
7723
9213
  }
7724
9214
 
9215
+ // src/renderers/tailwind.ts
9216
+ init_errors();
9217
+ init_token_utils();
9218
+
9219
+ // src/renderers/bundlers/tailwind.ts
9220
+ init_errors();
9221
+ init_utils();
9222
+ async function bundleAsTailwind(bundleData, options, formatThemeTokens, formatOverrideBlock) {
9223
+ const baseItem = bundleData.find((item) => item.isBase);
9224
+ if (!baseItem) {
9225
+ throw new BasePermutationError("Base permutation not found in bundle data");
9226
+ }
9227
+ const resolvedOpts = resolveOptions(options);
9228
+ const cssBlocks = [];
9229
+ const variantDeclarations = collectVariantDeclarations(bundleData, baseItem, resolvedOpts);
9230
+ const themeOpts = { ...resolvedOpts, variantDeclarations };
9231
+ const baseTokens = stripInternalMetadata(baseItem.tokens);
9232
+ const themeBlock = await formatThemeTokens(baseTokens, themeOpts);
9233
+ cssBlocks.push(themeBlock);
9234
+ for (const item of bundleData) {
9235
+ if (item.isBase) {
9236
+ continue;
9237
+ }
9238
+ const block = await formatModifierOverride(item, baseItem, resolvedOpts, formatOverrideBlock);
9239
+ if (block) {
9240
+ cssBlocks.push(block);
9241
+ }
9242
+ }
9243
+ return cssBlocks.join("\n");
9244
+ }
9245
+ async function formatModifierOverride({ tokens, modifierInputs }, baseItem, options, formatOverrideBlock) {
9246
+ const differenceCount = countModifierDifferences(modifierInputs, baseItem.modifierInputs);
9247
+ if (differenceCount > 1) {
9248
+ return void 0;
9249
+ }
9250
+ const tokensToInclude = filterTokensByValueChange(tokens, baseItem.tokens);
9251
+ if (Object.keys(tokensToInclude).length === 0) {
9252
+ return void 0;
9253
+ }
9254
+ const expectedSource = getExpectedSource(modifierInputs, baseItem.modifierInputs);
9255
+ const [modifier, context] = parseModifierSource(expectedSource);
9256
+ const cleanTokens = stripInternalMetadata(tokensToInclude);
9257
+ const selector = resolveSelector(
9258
+ options.selector,
9259
+ modifier,
9260
+ context,
9261
+ false,
9262
+ normalizeModifierInputs(modifierInputs)
9263
+ );
9264
+ const mediaQuery = resolveMediaQuery(
9265
+ options.mediaQuery,
9266
+ modifier,
9267
+ context,
9268
+ false,
9269
+ normalizeModifierInputs(modifierInputs)
9270
+ );
9271
+ const css2 = await formatOverrideBlock(cleanTokens, selector, mediaQuery, options.minify);
9272
+ return `/* Modifier: ${modifier}=${context} */
9273
+ ${css2}`;
9274
+ }
9275
+ function filterTokensByValueChange(currentTokens, baseTokens) {
9276
+ const changed = {};
9277
+ for (const [name, token] of Object.entries(currentTokens)) {
9278
+ const baseToken = baseTokens[name];
9279
+ if (!baseToken) {
9280
+ changed[name] = token;
9281
+ continue;
9282
+ }
9283
+ if (JSON.stringify(token.$value) !== JSON.stringify(baseToken.$value)) {
9284
+ changed[name] = token;
9285
+ }
9286
+ }
9287
+ return changed;
9288
+ }
9289
+ function collectVariantDeclarations(bundleData, baseItem, options) {
9290
+ const declarations = [];
9291
+ for (const item of bundleData) {
9292
+ if (item.isBase) {
9293
+ continue;
9294
+ }
9295
+ const differenceCount = countModifierDifferences(item.modifierInputs, baseItem.modifierInputs);
9296
+ if (differenceCount > 1) {
9297
+ continue;
9298
+ }
9299
+ const expectedSource = getExpectedSource(item.modifierInputs, baseItem.modifierInputs);
9300
+ const [modifier, context] = parseModifierSource(expectedSource);
9301
+ const variantName = `${modifier}-${context}`;
9302
+ const normalized = normalizeModifierInputs(item.modifierInputs);
9303
+ const mediaQuery = resolveMediaQuery(options.mediaQuery, modifier, context, false, normalized);
9304
+ if (mediaQuery !== "") {
9305
+ declarations.push(`@custom-variant ${variantName} (@media ${mediaQuery});`);
9306
+ continue;
9307
+ }
9308
+ const selector = resolveSelector(options.selector, modifier, context, false, normalized);
9309
+ declarations.push(`@custom-variant ${variantName} (&:where(${selector}, ${selector} *));`);
9310
+ }
9311
+ return declarations;
9312
+ }
9313
+ function resolveOptions(options) {
9314
+ return {
9315
+ preset: options.preset ?? "bundle",
9316
+ includeImport: options.includeImport ?? true,
9317
+ namespace: options.namespace ?? "",
9318
+ minify: options.minify ?? false,
9319
+ selector: options.selector,
9320
+ mediaQuery: options.mediaQuery,
9321
+ variantDeclarations: []
9322
+ };
9323
+ }
9324
+
9325
+ // src/renderers/tailwind.ts
9326
+ init_utils();
9327
+ var TAILWIND_NAMESPACE_MAP = {
9328
+ color: "color",
9329
+ dimension: "spacing",
9330
+ fontFamily: "font",
9331
+ fontWeight: "font-weight",
9332
+ duration: "duration",
9333
+ shadow: "shadow",
9334
+ number: "number",
9335
+ cubicBezier: "ease"
9336
+ };
9337
+ var TailwindRenderer = class {
9338
+ async format(context, options) {
9339
+ const opts = {
9340
+ preset: options?.preset ?? "bundle",
9341
+ includeImport: options?.includeImport ?? true,
9342
+ namespace: options?.namespace ?? "",
9343
+ minify: options?.minify ?? false,
9344
+ selector: options?.selector,
9345
+ mediaQuery: options?.mediaQuery,
9346
+ variantDeclarations: []
9347
+ };
9348
+ if (opts.preset === "bundle") {
9349
+ return await this.formatBundle(context, opts);
9350
+ }
9351
+ return await this.formatStandalone(context, opts);
9352
+ }
9353
+ /**
9354
+ * Format tokens as Tailwind v4 @theme CSS variables
9355
+ */
9356
+ async formatTokens(tokens, options) {
9357
+ const lines = [];
9358
+ const indent2 = options.minify ? "" : " ";
9359
+ const newline = options.minify ? "" : "\n";
9360
+ const space = options.minify ? "" : " ";
9361
+ if (options.includeImport) {
9362
+ lines.push(`@import "tailwindcss";${newline}`);
9363
+ }
9364
+ if (options.variantDeclarations.length > 0) {
9365
+ if (options.includeImport) {
9366
+ lines.push(newline);
9367
+ }
9368
+ for (const declaration of options.variantDeclarations) {
9369
+ lines.push(`${declaration}${newline}`);
9370
+ }
9371
+ }
9372
+ if (options.includeImport || options.variantDeclarations.length > 0) {
9373
+ lines.push(newline);
9374
+ }
9375
+ const themeDirective = options.namespace ? `@theme namespace(${options.namespace})` : "@theme";
9376
+ lines.push(`${themeDirective}${space}{${newline}`);
9377
+ for (const [, token] of getSortedTokenEntries(tokens)) {
9378
+ const varName = this.buildVariableName(token);
9379
+ const varValue = this.formatValue(token);
9380
+ lines.push(`${indent2}--${varName}:${space}${varValue};${newline}`);
9381
+ }
9382
+ lines.push(`}${newline}`);
9383
+ const cssString = lines.join("");
9384
+ return options.minify ? cssString : await this.formatWithPrettier(cssString);
9385
+ }
9386
+ /**
9387
+ * Format tokens as plain CSS custom property overrides inside a selector block.
9388
+ * Used for modifier overrides (e.g., dark mode) appended after the @theme block.
9389
+ */
9390
+ async formatOverrideBlock(tokens, selector, mediaQuery, minify) {
9391
+ const indent2 = minify ? "" : " ";
9392
+ const newline = minify ? "" : "\n";
9393
+ const space = minify ? "" : " ";
9394
+ const hasMediaQuery = mediaQuery !== "";
9395
+ const tokenIndent = hasMediaQuery ? indent2 + indent2 : indent2;
9396
+ const lines = [];
9397
+ if (hasMediaQuery) {
9398
+ lines.push(`@media ${mediaQuery}${space}{${newline}`);
9399
+ lines.push(`${indent2}${selector}${space}{${newline}`);
9400
+ } else {
9401
+ lines.push(`${selector}${space}{${newline}`);
9402
+ }
9403
+ for (const [, token] of getSortedTokenEntries(tokens)) {
9404
+ const varName = this.buildVariableName(token);
9405
+ const varValue = this.formatValue(token);
9406
+ lines.push(`${tokenIndent}--${varName}:${space}${varValue};${newline}`);
9407
+ }
9408
+ if (hasMediaQuery) {
9409
+ lines.push(`${indent2}}${newline}`);
9410
+ lines.push(`}${newline}`);
9411
+ } else {
9412
+ lines.push(`}${newline}`);
9413
+ }
9414
+ return lines.join("");
9415
+ }
9416
+ buildVariableName(token) {
9417
+ const prefix = TAILWIND_NAMESPACE_MAP[token.$type ?? ""];
9418
+ if (!prefix) {
9419
+ return token.name;
9420
+ }
9421
+ const nameLower = token.name.toLowerCase();
9422
+ const prefixLower = prefix.toLowerCase();
9423
+ if (nameLower.startsWith(`${prefixLower}-`) || nameLower.startsWith(`${prefixLower}.`)) {
9424
+ return token.name;
9425
+ }
9426
+ return `${prefix}-${token.name}`;
9427
+ }
9428
+ formatValue(token) {
9429
+ const value = token.$value;
9430
+ if (token.$type === "color" && isColorObject(value)) {
9431
+ return colorObjectToHex(value);
9432
+ }
9433
+ if (token.$type === "dimension" && isDimensionObject(value)) {
9434
+ return dimensionObjectToString(value);
9435
+ }
9436
+ if (token.$type === "duration" && this.isDurationObject(value)) {
9437
+ return `${value.value}${value.unit}`;
9438
+ }
9439
+ if (token.$type === "fontFamily") {
9440
+ if (Array.isArray(value)) {
9441
+ return value.map((v) => typeof v === "string" && v.includes(" ") ? `"${v}"` : v).join(", ");
9442
+ }
9443
+ return typeof value === "string" ? value : String(value);
9444
+ }
9445
+ if (token.$type === "shadow") {
9446
+ return this.formatShadowValue(value);
9447
+ }
9448
+ if (token.$type === "cubicBezier" && Array.isArray(value) && value.length === 4) {
9449
+ return `cubic-bezier(${value.join(", ")})`;
9450
+ }
9451
+ if (typeof value === "string") {
9452
+ return value;
9453
+ }
9454
+ if (typeof value === "number") {
9455
+ return String(value);
9456
+ }
9457
+ return String(value);
9458
+ }
9459
+ formatShadowValue(value) {
9460
+ if (Array.isArray(value) && value.length > 0 && typeof value[0] === "object") {
9461
+ return value.map((s) => this.formatSingleShadow(s)).join(", ");
9462
+ }
9463
+ if (typeof value === "object" && value !== null) {
9464
+ return this.formatSingleShadow(value);
9465
+ }
9466
+ return String(value);
9467
+ }
9468
+ formatSingleShadow(shadow) {
9469
+ const parts = [];
9470
+ if (shadow.inset === true) {
9471
+ parts.push("inset");
9472
+ }
9473
+ if (isDimensionObject(shadow.offsetX)) {
9474
+ parts.push(dimensionObjectToString(shadow.offsetX));
9475
+ }
9476
+ if (isDimensionObject(shadow.offsetY)) {
9477
+ parts.push(dimensionObjectToString(shadow.offsetY));
9478
+ }
9479
+ if (isDimensionObject(shadow.blur)) {
9480
+ parts.push(dimensionObjectToString(shadow.blur));
9481
+ }
9482
+ if (shadow.spread != null && isDimensionObject(shadow.spread)) {
9483
+ parts.push(dimensionObjectToString(shadow.spread));
9484
+ }
9485
+ if (isColorObject(shadow.color)) {
9486
+ parts.push(colorObjectToHex(shadow.color));
9487
+ } else if (shadow.color != null) {
9488
+ parts.push(String(shadow.color));
9489
+ }
9490
+ return parts.join(" ");
9491
+ }
9492
+ isDurationObject(value) {
9493
+ return typeof value === "object" && value !== null && "value" in value && "unit" in value && value.unit !== void 0;
9494
+ }
9495
+ async formatWithPrettier(css2) {
9496
+ try {
9497
+ return await prettier.format(css2, {
9498
+ parser: "css",
9499
+ printWidth: 80,
9500
+ tabWidth: 2,
9501
+ useTabs: false
9502
+ });
9503
+ } catch {
9504
+ return css2;
9505
+ }
9506
+ }
9507
+ async formatBundle(context, options) {
9508
+ const bundleData = context.permutations.map(({ tokens, modifierInputs }) => ({
9509
+ tokens,
9510
+ modifierInputs,
9511
+ isBase: this.isBasePermutation(modifierInputs, context.meta.defaults)
9512
+ }));
9513
+ return await bundleAsTailwind(
9514
+ bundleData,
9515
+ options,
9516
+ async (tokens, opts) => await this.formatTokens(tokens, opts),
9517
+ async (tokens, selector, mediaQuery, minify) => await this.formatOverrideBlock(tokens, selector, mediaQuery, minify)
9518
+ );
9519
+ }
9520
+ async formatStandalone(context, options) {
9521
+ const requiresFile = context.buildPath !== void 0 && context.buildPath !== "";
9522
+ if (!context.output.file && requiresFile) {
9523
+ throw new ConfigurationError(
9524
+ `Output "${context.output.name}": file is required for standalone Tailwind output`
9525
+ );
9526
+ }
9527
+ const files = {};
9528
+ for (const { tokens, modifierInputs } of context.permutations) {
9529
+ const processedTokens = stripInternalMetadata(tokens);
9530
+ const content = await this.formatTokens(processedTokens, options);
9531
+ const fileName = context.output.file ? resolveFileName(context.output.file, modifierInputs) : buildInMemoryOutputKey({
9532
+ outputName: context.output.name,
9533
+ extension: "css",
9534
+ modifierInputs,
9535
+ resolver: context.resolver,
9536
+ defaults: context.meta.defaults
9537
+ });
9538
+ files[fileName] = content;
9539
+ }
9540
+ return outputTree(files);
9541
+ }
9542
+ isBasePermutation(modifierInputs, defaults) {
9543
+ return Object.entries(defaults).every(
9544
+ ([key, value]) => modifierInputs[key]?.toLowerCase() === value.toLowerCase()
9545
+ );
9546
+ }
9547
+ };
9548
+ function tailwindRenderer() {
9549
+ const rendererInstance = new TailwindRenderer();
9550
+ return {
9551
+ format: (context, options) => rendererInstance.format(
9552
+ context,
9553
+ options ?? context.output.options
9554
+ )
9555
+ };
9556
+ }
9557
+
7725
9558
  // src/builders.ts
7726
9559
  function css(config) {
7727
9560
  const {
@@ -7783,6 +9616,58 @@ function js(config) {
7783
9616
  hooks
7784
9617
  };
7785
9618
  }
9619
+ function tailwind(config) {
9620
+ const { name, file, transforms, filters, hooks, preset = "bundle", ...rendererOptions } = config;
9621
+ return {
9622
+ name,
9623
+ file,
9624
+ renderer: tailwindRenderer(),
9625
+ options: { preset, ...rendererOptions },
9626
+ transforms,
9627
+ filters,
9628
+ hooks
9629
+ };
9630
+ }
9631
+ function ios(config) {
9632
+ const {
9633
+ name,
9634
+ file,
9635
+ transforms,
9636
+ filters,
9637
+ hooks,
9638
+ preset = "standalone",
9639
+ ...rendererOptions
9640
+ } = config;
9641
+ return {
9642
+ name,
9643
+ file,
9644
+ renderer: iosRenderer(),
9645
+ options: { preset, ...rendererOptions },
9646
+ transforms,
9647
+ filters,
9648
+ hooks
9649
+ };
9650
+ }
9651
+ function android(config) {
9652
+ const {
9653
+ name,
9654
+ file,
9655
+ transforms,
9656
+ filters,
9657
+ hooks,
9658
+ preset = "standalone",
9659
+ ...rendererOptions
9660
+ } = config;
9661
+ return {
9662
+ name,
9663
+ file,
9664
+ renderer: androidRenderer(),
9665
+ options: { preset, ...rendererOptions },
9666
+ transforms,
9667
+ filters,
9668
+ hooks
9669
+ };
9670
+ }
7786
9671
 
7787
9672
  // src/renderers/types.ts
7788
9673
  function defineRenderer(renderer) {
@@ -7796,7 +9681,14 @@ init_errors();
7796
9681
  * Copyright (c) 2025 Dispersa Contributors
7797
9682
  * SPDX-License-Identifier: MIT
7798
9683
  */
9684
+ /**
9685
+ * @license MIT
9686
+ * Copyright (c) 2025-present Dispersa Contributors
9687
+ *
9688
+ * This source code is licensed under the MIT license found in the
9689
+ * LICENSE file in the root directory of this source tree.
9690
+ */
7799
9691
 
7800
- export { BasePermutationError, CircularReferenceError, ColorParseError, ConfigurationError, DimensionFormatError, Dispersa, DispersaError, FileOperationError, ModifierError, TokenReferenceError, ValidationError, css, defineRenderer, isBorderToken, isColorToken, isDimensionToken, isDurationToken, isGradientToken, isOutputTree, isShadowToken, isTransitionToken, isTypographyToken, js, json, outputTree };
9692
+ export { BasePermutationError, CircularReferenceError, ColorParseError, ConfigurationError, DimensionFormatError, Dispersa, DispersaError, FileOperationError, ModifierError, TokenReferenceError, ValidationError, android, css, defineRenderer, ios, isBorderToken, isColorToken, isDimensionToken, isDurationToken, isGradientToken, isOutputTree, isShadowToken, isTransitionToken, isTypographyToken, js, json, outputTree, tailwind };
7801
9693
  //# sourceMappingURL=index.js.map
7802
9694
  //# sourceMappingURL=index.js.map