@fragments-sdk/cli 0.14.3 → 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.
- package/README.md +0 -3
- package/dist/bin.js +4290 -3754
- package/dist/bin.js.map +1 -1
- package/dist/{chunk-TXFCEDOC.js → chunk-2WXKALIG.js} +2 -2
- package/dist/{chunk-I34BC3CU.js → chunk-32LIWN2P.js} +1006 -3
- package/dist/chunk-32LIWN2P.js.map +1 -0
- package/dist/{chunk-55KERLWL.js → chunk-65WSVDV5.js} +314 -89
- package/dist/chunk-65WSVDV5.js.map +1 -0
- package/dist/chunk-7DZC4YEV.js +294 -0
- package/dist/chunk-7DZC4YEV.js.map +1 -0
- package/dist/{chunk-LOYS64QS.js → chunk-7WHVW72L.js} +230 -19
- package/dist/chunk-7WHVW72L.js.map +1 -0
- package/dist/{chunk-PJT5IZ37.js → chunk-BJE3425I.js} +19 -52
- package/dist/{chunk-PJT5IZ37.js.map → chunk-BJE3425I.js.map} +1 -1
- package/dist/{chunk-5A6X2Y73.js → chunk-CZD3AD4Q.js} +12 -11
- package/dist/chunk-CZD3AD4Q.js.map +1 -0
- package/dist/{chunk-EYXVAMEX.js → chunk-MN3TJ3D5.js} +72 -3
- package/dist/chunk-MN3TJ3D5.js.map +1 -0
- package/dist/chunk-QCN35LJU.js +630 -0
- package/dist/chunk-QCN35LJU.js.map +1 -0
- package/dist/chunk-T47OLCSF.js +36 -0
- package/dist/chunk-T47OLCSF.js.map +1 -0
- package/dist/{chunk-APTQIBS5.js → chunk-XJQ5BIWI.js} +144 -1049
- package/dist/chunk-XJQ5BIWI.js.map +1 -0
- package/dist/codebase-scanner-VOTPXRYW.js +22 -0
- package/dist/converter-JLINP7CJ.js +34 -0
- package/dist/converter-JLINP7CJ.js.map +1 -0
- package/dist/core/index.js +43 -1
- package/dist/{generate-RYWIPDN2.js → generate-A4FP5426.js} +3 -4
- package/dist/{generate-RYWIPDN2.js.map → generate-A4FP5426.js.map} +1 -1
- package/dist/govern-scan-UCBZR6D6.js +280 -0
- package/dist/govern-scan-UCBZR6D6.js.map +1 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +11 -11
- package/dist/{init-WRUSW7R5.js → init-HGSM35XA.js} +131 -128
- package/dist/init-HGSM35XA.js.map +1 -0
- package/dist/{init-cloud-REQ3XLHO.js → init-cloud-MQ6GRJAZ.js} +2 -2
- package/dist/mcp-bin.js +5 -36
- package/dist/mcp-bin.js.map +1 -1
- package/dist/scan-VNNKACG2.js +15 -0
- package/dist/{scan-generate-TFZVL3BT.js → scan-generate-TWRHNU5M.js} +335 -46
- package/dist/scan-generate-TWRHNU5M.js.map +1 -0
- package/dist/scanner-7LAZYPWZ.js +13 -0
- package/dist/{service-HKJ6B7P7.js → service-FHQU7YS7.js} +27 -23
- package/dist/{snapshot-C5DYIGIV.js → snapshot-KQEQ6XHL.js} +2 -2
- package/dist/{static-viewer-DUVC4UIM.js → static-viewer-63PG6FWY.js} +3 -3
- package/dist/static-viewer-63PG6FWY.js.map +1 -0
- package/dist/{test-JW7JIDFG.js → test-UQYUCZIS.js} +4 -6
- package/dist/{test-JW7JIDFG.js.map → test-UQYUCZIS.js.map} +1 -1
- package/dist/{tokens-KE73G5JC.js → tokens-6GYKDV6U.js} +6 -5
- package/dist/{tokens-KE73G5JC.js.map → tokens-6GYKDV6U.js.map} +1 -1
- package/dist/tokens-generate-VTZV5EEW.js +86 -0
- package/dist/tokens-generate-VTZV5EEW.js.map +1 -0
- package/package.json +6 -6
- package/src/bin.ts +210 -48
- package/src/build.ts +130 -6
- package/src/commands/__fixtures__/shadcn-label-wrapper/package.json +7 -0
- package/src/commands/__fixtures__/shadcn-label-wrapper/src/components/ui/label.contract.json +42 -0
- package/src/commands/__fixtures__/shadcn-label-wrapper/src/components/ui/label.tsx +11 -0
- package/src/commands/__fixtures__/shadcn-label-wrapper/src/components/ui/primitive.contract.json +20 -0
- package/src/commands/__fixtures__/shadcn-label-wrapper/src/components/ui/primitive.tsx +14 -0
- package/src/commands/__fixtures__/shadcn-label-wrapper/tsconfig.app.json +23 -0
- package/src/commands/__tests__/init.test.ts +113 -0
- package/src/commands/__tests__/scan-generate.test.ts +188 -69
- package/src/commands/__tests__/verify.test.ts +91 -0
- package/src/commands/discover.ts +151 -0
- package/src/commands/enhance.ts +3 -1
- package/src/commands/govern-scan.ts +386 -0
- package/src/commands/govern.ts +2 -2
- package/src/commands/init.ts +152 -28
- package/src/commands/inspect.ts +290 -0
- package/src/commands/migrate-contract.ts +85 -0
- package/src/commands/scan-generate.ts +438 -50
- package/src/commands/scan.ts +1 -0
- package/src/commands/setup.ts +27 -50
- package/src/commands/tokens-generate.ts +113 -0
- package/src/commands/verify.ts +195 -1
- package/src/core/__fixtures__/shadcn-input/input.tsx +7 -0
- package/src/core/__fixtures__/shadcn-input/tsconfig.json +14 -0
- package/src/core/__fixtures__/shadcn-label/label.tsx +11 -0
- package/src/core/__fixtures__/shadcn-label/primitive.tsx +14 -0
- package/src/core/__fixtures__/shadcn-label/tsconfig.json +14 -0
- package/src/core/__fixtures__/shadcn-radix-label/label.tsx +11 -0
- package/src/core/__fixtures__/shadcn-radix-label/node_modules/radix-ui/index.d.ts +12 -0
- package/src/core/__fixtures__/shadcn-radix-label/tsconfig.json +14 -0
- package/src/core/__tests__/contract-parity.test.ts +316 -0
- package/src/core/component-extractor.test.ts +39 -0
- package/src/core/component-extractor.ts +92 -1
- package/src/core/config.ts +2 -1
- package/src/core/discovery.ts +13 -2
- package/src/core/drift-verifier.ts +123 -0
- package/src/core/extractor-adapter.ts +80 -0
- package/src/mcp/__tests__/projectFields.test.ts +1 -1
- package/src/mcp/utils.ts +1 -50
- package/src/migrate/converter.ts +3 -3
- package/src/migrate/fragment-to-contract.ts +253 -0
- package/src/migrate/report.ts +1 -1
- package/src/scripts/token-benchmark.ts +121 -0
- package/src/service/__tests__/props-extractor.test.ts +94 -0
- package/src/service/__tests__/token-normalizer.test.ts +690 -0
- package/src/service/ast-utils.ts +4 -23
- package/src/service/babel-config.ts +23 -0
- package/src/service/enhance/converter.ts +61 -0
- package/src/service/enhance/props-extractor.ts +25 -8
- package/src/service/enhance/scanner.ts +5 -24
- package/src/service/snippet-validation.ts +9 -3
- package/src/service/token-normalizer.ts +510 -0
- package/src/shared/index.ts +1 -0
- package/src/shared/project-fields.ts +46 -0
- package/src/viewer/__tests__/viewer-integration.test.ts +8 -8
- package/src/viewer/preview-adapter.ts +116 -0
- package/src/viewer/style-utils.ts +27 -412
- package/src/viewer/vite-plugin.ts +2 -2
- package/dist/chunk-55KERLWL.js.map +0 -1
- package/dist/chunk-5A6X2Y73.js.map +0 -1
- package/dist/chunk-APTQIBS5.js.map +0 -1
- package/dist/chunk-EYXVAMEX.js.map +0 -1
- package/dist/chunk-I34BC3CU.js.map +0 -1
- package/dist/chunk-LOYS64QS.js.map +0 -1
- package/dist/chunk-ZKTFKHWN.js +0 -324
- package/dist/chunk-ZKTFKHWN.js.map +0 -1
- package/dist/discovery-VDANZAJ2.js +0 -28
- package/dist/init-WRUSW7R5.js.map +0 -1
- package/dist/scan-YJHQIRKG.js +0 -14
- package/dist/scan-generate-TFZVL3BT.js.map +0 -1
- package/dist/viewer-2TZS3NDL.js +0 -2730
- package/dist/viewer-2TZS3NDL.js.map +0 -1
- package/src/commands/dev.ts +0 -107
- /package/dist/{chunk-TXFCEDOC.js.map → chunk-2WXKALIG.js.map} +0 -0
- /package/dist/{discovery-VDANZAJ2.js.map → codebase-scanner-VOTPXRYW.js.map} +0 -0
- /package/dist/{init-cloud-REQ3XLHO.js.map → init-cloud-MQ6GRJAZ.js.map} +0 -0
- /package/dist/{scan-YJHQIRKG.js.map → scan-VNNKACG2.js.map} +0 -0
- /package/dist/{service-HKJ6B7P7.js.map → scanner-7LAZYPWZ.js.map} +0 -0
- /package/dist/{static-viewer-DUVC4UIM.js.map → service-FHQU7YS7.js.map} +0 -0
- /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
|
|
21
|
-
fileExtension: ".
|
|
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-
|
|
2530
|
+
//# sourceMappingURL=chunk-32LIWN2P.js.map
|