@fragments-sdk/cli 0.14.2 → 0.15.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 (135) hide show
  1. package/README.md +0 -3
  2. package/dist/bin.js +4290 -3754
  3. package/dist/bin.js.map +1 -1
  4. package/dist/{chunk-TXFCEDOC.js → chunk-2WXKALIG.js} +2 -2
  5. package/dist/{chunk-I34BC3CU.js → chunk-32LIWN2P.js} +1006 -3
  6. package/dist/chunk-32LIWN2P.js.map +1 -0
  7. package/dist/{chunk-55KERLWL.js → chunk-65WSVDV5.js} +314 -89
  8. package/dist/chunk-65WSVDV5.js.map +1 -0
  9. package/dist/chunk-7DZC4YEV.js +294 -0
  10. package/dist/chunk-7DZC4YEV.js.map +1 -0
  11. package/dist/{chunk-LOYS64QS.js → chunk-7WHVW72L.js} +230 -19
  12. package/dist/chunk-7WHVW72L.js.map +1 -0
  13. package/dist/{chunk-PJT5IZ37.js → chunk-BJE3425I.js} +19 -52
  14. package/dist/{chunk-PJT5IZ37.js.map → chunk-BJE3425I.js.map} +1 -1
  15. package/dist/{chunk-5A6X2Y73.js → chunk-CZD3AD4Q.js} +12 -11
  16. package/dist/chunk-CZD3AD4Q.js.map +1 -0
  17. package/dist/{chunk-EYXVAMEX.js → chunk-MN3TJ3D5.js} +72 -3
  18. package/dist/chunk-MN3TJ3D5.js.map +1 -0
  19. package/dist/chunk-QCN35LJU.js +630 -0
  20. package/dist/chunk-QCN35LJU.js.map +1 -0
  21. package/dist/chunk-T47OLCSF.js +36 -0
  22. package/dist/chunk-T47OLCSF.js.map +1 -0
  23. package/dist/{chunk-APTQIBS5.js → chunk-XJQ5BIWI.js} +144 -1049
  24. package/dist/chunk-XJQ5BIWI.js.map +1 -0
  25. package/dist/codebase-scanner-VOTPXRYW.js +22 -0
  26. package/dist/converter-JLINP7CJ.js +34 -0
  27. package/dist/converter-JLINP7CJ.js.map +1 -0
  28. package/dist/core/index.js +43 -1
  29. package/dist/{generate-RYWIPDN2.js → generate-A4FP5426.js} +3 -4
  30. package/dist/{generate-RYWIPDN2.js.map → generate-A4FP5426.js.map} +1 -1
  31. package/dist/govern-scan-UCBZR6D6.js +280 -0
  32. package/dist/govern-scan-UCBZR6D6.js.map +1 -0
  33. package/dist/index.d.ts +2 -1
  34. package/dist/index.js +11 -11
  35. package/dist/{init-WRUSW7R5.js → init-HGSM35XA.js} +131 -128
  36. package/dist/init-HGSM35XA.js.map +1 -0
  37. package/dist/{init-cloud-REQ3XLHO.js → init-cloud-MQ6GRJAZ.js} +2 -2
  38. package/dist/mcp-bin.js +5 -36
  39. package/dist/mcp-bin.js.map +1 -1
  40. package/dist/scan-VNNKACG2.js +15 -0
  41. package/dist/{scan-generate-TFZVL3BT.js → scan-generate-TWRHNU5M.js} +335 -46
  42. package/dist/scan-generate-TWRHNU5M.js.map +1 -0
  43. package/dist/scanner-7LAZYPWZ.js +13 -0
  44. package/dist/{service-HKJ6B7P7.js → service-FHQU7YS7.js} +27 -23
  45. package/dist/{snapshot-C5DYIGIV.js → snapshot-KQEQ6XHL.js} +2 -2
  46. package/dist/{static-viewer-DUVC4UIM.js → static-viewer-63PG6FWY.js} +3 -3
  47. package/dist/static-viewer-63PG6FWY.js.map +1 -0
  48. package/dist/{test-JW7JIDFG.js → test-UQYUCZIS.js} +4 -6
  49. package/dist/{test-JW7JIDFG.js.map → test-UQYUCZIS.js.map} +1 -1
  50. package/dist/{tokens-KE73G5JC.js → tokens-6GYKDV6U.js} +6 -5
  51. package/dist/{tokens-KE73G5JC.js.map → tokens-6GYKDV6U.js.map} +1 -1
  52. package/dist/tokens-generate-VTZV5EEW.js +86 -0
  53. package/dist/tokens-generate-VTZV5EEW.js.map +1 -0
  54. package/package.json +6 -6
  55. package/src/bin.ts +210 -48
  56. package/src/build.ts +130 -6
  57. package/src/commands/__fixtures__/shadcn-label-wrapper/package.json +7 -0
  58. package/src/commands/__fixtures__/shadcn-label-wrapper/src/components/ui/label.contract.json +42 -0
  59. package/src/commands/__fixtures__/shadcn-label-wrapper/src/components/ui/label.tsx +11 -0
  60. package/src/commands/__fixtures__/shadcn-label-wrapper/src/components/ui/primitive.contract.json +20 -0
  61. package/src/commands/__fixtures__/shadcn-label-wrapper/src/components/ui/primitive.tsx +14 -0
  62. package/src/commands/__fixtures__/shadcn-label-wrapper/tsconfig.app.json +23 -0
  63. package/src/commands/__tests__/init.test.ts +113 -0
  64. package/src/commands/__tests__/scan-generate.test.ts +188 -69
  65. package/src/commands/__tests__/verify.test.ts +91 -0
  66. package/src/commands/discover.ts +151 -0
  67. package/src/commands/enhance.ts +3 -1
  68. package/src/commands/govern-scan.ts +386 -0
  69. package/src/commands/govern.ts +2 -2
  70. package/src/commands/init.ts +152 -28
  71. package/src/commands/inspect.ts +290 -0
  72. package/src/commands/migrate-contract.ts +85 -0
  73. package/src/commands/scan-generate.ts +438 -50
  74. package/src/commands/scan.ts +1 -0
  75. package/src/commands/setup.ts +27 -50
  76. package/src/commands/tokens-generate.ts +113 -0
  77. package/src/commands/verify.ts +195 -1
  78. package/src/core/__fixtures__/shadcn-input/input.tsx +7 -0
  79. package/src/core/__fixtures__/shadcn-input/tsconfig.json +14 -0
  80. package/src/core/__fixtures__/shadcn-label/label.tsx +11 -0
  81. package/src/core/__fixtures__/shadcn-label/primitive.tsx +14 -0
  82. package/src/core/__fixtures__/shadcn-label/tsconfig.json +14 -0
  83. package/src/core/__fixtures__/shadcn-radix-label/label.tsx +11 -0
  84. package/src/core/__fixtures__/shadcn-radix-label/node_modules/radix-ui/index.d.ts +12 -0
  85. package/src/core/__fixtures__/shadcn-radix-label/tsconfig.json +14 -0
  86. package/src/core/__tests__/contract-parity.test.ts +316 -0
  87. package/src/core/component-extractor.test.ts +39 -0
  88. package/src/core/component-extractor.ts +92 -1
  89. package/src/core/config.ts +2 -1
  90. package/src/core/discovery.ts +13 -2
  91. package/src/core/drift-verifier.ts +123 -0
  92. package/src/core/extractor-adapter.ts +80 -0
  93. package/src/mcp/__tests__/projectFields.test.ts +1 -1
  94. package/src/mcp/utils.ts +1 -50
  95. package/src/migrate/converter.ts +3 -3
  96. package/src/migrate/fragment-to-contract.ts +253 -0
  97. package/src/migrate/report.ts +1 -1
  98. package/src/scripts/token-benchmark.ts +121 -0
  99. package/src/service/__tests__/props-extractor.test.ts +94 -0
  100. package/src/service/__tests__/token-normalizer.test.ts +690 -0
  101. package/src/service/ast-utils.ts +4 -23
  102. package/src/service/babel-config.ts +23 -0
  103. package/src/service/enhance/converter.ts +61 -0
  104. package/src/service/enhance/props-extractor.ts +25 -8
  105. package/src/service/enhance/scanner.ts +5 -24
  106. package/src/service/snippet-validation.ts +9 -3
  107. package/src/service/token-normalizer.ts +510 -0
  108. package/src/shared/index.ts +1 -0
  109. package/src/shared/project-fields.ts +46 -0
  110. package/src/viewer/__tests__/viewer-integration.test.ts +8 -8
  111. package/src/viewer/preview-adapter.ts +116 -0
  112. package/src/viewer/style-utils.ts +27 -412
  113. package/src/viewer/vite-plugin.ts +2 -2
  114. package/dist/chunk-55KERLWL.js.map +0 -1
  115. package/dist/chunk-5A6X2Y73.js.map +0 -1
  116. package/dist/chunk-APTQIBS5.js.map +0 -1
  117. package/dist/chunk-EYXVAMEX.js.map +0 -1
  118. package/dist/chunk-I34BC3CU.js.map +0 -1
  119. package/dist/chunk-LOYS64QS.js.map +0 -1
  120. package/dist/chunk-ZKTFKHWN.js +0 -324
  121. package/dist/chunk-ZKTFKHWN.js.map +0 -1
  122. package/dist/discovery-VDANZAJ2.js +0 -28
  123. package/dist/init-WRUSW7R5.js.map +0 -1
  124. package/dist/scan-YJHQIRKG.js +0 -14
  125. package/dist/scan-generate-TFZVL3BT.js.map +0 -1
  126. package/dist/viewer-2TZS3NDL.js +0 -2730
  127. package/dist/viewer-2TZS3NDL.js.map +0 -1
  128. package/src/commands/dev.ts +0 -107
  129. /package/dist/{chunk-TXFCEDOC.js.map → chunk-2WXKALIG.js.map} +0 -0
  130. /package/dist/{discovery-VDANZAJ2.js.map → codebase-scanner-VOTPXRYW.js.map} +0 -0
  131. /package/dist/{init-cloud-REQ3XLHO.js.map → init-cloud-MQ6GRJAZ.js.map} +0 -0
  132. /package/dist/{scan-YJHQIRKG.js.map → scan-VNNKACG2.js.map} +0 -0
  133. /package/dist/{service-HKJ6B7P7.js.map → scanner-7LAZYPWZ.js.map} +0 -0
  134. /package/dist/{static-viewer-DUVC4UIM.js.map → service-FHQU7YS7.js.map} +0 -0
  135. /package/dist/{snapshot-C5DYIGIV.js.map → snapshot-KQEQ6XHL.js.map} +0 -0
@@ -9,6 +9,8 @@ import {
9
9
  isExportStory as storybookIsExportStory
10
10
  } from "@storybook/csf";
11
11
  import { generateContext, filterPlaceholders, PLACEHOLDER_PATTERNS } from "@fragments-sdk/context/generate";
12
+ import { z as z2 } from "zod";
13
+ import { z as z3 } from "zod";
12
14
  import { ComponentGraphEngine } from "@fragments-sdk/context/graph";
13
15
  import { useEffect, useState } from "react";
14
16
  import { Fragment, jsx } from "react/jsx-runtime";
@@ -17,8 +19,8 @@ var BRAND = {
17
19
  name: "Fragments",
18
20
  /** Lowercase name for file paths and CLI (e.g., "fragments") */
19
21
  nameLower: "fragments",
20
- /** File extension for fragment definition files (e.g., ".fragment.tsx") */
21
- fileExtension: ".fragment.tsx",
22
+ /** File extension for fragment definition files V2 canonical format */
23
+ fileExtension: ".contract.json",
22
24
  /** Legacy file extension for segments (still supported for migration) */
23
25
  legacyFileExtension: ".segment.tsx",
24
26
  /** JSON file extension for compiled output */
@@ -134,6 +136,215 @@ function budgetBar(percent, width = 20) {
134
136
  const bar = "\u2588".repeat(filled) + "\u2591".repeat(width - filled);
135
137
  return percent > 100 ? `\x1B[31m${bar}\x1B[0m` : `\x1B[32m${bar}\x1B[0m`;
136
138
  }
139
+ var COLOR_PROPERTIES = /* @__PURE__ */ new Set(["backgroundColor", "borderColor", "color"]);
140
+ var NUMERIC_PROPERTIES = /* @__PURE__ */ new Set([
141
+ "borderWidth",
142
+ "borderRadius",
143
+ "fontSize",
144
+ "padding",
145
+ "gap"
146
+ ]);
147
+ var DEFAULT_STYLE_PROPERTIES = [
148
+ "backgroundColor",
149
+ "borderColor",
150
+ "borderWidth",
151
+ "borderRadius",
152
+ "fontFamily",
153
+ "fontSize",
154
+ "fontWeight",
155
+ "lineHeight",
156
+ "letterSpacing",
157
+ "textAlign",
158
+ "boxShadow",
159
+ "padding",
160
+ "gap",
161
+ "opacity"
162
+ ];
163
+ var DEFAULT_ENHANCED_STYLE_PROPERTIES = [
164
+ ...DEFAULT_STYLE_PROPERTIES,
165
+ "color"
166
+ ];
167
+ function normalizeStyleValue(prop, value) {
168
+ let normalized = value.trim().replace(/\s+/g, " ");
169
+ if (prop === "boxShadow" && normalized === "none") {
170
+ normalized = "";
171
+ }
172
+ if (normalized.match(/rgba\(\s*0\s*,\s*0\s*,\s*0\s*,\s*0\s*\)/)) {
173
+ normalized = "transparent";
174
+ }
175
+ return normalized;
176
+ }
177
+ function parseColor(color) {
178
+ const hexMatch = color.match(/^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i);
179
+ if (hexMatch) {
180
+ return {
181
+ r: parseInt(hexMatch[1], 16),
182
+ g: parseInt(hexMatch[2], 16),
183
+ b: parseInt(hexMatch[3], 16)
184
+ };
185
+ }
186
+ const rgbaMatch = color.match(
187
+ /rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(?:,\s*([\d.]+))?\s*\)/
188
+ );
189
+ if (rgbaMatch) {
190
+ return {
191
+ r: parseInt(rgbaMatch[1], 10),
192
+ g: parseInt(rgbaMatch[2], 10),
193
+ b: parseInt(rgbaMatch[3], 10),
194
+ a: rgbaMatch[4] ? parseFloat(rgbaMatch[4]) : 1
195
+ };
196
+ }
197
+ return null;
198
+ }
199
+ function compareColors(color1, color2, tolerance, alphaTolerance = 0.05) {
200
+ const rgb1 = parseColor(color1);
201
+ const rgb2 = parseColor(color2);
202
+ if (!rgb1 || !rgb2) {
203
+ return color1 === color2;
204
+ }
205
+ return Math.abs(rgb1.r - rgb2.r) <= tolerance && Math.abs(rgb1.g - rgb2.g) <= tolerance && Math.abs(rgb1.b - rgb2.b) <= tolerance && Math.abs((rgb1.a ?? 1) - (rgb2.a ?? 1)) <= alphaTolerance;
206
+ }
207
+ function compareNumericValues(value1, value2, tolerance) {
208
+ const num1 = parseFloat(value1);
209
+ const num2 = parseFloat(value2);
210
+ if (isNaN(num1) || isNaN(num2)) {
211
+ return value1 === value2;
212
+ }
213
+ return Math.abs(num1 - num2) <= tolerance;
214
+ }
215
+ function compareStyleValue(prop, figma2, rendered) {
216
+ const normalizedFigma = normalizeStyleValue(prop, figma2);
217
+ const normalizedRendered = normalizeStyleValue(prop, rendered);
218
+ if (normalizedFigma === normalizedRendered) {
219
+ return true;
220
+ }
221
+ if (COLOR_PROPERTIES.has(prop)) {
222
+ return compareColors(normalizedFigma, normalizedRendered, 5);
223
+ }
224
+ if (NUMERIC_PROPERTIES.has(prop)) {
225
+ return compareNumericValues(normalizedFigma, normalizedRendered, 1);
226
+ }
227
+ return false;
228
+ }
229
+ function compareStyles(figmaStyles, renderedStyles) {
230
+ const properties = [];
231
+ const cleanFigmaStyles = {};
232
+ const propsToCompare = DEFAULT_STYLE_PROPERTIES;
233
+ for (const prop of propsToCompare) {
234
+ const figmaValue = figmaStyles[prop];
235
+ const renderedValue = renderedStyles[prop];
236
+ if (figmaValue !== void 0) {
237
+ cleanFigmaStyles[prop] = figmaValue;
238
+ const match = compareStyleValue(prop, figmaValue, renderedValue || "");
239
+ properties.push({
240
+ property: prop,
241
+ figma: figmaValue,
242
+ rendered: renderedValue || "(not set)",
243
+ match
244
+ });
245
+ }
246
+ }
247
+ const allMatch = properties.every((p) => p.match);
248
+ return {
249
+ match: allMatch,
250
+ properties,
251
+ figmaStyles: cleanFigmaStyles,
252
+ renderedStyles
253
+ };
254
+ }
255
+ function compareStylesWithTokens(figmaStyles, renderedStyles, tokenLookup, theme = "default") {
256
+ const properties = [];
257
+ const cleanFigmaStyles = {};
258
+ const propsToCompare = DEFAULT_ENHANCED_STYLE_PROPERTIES;
259
+ for (const prop of propsToCompare) {
260
+ const figmaValue = figmaStyles[prop];
261
+ const renderedValue = renderedStyles[prop];
262
+ if (figmaValue !== void 0) {
263
+ cleanFigmaStyles[prop] = figmaValue;
264
+ const match = compareStyleValue(prop, figmaValue, renderedValue || "");
265
+ const item = {
266
+ property: prop,
267
+ figma: figmaValue,
268
+ rendered: renderedValue || "(not set)",
269
+ match,
270
+ isHardcoded: false
271
+ };
272
+ if (tokenLookup) {
273
+ const figmaTokens = tokenLookup.findByValue(figmaValue, theme);
274
+ const renderedTokens = renderedValue ? tokenLookup.findByValue(renderedValue, theme) : [];
275
+ if (figmaTokens.length > 0) {
276
+ item.figmaToken = figmaTokens[0];
277
+ }
278
+ if (renderedTokens.length > 0) {
279
+ item.renderedToken = renderedTokens[0];
280
+ }
281
+ item.isHardcoded = !!item.figmaToken && !item.renderedToken;
282
+ if (item.isHardcoded && item.figmaToken) {
283
+ const token = tokenLookup.getToken(item.figmaToken);
284
+ if (token) {
285
+ const cssProperty = toCssProperty(prop);
286
+ item.suggestedFix = {
287
+ tokenName: item.figmaToken,
288
+ tokenValue: token.resolvedValue,
289
+ codeFix: `${cssProperty}: var(${item.figmaToken});`,
290
+ confidence: 0.9,
291
+ reason: `Figma uses token ${item.figmaToken} (${token.resolvedValue}). Replace hardcoded value with token for consistency.`
292
+ };
293
+ }
294
+ }
295
+ }
296
+ properties.push(item);
297
+ }
298
+ }
299
+ const allMatch = properties.every((p) => p.match);
300
+ let tokenSummary;
301
+ if (tokenLookup) {
302
+ tokenSummary = tokenLookup.calculateUsageSummary(
303
+ properties.map((p) => ({
304
+ property: p.property,
305
+ figma: p.figma,
306
+ rendered: p.rendered,
307
+ match: p.match
308
+ })),
309
+ theme
310
+ );
311
+ }
312
+ return {
313
+ match: allMatch,
314
+ properties,
315
+ figmaStyles: cleanFigmaStyles,
316
+ renderedStyles,
317
+ tokenSummary
318
+ };
319
+ }
320
+ function toCssProperty(prop) {
321
+ return prop.replace(/([A-Z])/g, "-$1").toLowerCase();
322
+ }
323
+ function formatTokenSummary(summary) {
324
+ const lines = [];
325
+ lines.push(`Token Compliance: ${summary.compliancePercent}%`);
326
+ lines.push(
327
+ `${summary.usingTokens}/${summary.totalProperties} properties using tokens`
328
+ );
329
+ if (summary.hardcoded > 0) {
330
+ lines.push(`${summary.hardcoded} hardcoded value(s) detected`);
331
+ }
332
+ if (summary.implicitMatches > 0) {
333
+ lines.push(`${summary.implicitMatches} implicit match(es)`);
334
+ }
335
+ return lines.join("\n");
336
+ }
337
+ function getComplianceBadge(compliancePercent) {
338
+ if (compliancePercent >= 100) {
339
+ return { label: "Excellent", color: "green" };
340
+ } else if (compliancePercent >= 80) {
341
+ return { label: "Good", color: "blue" };
342
+ } else if (compliancePercent >= 50) {
343
+ return { label: "Fair", color: "yellow" };
344
+ } else {
345
+ return { label: "Poor", color: "red" };
346
+ }
347
+ }
137
348
  var figmaStringMappingSchema = z.object({
138
349
  __type: z.literal("figma-string"),
139
350
  figmaProperty: z.string().min(1)
@@ -1166,6 +1377,777 @@ function parseTokenFile(content, filePath) {
1166
1377
  total: tokens.length
1167
1378
  };
1168
1379
  }
1380
+ var DTCG_META_KEYS = /* @__PURE__ */ new Set([
1381
+ "$type",
1382
+ "$value",
1383
+ "$description",
1384
+ "$deprecated",
1385
+ "$extensions",
1386
+ "$extends"
1387
+ ]);
1388
+ var MAX_ALIAS_DEPTH = 10;
1389
+ function isDTCGFile(filePath) {
1390
+ return filePath.endsWith(".tokens.json") || filePath.endsWith(".tokens");
1391
+ }
1392
+ function mapDTCGTypeToCategory(type, tokenPath) {
1393
+ switch (type) {
1394
+ case "color":
1395
+ return "colors";
1396
+ case "dimension":
1397
+ if (/radius/i.test(tokenPath)) return "radius";
1398
+ return "spacing";
1399
+ case "fontFamily":
1400
+ case "fontWeight":
1401
+ return "typography";
1402
+ case "shadow":
1403
+ return "shadows";
1404
+ case "border":
1405
+ return "borders";
1406
+ case "duration":
1407
+ case "cubicBezier":
1408
+ case "transition":
1409
+ return "transitions";
1410
+ case "typography":
1411
+ return "typography";
1412
+ case "gradient":
1413
+ return "colors";
1414
+ case "strokeStyle":
1415
+ return "borders";
1416
+ case "number":
1417
+ return "other";
1418
+ default:
1419
+ return "other";
1420
+ }
1421
+ }
1422
+ function colorValueToCSS(value) {
1423
+ if (typeof value === "string") return value;
1424
+ if (value && typeof value === "object") {
1425
+ const obj = value;
1426
+ if (typeof obj.hex === "string") {
1427
+ if (obj.alpha !== void 0 && typeof obj.alpha === "number" && obj.alpha < 1) {
1428
+ const hex = obj.hex.replace("#", "");
1429
+ const r = parseInt(hex.substring(0, 2), 16);
1430
+ const g = parseInt(hex.substring(2, 4), 16);
1431
+ const b = parseInt(hex.substring(4, 6), 16);
1432
+ return `rgba(${r}, ${g}, ${b}, ${obj.alpha})`;
1433
+ }
1434
+ return obj.hex;
1435
+ }
1436
+ if (Array.isArray(obj.components)) {
1437
+ const comps = obj.components;
1438
+ if (comps.length >= 3) {
1439
+ const alpha = obj.alpha ?? (comps.length >= 4 ? comps[3] : 1);
1440
+ if (typeof alpha === "number" && alpha < 1) {
1441
+ return `rgba(${Math.round(comps[0] * 255)}, ${Math.round(comps[1] * 255)}, ${Math.round(comps[2] * 255)}, ${alpha})`;
1442
+ }
1443
+ if (comps.every((c) => c <= 1)) {
1444
+ const r = Math.round(comps[0] * 255);
1445
+ const g = Math.round(comps[1] * 255);
1446
+ const b = Math.round(comps[2] * 255);
1447
+ return `rgb(${r}, ${g}, ${b})`;
1448
+ }
1449
+ return `rgb(${comps[0]}, ${comps[1]}, ${comps[2]})`;
1450
+ }
1451
+ }
1452
+ }
1453
+ return String(value);
1454
+ }
1455
+ function dimensionValueToCSS(value) {
1456
+ if (typeof value === "string") return value;
1457
+ if (value && typeof value === "object") {
1458
+ const obj = value;
1459
+ if (typeof obj.value === "number" && typeof obj.unit === "string") {
1460
+ return `${obj.value}${obj.unit}`;
1461
+ }
1462
+ }
1463
+ return String(value);
1464
+ }
1465
+ function shadowValueToCSS(value) {
1466
+ if (typeof value === "string") return value;
1467
+ if (Array.isArray(value)) {
1468
+ return value.map((v) => shadowSingleToCSS(v)).join(", ");
1469
+ }
1470
+ return shadowSingleToCSS(value);
1471
+ }
1472
+ function shadowSingleToCSS(value) {
1473
+ if (typeof value === "string") return value;
1474
+ if (value && typeof value === "object") {
1475
+ const obj = value;
1476
+ const parts = [];
1477
+ if (obj.inset) parts.push("inset");
1478
+ parts.push(dimensionValueToCSS(obj.offsetX));
1479
+ parts.push(dimensionValueToCSS(obj.offsetY));
1480
+ parts.push(dimensionValueToCSS(obj.blur));
1481
+ if (obj.spread !== void 0) parts.push(dimensionValueToCSS(obj.spread));
1482
+ parts.push(colorValueToCSS(obj.color));
1483
+ return parts.join(" ");
1484
+ }
1485
+ return String(value);
1486
+ }
1487
+ function borderValueToCSS(value) {
1488
+ if (typeof value === "string") return value;
1489
+ if (value && typeof value === "object") {
1490
+ const obj = value;
1491
+ return `${dimensionValueToCSS(obj.width)} ${obj.style ?? "solid"} ${colorValueToCSS(obj.color)}`;
1492
+ }
1493
+ return String(value);
1494
+ }
1495
+ function typographyValueToCSS(value) {
1496
+ if (typeof value === "string") return value;
1497
+ if (value && typeof value === "object") {
1498
+ const obj = value;
1499
+ const parts = [];
1500
+ if (obj.fontWeight) parts.push(String(obj.fontWeight));
1501
+ if (obj.fontSize) parts.push(dimensionValueToCSS(obj.fontSize));
1502
+ if (obj.lineHeight) parts.push(`/ ${obj.lineHeight}`);
1503
+ if (obj.fontFamily) {
1504
+ const family = Array.isArray(obj.fontFamily) ? obj.fontFamily.join(", ") : String(obj.fontFamily);
1505
+ parts.push(family);
1506
+ }
1507
+ return parts.join(" ");
1508
+ }
1509
+ return String(value);
1510
+ }
1511
+ function cubicBezierValueToCSS(value) {
1512
+ if (typeof value === "string") return value;
1513
+ if (Array.isArray(value) && value.length === 4) {
1514
+ return `cubic-bezier(${value.join(", ")})`;
1515
+ }
1516
+ return String(value);
1517
+ }
1518
+ function transitionValueToCSS(value) {
1519
+ if (typeof value === "string") return value;
1520
+ if (value && typeof value === "object") {
1521
+ const obj = value;
1522
+ const parts = [];
1523
+ if (obj.duration) parts.push(String(obj.duration));
1524
+ if (obj.timingFunction) parts.push(cubicBezierValueToCSS(obj.timingFunction));
1525
+ if (obj.delay) parts.push(String(obj.delay));
1526
+ return parts.join(" ");
1527
+ }
1528
+ return String(value);
1529
+ }
1530
+ function gradientValueToCSS(value) {
1531
+ if (typeof value === "string") return value;
1532
+ if (Array.isArray(value)) {
1533
+ const stops = value.map((stop) => {
1534
+ if (stop && typeof stop === "object") {
1535
+ const s = stop;
1536
+ const color = colorValueToCSS(s.color);
1537
+ const position = typeof s.position === "number" ? ` ${s.position * 100}%` : "";
1538
+ return `${color}${position}`;
1539
+ }
1540
+ return String(stop);
1541
+ });
1542
+ return `linear-gradient(${stops.join(", ")})`;
1543
+ }
1544
+ return String(value);
1545
+ }
1546
+ function fontFamilyValueToCSS(value) {
1547
+ if (typeof value === "string") return value;
1548
+ if (Array.isArray(value)) return value.join(", ");
1549
+ return String(value);
1550
+ }
1551
+ function valueToCSS(type, value) {
1552
+ switch (type) {
1553
+ case "color":
1554
+ return colorValueToCSS(value);
1555
+ case "dimension":
1556
+ return dimensionValueToCSS(value);
1557
+ case "shadow":
1558
+ return shadowValueToCSS(value);
1559
+ case "border":
1560
+ return borderValueToCSS(value);
1561
+ case "typography":
1562
+ return typographyValueToCSS(value);
1563
+ case "cubicBezier":
1564
+ return cubicBezierValueToCSS(value);
1565
+ case "transition":
1566
+ return transitionValueToCSS(value);
1567
+ case "gradient":
1568
+ return gradientValueToCSS(value);
1569
+ case "fontFamily":
1570
+ return fontFamilyValueToCSS(value);
1571
+ case "fontWeight":
1572
+ return String(value);
1573
+ case "duration":
1574
+ return String(value);
1575
+ case "number":
1576
+ return String(value);
1577
+ case "strokeStyle":
1578
+ return typeof value === "string" ? value : String(value);
1579
+ default:
1580
+ return String(value);
1581
+ }
1582
+ }
1583
+ function isAlias(value) {
1584
+ return typeof value === "string" && /^\{.+\}$/.test(value);
1585
+ }
1586
+ function resolveAliasPath(alias) {
1587
+ return alias.slice(1, -1);
1588
+ }
1589
+ function resolveAlias(alias, root, visited, depth) {
1590
+ if (depth > MAX_ALIAS_DEPTH) {
1591
+ throw new Error(`Circular alias detected: ${alias} (max depth ${MAX_ALIAS_DEPTH} reached)`);
1592
+ }
1593
+ const path = resolveAliasPath(alias);
1594
+ if (visited.has(path)) {
1595
+ throw new Error(`Circular alias detected: ${[...visited, path].join(" \u2192 ")}`);
1596
+ }
1597
+ visited.add(path);
1598
+ const parts = path.split(".");
1599
+ let current = root;
1600
+ for (const part of parts) {
1601
+ if (current && typeof current === "object" && part in current) {
1602
+ current = current[part];
1603
+ } else {
1604
+ throw new Error(`Alias reference "${alias}" could not be resolved: "${part}" not found in path "${path}"`);
1605
+ }
1606
+ }
1607
+ if (current && typeof current === "object" && "$value" in current) {
1608
+ const resolvedValue = current.$value;
1609
+ if (isAlias(resolvedValue)) {
1610
+ return resolveAlias(resolvedValue, root, visited, depth + 1);
1611
+ }
1612
+ return resolvedValue;
1613
+ }
1614
+ if (isAlias(current)) {
1615
+ return resolveAlias(current, root, visited, depth + 1);
1616
+ }
1617
+ return current;
1618
+ }
1619
+ function resolveExtends(group, root, visited) {
1620
+ if (!group.$extends) return group;
1621
+ const extendsPath = group.$extends;
1622
+ if (visited.has(extendsPath)) {
1623
+ throw new Error(`Circular $extends detected: ${[...visited, extendsPath].join(" \u2192 ")}`);
1624
+ }
1625
+ visited.add(extendsPath);
1626
+ const parts = extendsPath.split(".");
1627
+ let parent = root;
1628
+ for (const part of parts) {
1629
+ if (parent && typeof parent === "object" && part in parent) {
1630
+ parent = parent[part];
1631
+ } else {
1632
+ throw new Error(`$extends reference "${extendsPath}" could not be resolved`);
1633
+ }
1634
+ }
1635
+ if (!parent || typeof parent !== "object") {
1636
+ throw new Error(`$extends target "${extendsPath}" is not a group`);
1637
+ }
1638
+ const resolvedParent = resolveExtends(parent, root, visited);
1639
+ const merged = { ...resolvedParent };
1640
+ for (const [key, value] of Object.entries(group)) {
1641
+ if (key === "$extends") continue;
1642
+ merged[key] = value;
1643
+ }
1644
+ return merged;
1645
+ }
1646
+ function walkTokenTree(node, root, path, inheritedType, tokens) {
1647
+ const resolved = resolveExtends(node, root, /* @__PURE__ */ new Set());
1648
+ const currentType = resolved.$type ?? inheritedType;
1649
+ for (const [key, child] of Object.entries(resolved)) {
1650
+ if (DTCG_META_KEYS.has(key)) continue;
1651
+ if (typeof child !== "object" || child === null) continue;
1652
+ const childObj = child;
1653
+ const childPath = [...path, key];
1654
+ if ("$value" in childObj) {
1655
+ const tokenType = childObj.$type ?? currentType;
1656
+ if (!tokenType) {
1657
+ continue;
1658
+ }
1659
+ let rawValue = childObj.$value;
1660
+ if (isAlias(rawValue)) {
1661
+ try {
1662
+ rawValue = resolveAlias(rawValue, root, /* @__PURE__ */ new Set(), 0);
1663
+ } catch {
1664
+ }
1665
+ }
1666
+ const cssValue = valueToCSS(tokenType, rawValue);
1667
+ tokens.push({
1668
+ path: childPath.join("."),
1669
+ type: tokenType,
1670
+ rawValue,
1671
+ cssValue,
1672
+ description: childObj.$description,
1673
+ deprecated: childObj.$deprecated,
1674
+ extensions: childObj.$extensions
1675
+ });
1676
+ } else {
1677
+ walkTokenTree(
1678
+ childObj,
1679
+ root,
1680
+ childPath,
1681
+ currentType,
1682
+ tokens
1683
+ );
1684
+ }
1685
+ }
1686
+ }
1687
+ function detectDTCGPrefix(tokens, root) {
1688
+ const extensions = root.$extensions;
1689
+ if (extensions) {
1690
+ const fragmentsExt = extensions["com.usefragments"];
1691
+ if (fragmentsExt?.prefix && typeof fragmentsExt.prefix === "string") {
1692
+ const p = fragmentsExt.prefix.replace(/-$/, "");
1693
+ return `--${p}-`;
1694
+ }
1695
+ }
1696
+ const topLevelKeys = Object.keys(root).filter((k) => !DTCG_META_KEYS.has(k));
1697
+ if (topLevelKeys.length === 1) {
1698
+ return `--${topLevelKeys[0]}-`;
1699
+ }
1700
+ if (tokens.length === 0) return "--";
1701
+ const firstParts = tokens[0].path.split(".");
1702
+ if (firstParts.length > 0) {
1703
+ return `--${firstParts[0]}-`;
1704
+ }
1705
+ return "--";
1706
+ }
1707
+ function tokenPathToCSSName(path, prefix) {
1708
+ const suffix = path.replace(/\./g, "-");
1709
+ const normalizedPrefix = prefix.endsWith("-") ? prefix : `${prefix}-`;
1710
+ const prefixBase = normalizedPrefix.replace(/^--/, "").replace(/-$/, "");
1711
+ if (suffix.startsWith(prefixBase + "-") || suffix === prefixBase) {
1712
+ return `--${suffix}`;
1713
+ }
1714
+ return `${normalizedPrefix}${suffix}`;
1715
+ }
1716
+ function parseDTCGFile(content, filePath) {
1717
+ const root = JSON.parse(content);
1718
+ const resolvedTokens = [];
1719
+ walkTokenTree(root, root, [], void 0, resolvedTokens);
1720
+ const prefix = detectDTCGPrefix(resolvedTokens, root);
1721
+ const categories = {};
1722
+ for (const token of resolvedTokens) {
1723
+ const category = mapDTCGTypeToCategory(token.type, token.path);
1724
+ const cssName = tokenPathToCSSName(token.path, prefix);
1725
+ const parsed = {
1726
+ name: cssName,
1727
+ value: token.cssValue,
1728
+ category,
1729
+ description: token.description
1730
+ };
1731
+ if (!categories[category]) {
1732
+ categories[category] = [];
1733
+ }
1734
+ categories[category].push(parsed);
1735
+ }
1736
+ return {
1737
+ prefix,
1738
+ categories,
1739
+ total: resolvedTokens.length
1740
+ };
1741
+ }
1742
+ var dtcgTokenTypeSchema = z2.enum([
1743
+ "color",
1744
+ "dimension",
1745
+ "fontFamily",
1746
+ "fontWeight",
1747
+ "duration",
1748
+ "cubicBezier",
1749
+ "number",
1750
+ "shadow",
1751
+ "border",
1752
+ "strokeStyle",
1753
+ "transition",
1754
+ "gradient",
1755
+ "typography"
1756
+ ]);
1757
+ var dtcgColorValueSchema = z2.union([
1758
+ z2.string(),
1759
+ z2.object({
1760
+ colorSpace: z2.string().default("srgb"),
1761
+ components: z2.array(z2.number()),
1762
+ hex: z2.string().optional(),
1763
+ alpha: z2.number().optional()
1764
+ })
1765
+ ]);
1766
+ var dtcgDimensionValueSchema = z2.union([
1767
+ z2.string(),
1768
+ z2.object({
1769
+ value: z2.number(),
1770
+ unit: z2.enum(["px", "rem"])
1771
+ })
1772
+ ]);
1773
+ var dtcgShadowValueSchema = z2.object({
1774
+ color: dtcgColorValueSchema,
1775
+ offsetX: dtcgDimensionValueSchema,
1776
+ offsetY: dtcgDimensionValueSchema,
1777
+ blur: dtcgDimensionValueSchema,
1778
+ spread: dtcgDimensionValueSchema.optional(),
1779
+ inset: z2.boolean().optional()
1780
+ });
1781
+ var dtcgBorderValueSchema = z2.object({
1782
+ color: dtcgColorValueSchema,
1783
+ width: dtcgDimensionValueSchema,
1784
+ style: z2.string()
1785
+ });
1786
+ var dtcgTypographyValueSchema = z2.object({
1787
+ fontFamily: z2.union([z2.string(), z2.array(z2.string())]),
1788
+ fontSize: dtcgDimensionValueSchema,
1789
+ fontWeight: z2.union([z2.number(), z2.string()]),
1790
+ letterSpacing: dtcgDimensionValueSchema.optional(),
1791
+ lineHeight: z2.union([z2.string(), z2.number()]).optional()
1792
+ });
1793
+ var dtcgTransitionValueSchema = z2.object({
1794
+ duration: z2.string(),
1795
+ delay: z2.string().optional(),
1796
+ timingFunction: z2.array(z2.number()).length(4)
1797
+ });
1798
+ var dtcgGradientStopSchema = z2.object({
1799
+ color: dtcgColorValueSchema,
1800
+ position: z2.number()
1801
+ });
1802
+ var dtcgCubicBezierValueSchema = z2.tuple([
1803
+ z2.number(),
1804
+ z2.number(),
1805
+ z2.number(),
1806
+ z2.number()
1807
+ ]);
1808
+ var dtcgTokenSchema = z2.object({
1809
+ $value: z2.unknown(),
1810
+ $type: dtcgTokenTypeSchema.optional(),
1811
+ $description: z2.string().optional(),
1812
+ $deprecated: z2.union([z2.boolean(), z2.string()]).optional(),
1813
+ $extensions: z2.record(z2.string(), z2.unknown()).optional()
1814
+ }).passthrough();
1815
+ var dtcgTokenFileSchema = z2.record(z2.string(), z2.unknown()).refine(
1816
+ (data) => {
1817
+ return data !== null && typeof data === "object" && !Array.isArray(data);
1818
+ },
1819
+ { message: "DTCG token file must be a JSON object" }
1820
+ );
1821
+ var contractPropSchema = z3.object({
1822
+ type: z3.string(),
1823
+ values: z3.array(z3.string()).optional(),
1824
+ default: z3.unknown().optional(),
1825
+ description: z3.string(),
1826
+ required: z3.boolean().optional(),
1827
+ constraints: z3.array(z3.string()).optional()
1828
+ });
1829
+ var contractUsageSchema = z3.object({
1830
+ when: z3.array(z3.string()),
1831
+ whenNot: z3.array(z3.string()),
1832
+ guidelines: z3.array(z3.string()).optional(),
1833
+ accessibility: z3.array(z3.string()).optional()
1834
+ });
1835
+ var contractExampleSchema = z3.object({
1836
+ name: z3.string(),
1837
+ description: z3.string(),
1838
+ code: z3.string(),
1839
+ args: z3.record(z3.string(), z3.unknown()).optional()
1840
+ });
1841
+ var contractRelationSchema = z3.object({
1842
+ component: z3.string(),
1843
+ relationship: z3.enum(["alternative", "parent", "child", "sibling", "composition", "complementary", "used-by"]),
1844
+ note: z3.string()
1845
+ });
1846
+ var contractContractSchema = z3.object({
1847
+ propsSummary: z3.array(z3.string()).optional(),
1848
+ scenarioTags: z3.array(z3.string()).optional(),
1849
+ a11yRules: z3.array(z3.string()).optional(),
1850
+ bans: z3.array(z3.object({
1851
+ pattern: z3.string(),
1852
+ message: z3.string()
1853
+ })).optional(),
1854
+ compoundChildren: z3.record(z3.string(), z3.object({
1855
+ required: z3.boolean().optional(),
1856
+ accepts: z3.array(z3.string()).optional(),
1857
+ description: z3.string().optional()
1858
+ })).optional(),
1859
+ canonicalUsage: z3.array(z3.string()).optional(),
1860
+ performanceBudget: z3.number().optional()
1861
+ });
1862
+ var contractAiSchema = z3.object({
1863
+ compositionPattern: z3.enum(["compound", "simple", "controlled", "wrapper"]).optional(),
1864
+ subComponents: z3.array(z3.string()).optional(),
1865
+ requiredChildren: z3.array(z3.string()).optional(),
1866
+ commonPatterns: z3.array(z3.string()).optional()
1867
+ });
1868
+ var contractPreviewSchema = z3.object({
1869
+ setupModule: z3.string().optional(),
1870
+ wrapperModule: z3.string().optional(),
1871
+ wrapperExport: z3.string().optional(),
1872
+ css: z3.array(z3.string()).optional(),
1873
+ theme: z3.enum(["light", "dark"]).optional()
1874
+ });
1875
+ var contractProvenanceSchema = z3.object({
1876
+ source: z3.enum(["manual", "extracted", "merged", "migrated"]),
1877
+ verified: z3.boolean(),
1878
+ frameworkSupport: z3.enum(["native", "manual-only"]).optional(),
1879
+ sourceHash: z3.string().optional(),
1880
+ extractedAt: z3.string().optional()
1881
+ });
1882
+ var contractFigmaSchema = z3.object({
1883
+ nodeUrl: z3.string().optional(),
1884
+ propMappings: z3.record(z3.string(), z3.object({
1885
+ type: z3.enum(["string", "boolean", "enum", "instance", "children", "textContent"]),
1886
+ figmaProperty: z3.string(),
1887
+ values: z3.record(z3.string(), z3.string()).optional()
1888
+ })).optional()
1889
+ });
1890
+ var componentContractSchema = z3.object({
1891
+ $schema: z3.string(),
1892
+ name: z3.string(),
1893
+ description: z3.string(),
1894
+ category: z3.string(),
1895
+ tags: z3.array(z3.string()).optional(),
1896
+ status: z3.enum(["stable", "beta", "deprecated", "experimental"]).optional(),
1897
+ framework: z3.enum(["react", "vue", "svelte", "web-components", "angular"]).optional(),
1898
+ sourcePath: z3.string(),
1899
+ exportName: z3.string(),
1900
+ propsSummary: z3.array(z3.string()),
1901
+ props: z3.record(z3.string(), contractPropSchema),
1902
+ usage: contractUsageSchema,
1903
+ examples: z3.array(contractExampleSchema).optional(),
1904
+ relations: z3.array(contractRelationSchema).optional(),
1905
+ contract: contractContractSchema.optional(),
1906
+ ai: contractAiSchema.optional(),
1907
+ preview: contractPreviewSchema.optional(),
1908
+ provenance: contractProvenanceSchema,
1909
+ tokens: z3.array(z3.string()).optional(),
1910
+ figma: contractFigmaSchema.optional()
1911
+ });
1912
+ function isContractFile(filePath) {
1913
+ return filePath.endsWith(".contract.json");
1914
+ }
1915
+ function parseComponentContract(content, filePath) {
1916
+ const raw = JSON.parse(content);
1917
+ const validated = componentContractSchema.parse(raw);
1918
+ return {
1919
+ filePath,
1920
+ meta: {
1921
+ name: validated.name,
1922
+ description: validated.description,
1923
+ category: validated.category,
1924
+ tags: validated.tags,
1925
+ status: validated.status,
1926
+ figma: validated.figma?.nodeUrl,
1927
+ figmaProps: validated.figma?.propMappings
1928
+ },
1929
+ usage: validated.usage,
1930
+ props: validated.props,
1931
+ relations: validated.relations ?? [],
1932
+ variants: (validated.examples ?? []).map((ex) => ({
1933
+ name: ex.name,
1934
+ description: ex.description,
1935
+ code: ex.code,
1936
+ args: ex.args
1937
+ })),
1938
+ contract: validated.contract ? {
1939
+ ...validated.contract,
1940
+ // Merge top-level propsSummary into contract.propsSummary if not already set
1941
+ propsSummary: validated.contract.propsSummary ?? validated.propsSummary
1942
+ } : {
1943
+ propsSummary: validated.propsSummary
1944
+ },
1945
+ framework: validated.framework,
1946
+ ai: validated.ai,
1947
+ propsSummary: validated.propsSummary,
1948
+ provenance: validated.provenance,
1949
+ sourcePath: validated.sourcePath,
1950
+ exportName: validated.exportName
1951
+ };
1952
+ }
1953
+ function parseToFlatTokens(tokens, prefix) {
1954
+ const parsed = parseDTCGFile(JSON.stringify(tokens), "tokens.tokens.json");
1955
+ const effectivePrefix = prefix ? `--${prefix.replace(/^--/, "").replace(/-$/, "")}-` : parsed.prefix;
1956
+ const result = [];
1957
+ for (const [category, categoryTokens] of Object.entries(parsed.categories)) {
1958
+ for (const token of categoryTokens) {
1959
+ let cssName = token.name;
1960
+ if (prefix && token.name.startsWith(parsed.prefix)) {
1961
+ cssName = effectivePrefix + token.name.slice(parsed.prefix.length);
1962
+ }
1963
+ result.push({
1964
+ cssName,
1965
+ cssValue: token.value ?? "",
1966
+ category,
1967
+ description: token.description
1968
+ });
1969
+ }
1970
+ }
1971
+ return result;
1972
+ }
1973
+ function generateCSSCustomProperties(tokens, options) {
1974
+ const selector = options?.selector ?? ":root";
1975
+ const flatTokens = parseToFlatTokens(tokens, options?.prefix);
1976
+ const lines = [];
1977
+ lines.push(`${selector} {`);
1978
+ const grouped = /* @__PURE__ */ new Map();
1979
+ for (const token of flatTokens) {
1980
+ const group = grouped.get(token.category) ?? [];
1981
+ group.push(token);
1982
+ grouped.set(token.category, group);
1983
+ }
1984
+ for (const [category, categoryTokens] of grouped) {
1985
+ lines.push(` /* ${category} */`);
1986
+ for (const token of categoryTokens) {
1987
+ if (token.description) {
1988
+ lines.push(` /* ${token.description} */`);
1989
+ }
1990
+ lines.push(` ${token.cssName}: ${token.cssValue};`);
1991
+ }
1992
+ lines.push("");
1993
+ }
1994
+ lines.push("}");
1995
+ return lines.join("\n");
1996
+ }
1997
+ function generateSCSSVariables(tokens, options) {
1998
+ const flatTokens = parseToFlatTokens(tokens, options?.prefix);
1999
+ const lines = [];
2000
+ lines.push("// Auto-generated from DTCG token file");
2001
+ lines.push("// Do not edit directly \u2014 modify the .tokens.json source");
2002
+ lines.push("");
2003
+ const grouped = /* @__PURE__ */ new Map();
2004
+ for (const token of flatTokens) {
2005
+ const group = grouped.get(token.category) ?? [];
2006
+ group.push(token);
2007
+ grouped.set(token.category, group);
2008
+ }
2009
+ for (const [category, categoryTokens] of grouped) {
2010
+ lines.push(`// ${category}`);
2011
+ for (const token of categoryTokens) {
2012
+ const scssName = token.cssName.replace(/^--/, "$");
2013
+ lines.push(`${scssName}: ${token.cssValue} !default;`);
2014
+ }
2015
+ lines.push("");
2016
+ }
2017
+ return lines.join("\n");
2018
+ }
2019
+ function generateTailwindConfig(tokens) {
2020
+ const flatTokens = parseToFlatTokens(tokens);
2021
+ const theme = {};
2022
+ for (const token of flatTokens) {
2023
+ const tailwindKey = categoryToTailwindKey(token.category);
2024
+ if (!tailwindKey) continue;
2025
+ if (!theme[tailwindKey]) {
2026
+ theme[tailwindKey] = {};
2027
+ }
2028
+ const parts = token.cssName.replace(/^--[\w]+-/, "").split("-");
2029
+ const tokenKey = parts.join("-") || token.cssName;
2030
+ theme[tailwindKey][tokenKey] = `var(${token.cssName})`;
2031
+ }
2032
+ return {
2033
+ theme: {
2034
+ extend: theme
2035
+ }
2036
+ };
2037
+ }
2038
+ function categoryToTailwindKey(category) {
2039
+ switch (category) {
2040
+ case "colors":
2041
+ case "surfaces":
2042
+ case "text":
2043
+ return "colors";
2044
+ case "spacing":
2045
+ return "spacing";
2046
+ case "radius":
2047
+ return "borderRadius";
2048
+ case "typography":
2049
+ return "fontFamily";
2050
+ case "shadows":
2051
+ return "boxShadow";
2052
+ case "borders":
2053
+ return "borderWidth";
2054
+ case "transitions":
2055
+ return "transitionDuration";
2056
+ case "z-index":
2057
+ return "zIndex";
2058
+ default:
2059
+ return void 0;
2060
+ }
2061
+ }
2062
+ function generateFigmaVariables(tokens) {
2063
+ const flatTokens = parseToFlatTokens(tokens);
2064
+ const collections = /* @__PURE__ */ new Map();
2065
+ for (const token of flatTokens) {
2066
+ const collectionName = categoryToFigmaCollection(token.category);
2067
+ const vars = collections.get(collectionName) ?? [];
2068
+ vars.push({
2069
+ name: token.cssName.replace(/^--/, "").replace(/-/g, "/"),
2070
+ type: categoryToFigmaType(token.category),
2071
+ value: parseFigmaValue(token.cssValue, token.category),
2072
+ description: token.description,
2073
+ scopes: categoryToFigmaScopes(token.category)
2074
+ });
2075
+ collections.set(collectionName, vars);
2076
+ }
2077
+ return Array.from(collections.entries()).map(([name, variables]) => ({
2078
+ name,
2079
+ modes: [{ name: "Default", modeId: "default" }],
2080
+ variables
2081
+ }));
2082
+ }
2083
+ function categoryToFigmaCollection(category) {
2084
+ switch (category) {
2085
+ case "colors":
2086
+ case "surfaces":
2087
+ case "text":
2088
+ return "Colors";
2089
+ case "spacing":
2090
+ case "radius":
2091
+ return "Dimensions";
2092
+ case "typography":
2093
+ return "Typography";
2094
+ case "shadows":
2095
+ return "Effects";
2096
+ default:
2097
+ return "Other";
2098
+ }
2099
+ }
2100
+ function categoryToFigmaType(category) {
2101
+ switch (category) {
2102
+ case "colors":
2103
+ case "surfaces":
2104
+ case "text":
2105
+ return "COLOR";
2106
+ case "spacing":
2107
+ case "radius":
2108
+ case "z-index":
2109
+ return "FLOAT";
2110
+ default:
2111
+ return "STRING";
2112
+ }
2113
+ }
2114
+ function categoryToFigmaScopes(category) {
2115
+ switch (category) {
2116
+ case "colors":
2117
+ return ["ALL_FILLS", "STROKE_COLOR"];
2118
+ case "surfaces":
2119
+ return ["FRAME_FILL", "SHAPE_FILL"];
2120
+ case "text":
2121
+ return ["TEXT_FILL"];
2122
+ case "spacing":
2123
+ return ["GAP", "WIDTH_HEIGHT"];
2124
+ case "radius":
2125
+ return ["CORNER_RADIUS"];
2126
+ default:
2127
+ return ["ALL_SCOPES"];
2128
+ }
2129
+ }
2130
+ function parseFigmaValue(cssValue, category) {
2131
+ if (category === "colors" || category === "surfaces" || category === "text") {
2132
+ const hexMatch = cssValue.match(/^#([0-9a-f]{6})$/i);
2133
+ if (hexMatch) {
2134
+ const hex = hexMatch[1];
2135
+ return {
2136
+ r: parseInt(hex.substring(0, 2), 16) / 255,
2137
+ g: parseInt(hex.substring(2, 4), 16) / 255,
2138
+ b: parseInt(hex.substring(4, 6), 16) / 255,
2139
+ a: 1
2140
+ };
2141
+ }
2142
+ }
2143
+ if (category === "spacing" || category === "radius") {
2144
+ const numMatch = cssValue.match(/^(\d+(?:\.\d+)?)/);
2145
+ if (numMatch) {
2146
+ return parseFloat(numMatch[1]);
2147
+ }
2148
+ }
2149
+ return cssValue;
2150
+ }
1169
2151
  var CATEGORY_AFFINITIES = {
1170
2152
  forms: ["feedback"],
1171
2153
  actions: ["feedback"]
@@ -1477,6 +2459,17 @@ export {
1477
2459
  classifyComplexity,
1478
2460
  formatBytes,
1479
2461
  budgetBar,
2462
+ DEFAULT_STYLE_PROPERTIES,
2463
+ DEFAULT_ENHANCED_STYLE_PROPERTIES,
2464
+ normalizeStyleValue,
2465
+ parseColor,
2466
+ compareColors,
2467
+ compareNumericValues,
2468
+ compareStyleValue,
2469
+ compareStyles,
2470
+ compareStylesWithTokens,
2471
+ formatTokenSummary,
2472
+ getComplianceBadge,
1480
2473
  figmaPropMappingSchema,
1481
2474
  fragmentMetaSchema,
1482
2475
  fragmentUsageSchema,
@@ -1516,6 +2509,16 @@ export {
1516
2509
  isFigmaPropMapping,
1517
2510
  resolveFigmaMapping,
1518
2511
  parseTokenFile,
2512
+ isDTCGFile,
2513
+ parseDTCGFile,
2514
+ dtcgTokenFileSchema,
2515
+ componentContractSchema,
2516
+ isContractFile,
2517
+ parseComponentContract,
2518
+ generateCSSCustomProperties,
2519
+ generateSCSSVariables,
2520
+ generateTailwindConfig,
2521
+ generateFigmaVariables,
1519
2522
  analyzeComposition,
1520
2523
  executeVariantLoaders,
1521
2524
  resolvePreviewRuntimeState,
@@ -1524,4 +2527,4 @@ export {
1524
2527
  isReactComponent,
1525
2528
  discoverComponents
1526
2529
  };
1527
- //# sourceMappingURL=chunk-I34BC3CU.js.map
2530
+ //# sourceMappingURL=chunk-32LIWN2P.js.map