@fragments-sdk/core 0.1.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/LICENSE +84 -0
- package/dist/index.d.ts +2873 -0
- package/dist/index.js +1431 -0
- package/dist/index.js.map +1 -0
- package/package.json +75 -0
- package/src/__tests__/preview-runtime.test.tsx +111 -0
- package/src/composition.test.ts +262 -0
- package/src/composition.ts +318 -0
- package/src/constants.ts +114 -0
- package/src/context.ts +2 -0
- package/src/defineFragment.ts +141 -0
- package/src/figma.ts +263 -0
- package/src/fragment-types.ts +214 -0
- package/src/index.ts +207 -0
- package/src/performance-presets.ts +142 -0
- package/src/preview-runtime.tsx +144 -0
- package/src/schema.ts +229 -0
- package/src/storyAdapter.test.ts +571 -0
- package/src/storyAdapter.ts +761 -0
- package/src/storyFilters.test.ts +350 -0
- package/src/storyFilters.ts +253 -0
- package/src/storybook-csf.ts +11 -0
- package/src/token-parser.ts +321 -0
- package/src/token-types.ts +287 -0
- package/src/types.ts +784 -0
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Token Parser — extracts CSS custom property declarations from SCSS/CSS files.
|
|
3
|
+
*
|
|
4
|
+
* Parses files for `--prefix-*: value;` declarations and groups them
|
|
5
|
+
* by SCSS comment sections (e.g., `// Typography`, `// Colors`).
|
|
6
|
+
* Falls back to naming-convention-based categorization when comments
|
|
7
|
+
* are absent.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export interface ParsedToken {
|
|
11
|
+
/** Full CSS variable name (e.g., "--fui-color-accent") */
|
|
12
|
+
name: string;
|
|
13
|
+
/** Raw value from the declaration (e.g., "#{$fui-space-4}" or "16px") */
|
|
14
|
+
value?: string;
|
|
15
|
+
/** Resolved value after SCSS variable substitution (e.g., "16px") */
|
|
16
|
+
resolvedValue?: string;
|
|
17
|
+
/** Category inferred from SCSS comment or naming convention */
|
|
18
|
+
category: string;
|
|
19
|
+
/** Description from inline comment, if any */
|
|
20
|
+
description?: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface TokenParseOutput {
|
|
24
|
+
/** Detected prefix (e.g., "--fui-") */
|
|
25
|
+
prefix: string;
|
|
26
|
+
/** Tokens grouped by category */
|
|
27
|
+
categories: Record<string, ParsedToken[]>;
|
|
28
|
+
/** Total number of tokens found */
|
|
29
|
+
total: number;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Category inference from naming conventions.
|
|
34
|
+
* Order matters — first match wins.
|
|
35
|
+
*/
|
|
36
|
+
const NAMING_RULES: Array<{ pattern: RegExp; category: string }> = [
|
|
37
|
+
{ pattern: /--\w+-font-/, category: 'typography' },
|
|
38
|
+
{ pattern: /--\w+-line-height-/, category: 'typography' },
|
|
39
|
+
{ pattern: /--\w+-space-/, category: 'spacing' },
|
|
40
|
+
{ pattern: /--\w+-padding-/, category: 'spacing' },
|
|
41
|
+
{ pattern: /--\w+-radius-/, category: 'radius' },
|
|
42
|
+
{ pattern: /--\w+-color-/, category: 'colors' },
|
|
43
|
+
{ pattern: /--\w+-bg-/, category: 'surfaces' },
|
|
44
|
+
{ pattern: /--\w+-text-/, category: 'text' },
|
|
45
|
+
{ pattern: /--\w+-border/, category: 'borders' },
|
|
46
|
+
{ pattern: /--\w+-shadow-/, category: 'shadows' },
|
|
47
|
+
{ pattern: /--\w+-focus-/, category: 'focus' },
|
|
48
|
+
{ pattern: /--\w+-transition-/, category: 'transitions' },
|
|
49
|
+
{ pattern: /--\w+-scrollbar-/, category: 'scrollbar' },
|
|
50
|
+
{ pattern: /--\w+-z-index/, category: 'z-index' },
|
|
51
|
+
{ pattern: /--\w+-(button|input|touch)-/, category: 'component-sizing' },
|
|
52
|
+
{ pattern: /--\w+-appshell-/, category: 'layout' },
|
|
53
|
+
{ pattern: /--\w+-header-/, category: 'layout' },
|
|
54
|
+
{ pattern: /--\w+-code-/, category: 'code' },
|
|
55
|
+
{ pattern: /--\w+-tooltip-/, category: 'tooltip' },
|
|
56
|
+
{ pattern: /--\w+-hero-/, category: 'marketing' },
|
|
57
|
+
];
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Infer category from a CSS variable name using naming conventions.
|
|
61
|
+
*/
|
|
62
|
+
function inferCategory(name: string): string {
|
|
63
|
+
for (const rule of NAMING_RULES) {
|
|
64
|
+
if (rule.pattern.test(name)) {
|
|
65
|
+
return rule.category;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return 'other';
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Detect the most common prefix from a list of CSS variable names.
|
|
73
|
+
* E.g., given ["--fui-color-accent", "--fui-bg-primary"] → "--fui-"
|
|
74
|
+
*/
|
|
75
|
+
function detectPrefix(names: string[]): string {
|
|
76
|
+
if (names.length === 0) return '--';
|
|
77
|
+
|
|
78
|
+
// Find common prefix after "--"
|
|
79
|
+
const stripped = names.map((n) => n.slice(2)); // remove "--"
|
|
80
|
+
let prefix = '';
|
|
81
|
+
const first = stripped[0];
|
|
82
|
+
|
|
83
|
+
for (let i = 0; i < first.length; i++) {
|
|
84
|
+
const ch = first[i];
|
|
85
|
+
if (stripped.every((s) => s[i] === ch)) {
|
|
86
|
+
prefix += ch;
|
|
87
|
+
} else {
|
|
88
|
+
break;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Trim to last hyphen to get clean prefix
|
|
93
|
+
const lastHyphen = prefix.lastIndexOf('-');
|
|
94
|
+
if (lastHyphen > 0) {
|
|
95
|
+
prefix = prefix.slice(0, lastHyphen + 1);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return `--${prefix}`;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Normalize a SCSS comment into a category name.
|
|
103
|
+
* "// Typography" → "typography"
|
|
104
|
+
* "// Component heights" → "component-sizing"
|
|
105
|
+
* "// Hero/Marketing gradient" → "marketing"
|
|
106
|
+
*/
|
|
107
|
+
function normalizeCategory(comment: string): string {
|
|
108
|
+
const text = comment
|
|
109
|
+
.trim()
|
|
110
|
+
.replace(/^\/\/\s*/, '')
|
|
111
|
+
.replace(/^\/\*+\s*/, '')
|
|
112
|
+
.replace(/\s*\*+\/$/, '')
|
|
113
|
+
.trim()
|
|
114
|
+
.toLowerCase();
|
|
115
|
+
|
|
116
|
+
// Map common comment headings to clean category names
|
|
117
|
+
const mappings: Record<string, string> = {
|
|
118
|
+
'base configuration': 'base',
|
|
119
|
+
'typography': 'typography',
|
|
120
|
+
'spacing (micro)': 'spacing',
|
|
121
|
+
'spacing': 'spacing',
|
|
122
|
+
'density padding': 'spacing',
|
|
123
|
+
'border radius': 'radius',
|
|
124
|
+
'transitions': 'transitions',
|
|
125
|
+
'colors': 'colors',
|
|
126
|
+
'surfaces': 'surfaces',
|
|
127
|
+
'text': 'text',
|
|
128
|
+
'borders': 'borders',
|
|
129
|
+
'shadows': 'shadows',
|
|
130
|
+
'focus': 'focus',
|
|
131
|
+
'scrollbar': 'scrollbar',
|
|
132
|
+
'component heights': 'component-sizing',
|
|
133
|
+
'appshell layout': 'layout',
|
|
134
|
+
'codeblock': 'code',
|
|
135
|
+
'tooltip': 'tooltip',
|
|
136
|
+
'hero/marketing gradient': 'marketing',
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
return mappings[text] ?? text.replace(/\s+/g, '-');
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Extract SCSS variable declarations ($name: value;) from file content.
|
|
144
|
+
* Returns a map of variable name → value.
|
|
145
|
+
*/
|
|
146
|
+
function extractScssVariables(content: string): Map<string, string> {
|
|
147
|
+
const vars = new Map<string, string>();
|
|
148
|
+
// Match: $var-name: value; (handles multi-word values, stops at semicolon)
|
|
149
|
+
const scssVarRegex = /^\s*(\$[\w-]+)\s*:\s*(.+?)\s*(?:!default\s*)?;/gm;
|
|
150
|
+
|
|
151
|
+
let match: RegExpExecArray | null;
|
|
152
|
+
while ((match = scssVarRegex.exec(content)) !== null) {
|
|
153
|
+
const name = match[1];
|
|
154
|
+
const value = match[2].replace(/\s*\/\/.*$/, '').trim();
|
|
155
|
+
// Only store the first occurrence (canonical definition)
|
|
156
|
+
if (!vars.has(name)) {
|
|
157
|
+
vars.set(name, value);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return vars;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Resolve SCSS interpolations and variable references in a token value.
|
|
166
|
+
*
|
|
167
|
+
* Handles:
|
|
168
|
+
* - `#{$var}` → looks up $var in scssVars map
|
|
169
|
+
* - `$var` standalone → looks up in scssVars map
|
|
170
|
+
* - `var(--other-token, fallback)` → returns fallback if provided
|
|
171
|
+
* - Recursive resolution up to 5 levels deep
|
|
172
|
+
*/
|
|
173
|
+
function resolveTokenValue(
|
|
174
|
+
rawValue: string,
|
|
175
|
+
scssVars: Map<string, string>,
|
|
176
|
+
cssVarValues: Map<string, string>,
|
|
177
|
+
depth = 0
|
|
178
|
+
): string {
|
|
179
|
+
if (depth > 5) return rawValue; // Prevent infinite recursion
|
|
180
|
+
|
|
181
|
+
let resolved = rawValue;
|
|
182
|
+
|
|
183
|
+
// Resolve #{$var} interpolations
|
|
184
|
+
resolved = resolved.replace(/#\{(\$[\w-]+)\}/g, (_, varName) => {
|
|
185
|
+
const val = scssVars.get(varName);
|
|
186
|
+
return val !== undefined
|
|
187
|
+
? resolveTokenValue(val, scssVars, cssVarValues, depth + 1)
|
|
188
|
+
: `#{${varName}}`;
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
// Resolve standalone $var references (not inside #{})
|
|
192
|
+
resolved = resolved.replace(/(?<![#\{])(\$[\w-]+)/g, (_, varName) => {
|
|
193
|
+
const val = scssVars.get(varName);
|
|
194
|
+
return val !== undefined
|
|
195
|
+
? resolveTokenValue(val, scssVars, cssVarValues, depth + 1)
|
|
196
|
+
: varName;
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
// Resolve var(--token, fallback) — use the referenced token value or fallback
|
|
200
|
+
resolved = resolved.replace(
|
|
201
|
+
/var\((--[\w-]+)(?:\s*,\s*(.+?))?\)/g,
|
|
202
|
+
(original, tokenName, fallback) => {
|
|
203
|
+
const tokenVal = cssVarValues.get(tokenName);
|
|
204
|
+
if (tokenVal !== undefined) {
|
|
205
|
+
return resolveTokenValue(tokenVal, scssVars, cssVarValues, depth + 1);
|
|
206
|
+
}
|
|
207
|
+
if (fallback) {
|
|
208
|
+
return resolveTokenValue(fallback.trim(), scssVars, cssVarValues, depth + 1);
|
|
209
|
+
}
|
|
210
|
+
return original;
|
|
211
|
+
}
|
|
212
|
+
);
|
|
213
|
+
|
|
214
|
+
return resolved;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Parse a SCSS or CSS file and extract CSS custom property declarations.
|
|
219
|
+
*
|
|
220
|
+
* Handles two grouping strategies:
|
|
221
|
+
* 1. Comment-based: Uses `// Category` comments above groups of declarations
|
|
222
|
+
* 2. Naming-based: Falls back to inferring category from variable name patterns
|
|
223
|
+
*
|
|
224
|
+
* Also resolves SCSS variable interpolations (e.g., `#{$fui-space-4}` → `16px`)
|
|
225
|
+
* when the SCSS variable definitions are found in the same file content.
|
|
226
|
+
*/
|
|
227
|
+
export function parseTokenFile(content: string, filePath: string): TokenParseOutput {
|
|
228
|
+
const lines = content.split('\n');
|
|
229
|
+
const tokens: ParsedToken[] = [];
|
|
230
|
+
const seenNames = new Set<string>();
|
|
231
|
+
let currentCategory = 'other';
|
|
232
|
+
let hasCommentCategories = false;
|
|
233
|
+
|
|
234
|
+
// First pass: extract SCSS variable declarations for resolution
|
|
235
|
+
const scssVars = extractScssVariables(content);
|
|
236
|
+
|
|
237
|
+
// Regex for CSS custom property declarations
|
|
238
|
+
// Matches: --name: value; (with optional SCSS interpolation)
|
|
239
|
+
// Captures both the variable name and its value
|
|
240
|
+
const varDeclRegex = /^\s*(--[\w-]+)\s*:\s*(.+?)\s*;/;
|
|
241
|
+
// Regex for section comments (// Category or /* Category */)
|
|
242
|
+
// Allow any characters after uppercase start (including / for "Hero/Marketing")
|
|
243
|
+
const sectionCommentRegex = /^\s*\/\/\s+([A-Z].+)$/;
|
|
244
|
+
|
|
245
|
+
for (const line of lines) {
|
|
246
|
+
// Check for section comment
|
|
247
|
+
const commentMatch = line.match(sectionCommentRegex);
|
|
248
|
+
if (commentMatch) {
|
|
249
|
+
const normalized = normalizeCategory(commentMatch[0]);
|
|
250
|
+
if (normalized) {
|
|
251
|
+
currentCategory = normalized;
|
|
252
|
+
hasCommentCategories = true;
|
|
253
|
+
}
|
|
254
|
+
continue;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Check for CSS variable declaration
|
|
258
|
+
const varMatch = line.match(varDeclRegex);
|
|
259
|
+
if (varMatch) {
|
|
260
|
+
const name = varMatch[1];
|
|
261
|
+
const rawValue = varMatch[2];
|
|
262
|
+
|
|
263
|
+
// Deduplicate: keep only the first occurrence of each variable.
|
|
264
|
+
// Dark mode and high contrast blocks redefine the same variables
|
|
265
|
+
// with different values — we only want the canonical list.
|
|
266
|
+
if (seenNames.has(name)) continue;
|
|
267
|
+
seenNames.add(name);
|
|
268
|
+
|
|
269
|
+
// Extract inline comment if present
|
|
270
|
+
const inlineComment = line.match(/\/\/\s*(.+)$/);
|
|
271
|
+
const description = inlineComment ? inlineComment[1].trim() : undefined;
|
|
272
|
+
|
|
273
|
+
// Clean the value: strip trailing inline comments
|
|
274
|
+
const cleanValue = rawValue.replace(/\s*\/\/.*$/, '').trim();
|
|
275
|
+
|
|
276
|
+
tokens.push({
|
|
277
|
+
name,
|
|
278
|
+
value: cleanValue || undefined,
|
|
279
|
+
category: hasCommentCategories ? currentCategory : inferCategory(name),
|
|
280
|
+
description,
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// Second pass: build a CSS custom property → raw value map for cross-references
|
|
286
|
+
const cssVarValues = new Map<string, string>();
|
|
287
|
+
for (const token of tokens) {
|
|
288
|
+
if (token.value) {
|
|
289
|
+
cssVarValues.set(token.name, token.value);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// Third pass: resolve SCSS interpolations and var() references
|
|
294
|
+
for (const token of tokens) {
|
|
295
|
+
if (token.value) {
|
|
296
|
+
const resolved = resolveTokenValue(token.value, scssVars, cssVarValues);
|
|
297
|
+
// Only set resolvedValue if it's different from raw and doesn't still contain unresolved refs
|
|
298
|
+
if (resolved !== token.value && !resolved.includes('#{') && !resolved.includes('$')) {
|
|
299
|
+
token.resolvedValue = resolved;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Group by category
|
|
305
|
+
const categories: Record<string, ParsedToken[]> = {};
|
|
306
|
+
for (const token of tokens) {
|
|
307
|
+
if (!categories[token.category]) {
|
|
308
|
+
categories[token.category] = [];
|
|
309
|
+
}
|
|
310
|
+
categories[token.category].push(token);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// Detect prefix
|
|
314
|
+
const prefix = detectPrefix(tokens.map((t) => t.name));
|
|
315
|
+
|
|
316
|
+
return {
|
|
317
|
+
prefix,
|
|
318
|
+
categories,
|
|
319
|
+
total: tokens.length,
|
|
320
|
+
};
|
|
321
|
+
}
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Design Token Types for Fragments
|
|
3
|
+
*
|
|
4
|
+
* These types define the structure for CSS custom property (CSS variable) discovery,
|
|
5
|
+
* parsing, and reverse lookup capabilities. The token system enables:
|
|
6
|
+
*
|
|
7
|
+
* 1. Automatic discovery of design tokens from CSS/SCSS files
|
|
8
|
+
* 2. Reverse lookup: given a computed value, find which token(s) produce it
|
|
9
|
+
* 3. Detection of hardcoded values vs token usage
|
|
10
|
+
* 4. AI-friendly fix suggestions
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Token categories for grouping and filtering
|
|
15
|
+
*/
|
|
16
|
+
export type TokenCategory =
|
|
17
|
+
| "color"
|
|
18
|
+
| "spacing"
|
|
19
|
+
| "typography"
|
|
20
|
+
| "radius"
|
|
21
|
+
| "shadow"
|
|
22
|
+
| "sizing"
|
|
23
|
+
| "border"
|
|
24
|
+
| "animation"
|
|
25
|
+
| "z-index"
|
|
26
|
+
| "other";
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* A single design token (CSS custom property)
|
|
30
|
+
*/
|
|
31
|
+
export interface DesignToken {
|
|
32
|
+
/** Token name with leading dashes (e.g., "--color-primary") */
|
|
33
|
+
name: string;
|
|
34
|
+
|
|
35
|
+
/** Raw value as written in CSS (e.g., "var(--color-cobalt-50)") */
|
|
36
|
+
rawValue: string;
|
|
37
|
+
|
|
38
|
+
/** Fully resolved value (e.g., "#0051c2") */
|
|
39
|
+
resolvedValue: string;
|
|
40
|
+
|
|
41
|
+
/** Inferred category based on naming convention */
|
|
42
|
+
category: TokenCategory;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Token level in the design system hierarchy:
|
|
46
|
+
* - 1 = Base/primitive tokens (raw values like colors, sizes)
|
|
47
|
+
* - 2 = Semantic tokens (references to base tokens with meaning)
|
|
48
|
+
* - 3 = Component tokens (component-specific tokens)
|
|
49
|
+
*/
|
|
50
|
+
level: 1 | 2 | 3;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Reference chain showing how the value was resolved
|
|
54
|
+
* e.g., ["--color-primary", "--color-cobalt-50"] means
|
|
55
|
+
* --color-primary references --color-cobalt-50
|
|
56
|
+
*/
|
|
57
|
+
referenceChain: string[];
|
|
58
|
+
|
|
59
|
+
/** Source file where this token was defined */
|
|
60
|
+
sourceFile: string;
|
|
61
|
+
|
|
62
|
+
/** Line number in source file */
|
|
63
|
+
lineNumber?: number;
|
|
64
|
+
|
|
65
|
+
/** Theme this token belongs to (e.g., "default", "dark", "light") */
|
|
66
|
+
theme: string;
|
|
67
|
+
|
|
68
|
+
/** CSS selector where this token is defined (e.g., ":root", "[data-theme='dark']") */
|
|
69
|
+
selector: string;
|
|
70
|
+
|
|
71
|
+
/** Optional description from comments */
|
|
72
|
+
description?: string;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Token registry for fast lookups
|
|
77
|
+
*/
|
|
78
|
+
export interface TokenRegistry {
|
|
79
|
+
/** Lookup by token name (e.g., "--color-primary") */
|
|
80
|
+
byName: Map<string, DesignToken>;
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* REVERSE lookup: resolved value -> token names
|
|
84
|
+
* Key is normalized value (e.g., "#0051c2" lowercase)
|
|
85
|
+
* Value is array of token names that resolve to this value
|
|
86
|
+
*/
|
|
87
|
+
byValue: Map<string, string[]>;
|
|
88
|
+
|
|
89
|
+
/** Tokens grouped by theme */
|
|
90
|
+
byTheme: Map<string, DesignToken[]>;
|
|
91
|
+
|
|
92
|
+
/** Tokens grouped by category */
|
|
93
|
+
byCategory: Map<TokenCategory, DesignToken[]>;
|
|
94
|
+
|
|
95
|
+
/** Registry metadata */
|
|
96
|
+
meta: TokenRegistryMeta;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Token registry metadata
|
|
101
|
+
*/
|
|
102
|
+
export interface TokenRegistryMeta {
|
|
103
|
+
/** When tokens were discovered */
|
|
104
|
+
discoveredAt: Date;
|
|
105
|
+
|
|
106
|
+
/** Source files that were parsed */
|
|
107
|
+
sourceFiles: string[];
|
|
108
|
+
|
|
109
|
+
/** Total number of tokens discovered */
|
|
110
|
+
totalTokens: number;
|
|
111
|
+
|
|
112
|
+
/** Time taken to parse (ms) */
|
|
113
|
+
parseTimeMs: number;
|
|
114
|
+
|
|
115
|
+
/** Number of circular references detected */
|
|
116
|
+
circularRefs: number;
|
|
117
|
+
|
|
118
|
+
/** Number of unresolved references */
|
|
119
|
+
unresolvedRefs: number;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Enhanced style diff item with token information
|
|
124
|
+
*/
|
|
125
|
+
export interface EnhancedStyleDiffItem {
|
|
126
|
+
/** CSS property name (e.g., "backgroundColor") */
|
|
127
|
+
property: string;
|
|
128
|
+
|
|
129
|
+
/** Value from Figma design */
|
|
130
|
+
figma: string;
|
|
131
|
+
|
|
132
|
+
/** Value from rendered component */
|
|
133
|
+
rendered: string;
|
|
134
|
+
|
|
135
|
+
/** Whether values match (within tolerance) */
|
|
136
|
+
match: boolean;
|
|
137
|
+
|
|
138
|
+
/** Token name if Figma value matches a known token */
|
|
139
|
+
figmaToken?: string;
|
|
140
|
+
|
|
141
|
+
/** Token name if rendered value uses a token */
|
|
142
|
+
renderedToken?: string;
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* True if rendered value doesn't use a token but should
|
|
146
|
+
* (i.e., Figma uses a token but code uses hardcoded value)
|
|
147
|
+
*/
|
|
148
|
+
isHardcoded: boolean;
|
|
149
|
+
|
|
150
|
+
/** Suggested fix if hardcoded */
|
|
151
|
+
suggestedFix?: TokenFix;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Token-based fix suggestion
|
|
156
|
+
*/
|
|
157
|
+
export interface TokenFix {
|
|
158
|
+
/** Token name to use (e.g., "--color-primary") */
|
|
159
|
+
tokenName: string;
|
|
160
|
+
|
|
161
|
+
/** Token's resolved value */
|
|
162
|
+
tokenValue: string;
|
|
163
|
+
|
|
164
|
+
/** Code snippet to fix the issue */
|
|
165
|
+
codeFix: string;
|
|
166
|
+
|
|
167
|
+
/** Confidence score 0-1 */
|
|
168
|
+
confidence: number;
|
|
169
|
+
|
|
170
|
+
/** Human-readable explanation */
|
|
171
|
+
reason: string;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Configuration for token discovery
|
|
176
|
+
*/
|
|
177
|
+
export interface TokenConfig {
|
|
178
|
+
/**
|
|
179
|
+
* Glob patterns for files to scan for tokens
|
|
180
|
+
* e.g., ["src/styles/theme.scss", "src/styles/variables.css"]
|
|
181
|
+
*/
|
|
182
|
+
include: string[];
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Glob patterns to exclude
|
|
186
|
+
* @example ["node_modules"]
|
|
187
|
+
*/
|
|
188
|
+
exclude?: string[];
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Map CSS selectors to theme names
|
|
192
|
+
* @example { ":root": "default", "[data-theme='dark']": "dark" }
|
|
193
|
+
*/
|
|
194
|
+
themeSelectors?: Record<string, string>;
|
|
195
|
+
|
|
196
|
+
/** Enable token comparison in style diffs (default: true) */
|
|
197
|
+
enabled?: boolean;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Result of parsing a CSS/SCSS file for tokens
|
|
202
|
+
*/
|
|
203
|
+
export interface TokenParseResult {
|
|
204
|
+
/** Tokens discovered in the file */
|
|
205
|
+
tokens: DesignToken[];
|
|
206
|
+
|
|
207
|
+
/** Errors encountered during parsing */
|
|
208
|
+
errors: TokenParseError[];
|
|
209
|
+
|
|
210
|
+
/** Warnings (non-fatal issues) */
|
|
211
|
+
warnings: string[];
|
|
212
|
+
|
|
213
|
+
/** Parse time in ms */
|
|
214
|
+
parseTimeMs: number;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Error during token parsing
|
|
219
|
+
*/
|
|
220
|
+
export interface TokenParseError {
|
|
221
|
+
/** Error message */
|
|
222
|
+
message: string;
|
|
223
|
+
|
|
224
|
+
/** File where error occurred */
|
|
225
|
+
file: string;
|
|
226
|
+
|
|
227
|
+
/** Line number if known */
|
|
228
|
+
line?: number;
|
|
229
|
+
|
|
230
|
+
/** The problematic content if available */
|
|
231
|
+
content?: string;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Request to match a value to tokens
|
|
236
|
+
*/
|
|
237
|
+
export interface TokenMatchRequest {
|
|
238
|
+
/** The value to find tokens for (e.g., "#0051c2") */
|
|
239
|
+
value: string;
|
|
240
|
+
|
|
241
|
+
/** Property type hint for better matching (e.g., "color") */
|
|
242
|
+
propertyType?: "color" | "spacing" | "typography" | "other";
|
|
243
|
+
|
|
244
|
+
/** Specific theme to search in */
|
|
245
|
+
theme?: string;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Result of token matching
|
|
250
|
+
*/
|
|
251
|
+
export interface TokenMatchResult {
|
|
252
|
+
/** Exact matches (same resolved value) */
|
|
253
|
+
exactMatches: DesignToken[];
|
|
254
|
+
|
|
255
|
+
/** Close matches (similar value, useful for colors) */
|
|
256
|
+
closeMatches: Array<{
|
|
257
|
+
token: DesignToken;
|
|
258
|
+
/** How close the match is (0-1, 1 = exact) */
|
|
259
|
+
similarity: number;
|
|
260
|
+
}>;
|
|
261
|
+
|
|
262
|
+
/** Whether any match was found */
|
|
263
|
+
found: boolean;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Summary of token usage in a component
|
|
268
|
+
*/
|
|
269
|
+
export interface TokenUsageSummary {
|
|
270
|
+
/** Total CSS properties checked */
|
|
271
|
+
totalProperties: number;
|
|
272
|
+
|
|
273
|
+
/** Properties using design tokens */
|
|
274
|
+
usingTokens: number;
|
|
275
|
+
|
|
276
|
+
/** Properties with hardcoded values */
|
|
277
|
+
hardcoded: number;
|
|
278
|
+
|
|
279
|
+
/** Properties matching but not using tokens explicitly */
|
|
280
|
+
implicitMatches: number;
|
|
281
|
+
|
|
282
|
+
/** Compliance percentage (usingTokens / totalProperties * 100) */
|
|
283
|
+
compliancePercent: number;
|
|
284
|
+
|
|
285
|
+
/** List of hardcoded properties with fix suggestions */
|
|
286
|
+
hardcodedProperties: EnhancedStyleDiffItem[];
|
|
287
|
+
}
|