designlang 4.0.1 → 6.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +66 -5
- package/bin/design-extract.js +269 -70
- package/package.json +9 -4
- package/src/apply.js +65 -0
- package/src/config.js +36 -0
- package/src/crawler.js +247 -82
- package/src/darkdiff.js +65 -0
- package/src/extractors/animations.js +76 -8
- package/src/extractors/borders.js +40 -5
- package/src/extractors/components.js +100 -1
- package/src/extractors/fonts.js +82 -0
- package/src/extractors/gradients.js +100 -0
- package/src/extractors/icons.js +80 -0
- package/src/extractors/images.js +76 -0
- package/src/extractors/shadows.js +60 -17
- package/src/extractors/spacing.js +31 -2
- package/src/extractors/variables.js +20 -1
- package/src/extractors/zindex.js +65 -0
- package/src/formatters/figma.js +66 -47
- package/src/formatters/markdown.js +98 -0
- package/src/formatters/preview.js +65 -22
- package/src/formatters/svelte-theme.js +40 -0
- package/src/formatters/tailwind.js +57 -4
- package/src/formatters/theme.js +134 -0
- package/src/formatters/vue-theme.js +44 -0
- package/src/formatters/wordpress.js +84 -0
- package/src/history.js +8 -1
- package/src/index.js +54 -16
- package/src/utils.js +68 -0
- package/tests/cli.test.js +34 -0
- package/tests/extractors.test.js +661 -0
- package/tests/formatters.test.js +477 -0
- package/tests/utils.test.js +413 -0
- package/website/app/api/extract/route.js +85 -0
- package/website/app/components/Extractor.js +184 -0
- package/website/app/globals.css +291 -0
- package/website/app/page.js +13 -0
- package/website/next.config.mjs +10 -1
- package/website/package-lock.json +356 -0
- package/website/package.json +4 -1
|
@@ -0,0 +1,477 @@
|
|
|
1
|
+
import { describe, it } from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { formatMarkdown } from '../src/formatters/markdown.js';
|
|
4
|
+
import { formatTokens } from '../src/formatters/tokens.js';
|
|
5
|
+
import { formatTailwind } from '../src/formatters/tailwind.js';
|
|
6
|
+
import { formatCssVars } from '../src/formatters/css-vars.js';
|
|
7
|
+
import { formatPreview } from '../src/formatters/preview.js';
|
|
8
|
+
import { formatFigma } from '../src/formatters/figma.js';
|
|
9
|
+
import { formatReactTheme, formatShadcnTheme } from '../src/formatters/theme.js';
|
|
10
|
+
|
|
11
|
+
// ── Shared mock design object ───────────────────────────────────
|
|
12
|
+
|
|
13
|
+
const mockDesign = {
|
|
14
|
+
meta: {
|
|
15
|
+
url: 'https://example.com',
|
|
16
|
+
title: 'Test Site',
|
|
17
|
+
timestamp: new Date().toISOString(),
|
|
18
|
+
elementCount: 100,
|
|
19
|
+
pagesAnalyzed: 1,
|
|
20
|
+
},
|
|
21
|
+
colors: {
|
|
22
|
+
primary: { hex: '#0066cc', rgb: { r: 0, g: 102, b: 204 }, hsl: { h: 210, s: 100, l: 40 }, count: 50 },
|
|
23
|
+
secondary: { hex: '#cc6600', rgb: { r: 204, g: 102, b: 0 }, hsl: { h: 30, s: 100, l: 40 }, count: 25 },
|
|
24
|
+
accent: { hex: '#00cc66', rgb: { r: 0, g: 204, b: 102 }, hsl: { h: 150, s: 100, l: 40 }, count: 10 },
|
|
25
|
+
neutrals: [
|
|
26
|
+
{ hex: '#333333', rgb: { r: 51, g: 51, b: 51 }, hsl: { h: 0, s: 0, l: 20 }, count: 30 },
|
|
27
|
+
{ hex: '#666666', rgb: { r: 102, g: 102, b: 102 }, hsl: { h: 0, s: 0, l: 40 }, count: 20 },
|
|
28
|
+
],
|
|
29
|
+
backgrounds: ['#ffffff', '#f5f5f5'],
|
|
30
|
+
text: ['#333333', '#666666'],
|
|
31
|
+
gradients: ['linear-gradient(to right, #0066cc, #00cc66)'],
|
|
32
|
+
all: [
|
|
33
|
+
{ hex: '#0066cc', rgb: { r: 0, g: 102, b: 204 }, hsl: { h: 210, s: 100, l: 40 }, count: 50, contexts: ['text', 'background'] },
|
|
34
|
+
{ hex: '#333333', rgb: { r: 51, g: 51, b: 51 }, hsl: { h: 0, s: 0, l: 20 }, count: 30, contexts: ['text'] },
|
|
35
|
+
],
|
|
36
|
+
},
|
|
37
|
+
typography: {
|
|
38
|
+
families: [
|
|
39
|
+
{ name: 'Inter', count: 80, usage: 'all' },
|
|
40
|
+
{ name: 'Playfair Display', count: 20, usage: 'headings' },
|
|
41
|
+
],
|
|
42
|
+
scale: [
|
|
43
|
+
{ size: 48, weight: '700', lineHeight: '1.2', letterSpacing: '-0.02em', tags: ['h1'], count: 5 },
|
|
44
|
+
{ size: 36, weight: '700', lineHeight: '1.3', letterSpacing: 'normal', tags: ['h2'], count: 8 },
|
|
45
|
+
{ size: 24, weight: '600', lineHeight: '1.4', letterSpacing: 'normal', tags: ['h3'], count: 12 },
|
|
46
|
+
{ size: 16, weight: '400', lineHeight: '1.5', letterSpacing: 'normal', tags: ['p', 'span'], count: 60 },
|
|
47
|
+
],
|
|
48
|
+
headings: [
|
|
49
|
+
{ size: 48, weight: '700', lineHeight: '1.2', letterSpacing: '-0.02em', tags: ['h1'], count: 5 },
|
|
50
|
+
{ size: 36, weight: '700', lineHeight: '1.3', letterSpacing: 'normal', tags: ['h2'], count: 8 },
|
|
51
|
+
],
|
|
52
|
+
body: { size: 16, weight: '400', lineHeight: '1.5', letterSpacing: 'normal', tags: ['p'], count: 60 },
|
|
53
|
+
weights: [{ weight: '400', count: 60 }, { weight: '600', count: 12 }, { weight: '700', count: 13 }],
|
|
54
|
+
},
|
|
55
|
+
spacing: {
|
|
56
|
+
base: 4,
|
|
57
|
+
scale: [4, 8, 12, 16, 24, 32, 48, 64],
|
|
58
|
+
tokens: { '1': '4px', '2': '8px', '3': '12px', '4': '16px', '6': '24px', '8': '32px', '12': '48px', '16': '64px' },
|
|
59
|
+
raw: [4, 8, 12, 16, 24, 32, 48, 64],
|
|
60
|
+
},
|
|
61
|
+
shadows: {
|
|
62
|
+
values: [
|
|
63
|
+
{ raw: '0 1px 3px rgba(0,0,0,0.1)', blur: 3, inset: false, label: 'sm' },
|
|
64
|
+
{ raw: '0 4px 12px rgba(0,0,0,0.15)', blur: 12, inset: false, label: 'md' },
|
|
65
|
+
],
|
|
66
|
+
},
|
|
67
|
+
borders: {
|
|
68
|
+
radii: [
|
|
69
|
+
{ value: 4, label: 'sm', count: 20 },
|
|
70
|
+
{ value: 8, label: 'md', count: 15 },
|
|
71
|
+
{ value: 16, label: 'lg', count: 5 },
|
|
72
|
+
],
|
|
73
|
+
widths: [1, 2],
|
|
74
|
+
styles: ['solid'],
|
|
75
|
+
},
|
|
76
|
+
variables: { colors: { '--color-primary': '#0066cc' }, spacing: {}, typography: {} },
|
|
77
|
+
breakpoints: [
|
|
78
|
+
{ value: 640, label: 'mobile', type: 'min-width' },
|
|
79
|
+
{ value: 768, label: 'tablet', type: 'min-width' },
|
|
80
|
+
{ value: 1024, label: 'desktop', type: 'min-width' },
|
|
81
|
+
],
|
|
82
|
+
animations: {
|
|
83
|
+
transitions: ['all 0.2s ease', 'opacity 0.3s ease-in-out'],
|
|
84
|
+
keyframes: [],
|
|
85
|
+
easings: ['ease', 'ease-in-out'],
|
|
86
|
+
durations: ['0.2s', '0.3s'],
|
|
87
|
+
},
|
|
88
|
+
components: {
|
|
89
|
+
buttons: {
|
|
90
|
+
count: 10,
|
|
91
|
+
baseStyle: { backgroundColor: '#0066cc', color: '#ffffff', borderRadius: '4px', fontSize: '14px' },
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
accessibility: { score: 90, passCount: 45, failCount: 5, totalPairs: 50, pairs: [] },
|
|
95
|
+
layout: {
|
|
96
|
+
gridCount: 5,
|
|
97
|
+
flexCount: 20,
|
|
98
|
+
gridColumns: [{ columns: 3, count: 5 }],
|
|
99
|
+
flexDirections: { 'row/nowrap': 15, 'column/nowrap': 5 },
|
|
100
|
+
justifyPatterns: {},
|
|
101
|
+
alignPatterns: {},
|
|
102
|
+
containerWidths: [{ maxWidth: '1200px', padding: '16px' }],
|
|
103
|
+
gaps: ['16px', '24px'],
|
|
104
|
+
topGrids: [{ columns: 'repeat(3, 1fr)', rows: 'none', gap: '24px' }],
|
|
105
|
+
topFlex: [],
|
|
106
|
+
},
|
|
107
|
+
gradients: { count: 0, gradients: [] },
|
|
108
|
+
zIndex: { allValues: [], layers: [], issues: [], scale: [] },
|
|
109
|
+
icons: { icons: [], count: 0 },
|
|
110
|
+
fonts: { fonts: [], systemFonts: [] },
|
|
111
|
+
images: { patterns: [], aspectRatios: [] },
|
|
112
|
+
componentScreenshots: {},
|
|
113
|
+
score: {
|
|
114
|
+
overall: 85,
|
|
115
|
+
grade: 'B',
|
|
116
|
+
scores: {
|
|
117
|
+
colorDiscipline: 85,
|
|
118
|
+
typographyConsistency: 100,
|
|
119
|
+
spacingSystem: 90,
|
|
120
|
+
shadowConsistency: 100,
|
|
121
|
+
radiusConsistency: 100,
|
|
122
|
+
accessibility: 90,
|
|
123
|
+
tokenization: 50,
|
|
124
|
+
},
|
|
125
|
+
issues: ['No CSS custom properties found'],
|
|
126
|
+
strengths: ['Tight, disciplined color palette'],
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
// ── formatMarkdown ──────────────────────────────────────────────
|
|
131
|
+
|
|
132
|
+
describe('formatMarkdown', () => {
|
|
133
|
+
it('returns a string', () => {
|
|
134
|
+
const result = formatMarkdown(mockDesign);
|
|
135
|
+
assert.equal(typeof result, 'string');
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it('contains the site title', () => {
|
|
139
|
+
const result = formatMarkdown(mockDesign);
|
|
140
|
+
assert.ok(result.includes('Test Site'));
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it('contains color palette section', () => {
|
|
144
|
+
const result = formatMarkdown(mockDesign);
|
|
145
|
+
assert.ok(result.includes('## Color Palette'));
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it('contains typography section', () => {
|
|
149
|
+
const result = formatMarkdown(mockDesign);
|
|
150
|
+
assert.ok(result.includes('## Typography'));
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it('contains spacing section', () => {
|
|
154
|
+
const result = formatMarkdown(mockDesign);
|
|
155
|
+
assert.ok(result.includes('## Spacing'));
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it('contains the primary color hex', () => {
|
|
159
|
+
const result = formatMarkdown(mockDesign);
|
|
160
|
+
assert.ok(result.includes('#0066cc'));
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it('contains font family names', () => {
|
|
164
|
+
const result = formatMarkdown(mockDesign);
|
|
165
|
+
assert.ok(result.includes('Inter'));
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it('contains component patterns section', () => {
|
|
169
|
+
const result = formatMarkdown(mockDesign);
|
|
170
|
+
assert.ok(result.includes('## Component Patterns'));
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
it('contains design system score section', () => {
|
|
174
|
+
const result = formatMarkdown(mockDesign);
|
|
175
|
+
assert.ok(result.includes('## Design System Score'));
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
it('contains layout section', () => {
|
|
179
|
+
const result = formatMarkdown(mockDesign);
|
|
180
|
+
assert.ok(result.includes('## Layout System'));
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
// ── formatTokens ────────────────────────────────────────────────
|
|
185
|
+
|
|
186
|
+
describe('formatTokens', () => {
|
|
187
|
+
it('returns valid JSON', () => {
|
|
188
|
+
const result = formatTokens(mockDesign);
|
|
189
|
+
const parsed = JSON.parse(result);
|
|
190
|
+
assert.ok(typeof parsed === 'object');
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
it('contains color tokens', () => {
|
|
194
|
+
const parsed = JSON.parse(formatTokens(mockDesign));
|
|
195
|
+
assert.ok(parsed.color);
|
|
196
|
+
assert.ok(parsed.color.primary);
|
|
197
|
+
assert.equal(parsed.color.primary.$value, '#0066cc');
|
|
198
|
+
assert.equal(parsed.color.primary.$type, 'color');
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
it('contains fontFamily tokens', () => {
|
|
202
|
+
const parsed = JSON.parse(formatTokens(mockDesign));
|
|
203
|
+
assert.ok(parsed.fontFamily);
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
it('contains spacing tokens', () => {
|
|
207
|
+
const parsed = JSON.parse(formatTokens(mockDesign));
|
|
208
|
+
assert.ok(parsed.spacing);
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
it('contains borderRadius tokens', () => {
|
|
212
|
+
const parsed = JSON.parse(formatTokens(mockDesign));
|
|
213
|
+
assert.ok(parsed.borderRadius);
|
|
214
|
+
assert.ok(parsed.borderRadius.sm);
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
it('contains shadow tokens', () => {
|
|
218
|
+
const parsed = JSON.parse(formatTokens(mockDesign));
|
|
219
|
+
assert.ok(parsed.shadow);
|
|
220
|
+
assert.ok(parsed.shadow.sm);
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
it('contains breakpoint tokens', () => {
|
|
224
|
+
const parsed = JSON.parse(formatTokens(mockDesign));
|
|
225
|
+
assert.ok(parsed.breakpoint);
|
|
226
|
+
});
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
// ── formatTailwind ──────────────────────────────────────────────
|
|
230
|
+
|
|
231
|
+
describe('formatTailwind', () => {
|
|
232
|
+
it('returns a string', () => {
|
|
233
|
+
const result = formatTailwind(mockDesign);
|
|
234
|
+
assert.equal(typeof result, 'string');
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
it('contains export default', () => {
|
|
238
|
+
const result = formatTailwind(mockDesign);
|
|
239
|
+
assert.ok(result.includes('export default'));
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
it('contains primary color', () => {
|
|
243
|
+
const result = formatTailwind(mockDesign);
|
|
244
|
+
assert.ok(result.includes('#0066cc'));
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
it('contains font family', () => {
|
|
248
|
+
const result = formatTailwind(mockDesign);
|
|
249
|
+
assert.ok(result.includes('Inter'));
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
it('contains spacing values', () => {
|
|
253
|
+
const result = formatTailwind(mockDesign);
|
|
254
|
+
assert.ok(result.includes('4px'));
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
it('contains screen breakpoints', () => {
|
|
258
|
+
const result = formatTailwind(mockDesign);
|
|
259
|
+
assert.ok(result.includes('768px'));
|
|
260
|
+
});
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
// ── formatCssVars ───────────────────────────────────────────────
|
|
264
|
+
|
|
265
|
+
describe('formatCssVars', () => {
|
|
266
|
+
it('returns a string', () => {
|
|
267
|
+
const result = formatCssVars(mockDesign);
|
|
268
|
+
assert.equal(typeof result, 'string');
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
it('starts with :root {', () => {
|
|
272
|
+
const result = formatCssVars(mockDesign);
|
|
273
|
+
assert.ok(result.startsWith(':root {'));
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
it('ends with closing brace', () => {
|
|
277
|
+
const result = formatCssVars(mockDesign);
|
|
278
|
+
assert.ok(result.trimEnd().endsWith('}'));
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
it('contains color variables', () => {
|
|
282
|
+
const result = formatCssVars(mockDesign);
|
|
283
|
+
assert.ok(result.includes('--color-primary: #0066cc;'));
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
it('contains spacing variables', () => {
|
|
287
|
+
const result = formatCssVars(mockDesign);
|
|
288
|
+
assert.ok(result.includes('--spacing-'));
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
it('contains font variables', () => {
|
|
292
|
+
const result = formatCssVars(mockDesign);
|
|
293
|
+
assert.ok(result.includes('--font-'));
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
it('contains radius variables', () => {
|
|
297
|
+
const result = formatCssVars(mockDesign);
|
|
298
|
+
assert.ok(result.includes('--radius-'));
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
it('contains shadow variables', () => {
|
|
302
|
+
const result = formatCssVars(mockDesign);
|
|
303
|
+
assert.ok(result.includes('--shadow-'));
|
|
304
|
+
});
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
// ── formatPreview ───────────────────────────────────────────────
|
|
308
|
+
|
|
309
|
+
describe('formatPreview', () => {
|
|
310
|
+
it('returns a string', () => {
|
|
311
|
+
const result = formatPreview(mockDesign);
|
|
312
|
+
assert.equal(typeof result, 'string');
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
it('returns valid HTML with doctype', () => {
|
|
316
|
+
const result = formatPreview(mockDesign);
|
|
317
|
+
assert.ok(result.includes('<!DOCTYPE html>'));
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
it('contains html and body tags', () => {
|
|
321
|
+
const result = formatPreview(mockDesign);
|
|
322
|
+
assert.ok(result.includes('<html'));
|
|
323
|
+
assert.ok(result.includes('<body>'));
|
|
324
|
+
assert.ok(result.includes('</body>'));
|
|
325
|
+
assert.ok(result.includes('</html>'));
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
it('contains the site title', () => {
|
|
329
|
+
const result = formatPreview(mockDesign);
|
|
330
|
+
assert.ok(result.includes('Test Site'));
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
it('contains color swatches section', () => {
|
|
334
|
+
const result = formatPreview(mockDesign);
|
|
335
|
+
assert.ok(result.includes('Color Palette'));
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
it('contains typography section', () => {
|
|
339
|
+
const result = formatPreview(mockDesign);
|
|
340
|
+
assert.ok(result.includes('Typography'));
|
|
341
|
+
});
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
// ── formatFigma ─────────────────────────────────────────────────
|
|
345
|
+
|
|
346
|
+
describe('formatFigma', () => {
|
|
347
|
+
it('returns valid JSON', () => {
|
|
348
|
+
const result = formatFigma(mockDesign);
|
|
349
|
+
const parsed = JSON.parse(result);
|
|
350
|
+
assert.ok(typeof parsed === 'object');
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
it('has collections array with Brand collection', () => {
|
|
354
|
+
const parsed = JSON.parse(formatFigma(mockDesign));
|
|
355
|
+
assert.ok(Array.isArray(parsed.collections));
|
|
356
|
+
const brand = parsed.collections.find(c => c.name === 'Brand');
|
|
357
|
+
assert.ok(brand);
|
|
358
|
+
assert.ok(Array.isArray(brand.modes));
|
|
359
|
+
assert.ok(brand.variables.length > 0);
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
it('has Typography and Spacing collections', () => {
|
|
363
|
+
const parsed = JSON.parse(formatFigma(mockDesign));
|
|
364
|
+
const typo = parsed.collections.find(c => c.name === 'Typography');
|
|
365
|
+
const spacing = parsed.collections.find(c => c.name === 'Spacing');
|
|
366
|
+
assert.ok(typo);
|
|
367
|
+
assert.ok(spacing);
|
|
368
|
+
assert.ok(typo.variables.length > 0);
|
|
369
|
+
assert.ok(spacing.variables.length > 0);
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
it('contains color variables with normalized RGB in light mode', () => {
|
|
373
|
+
const parsed = JSON.parse(formatFigma(mockDesign));
|
|
374
|
+
const brand = parsed.collections.find(c => c.name === 'Brand');
|
|
375
|
+
const primary = brand.variables.find(v => v.name === 'color/primary');
|
|
376
|
+
assert.ok(primary);
|
|
377
|
+
assert.ok(primary.values.light.r >= 0 && primary.values.light.r <= 1);
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
it('contains spacing variables', () => {
|
|
381
|
+
const parsed = JSON.parse(formatFigma(mockDesign));
|
|
382
|
+
const spacing = parsed.collections.find(c => c.name === 'Spacing');
|
|
383
|
+
const spacingVars = spacing.variables.filter(v => v.name.startsWith('spacing/'));
|
|
384
|
+
assert.ok(spacingVars.length > 0);
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
it('contains radius variables', () => {
|
|
388
|
+
const parsed = JSON.parse(formatFigma(mockDesign));
|
|
389
|
+
const spacing = parsed.collections.find(c => c.name === 'Spacing');
|
|
390
|
+
const radiusVars = spacing.variables.filter(v => v.name.startsWith('radius/'));
|
|
391
|
+
assert.ok(radiusVars.length > 0);
|
|
392
|
+
});
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
// ── formatReactTheme ────────────────────────────────────────────
|
|
396
|
+
|
|
397
|
+
describe('formatReactTheme', () => {
|
|
398
|
+
it('returns a string', () => {
|
|
399
|
+
const result = formatReactTheme(mockDesign);
|
|
400
|
+
assert.equal(typeof result, 'string');
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
it('contains export const theme', () => {
|
|
404
|
+
const result = formatReactTheme(mockDesign);
|
|
405
|
+
assert.ok(result.includes('export const theme'));
|
|
406
|
+
});
|
|
407
|
+
|
|
408
|
+
it('contains export default theme', () => {
|
|
409
|
+
const result = formatReactTheme(mockDesign);
|
|
410
|
+
assert.ok(result.includes('export default theme'));
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
it('contains the primary color', () => {
|
|
414
|
+
const result = formatReactTheme(mockDesign);
|
|
415
|
+
assert.ok(result.includes('#0066cc'));
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
it('contains font family', () => {
|
|
419
|
+
const result = formatReactTheme(mockDesign);
|
|
420
|
+
assert.ok(result.includes('Inter'));
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
it('contains spacing values', () => {
|
|
424
|
+
const result = formatReactTheme(mockDesign);
|
|
425
|
+
assert.ok(result.includes('4px'));
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
it('contains the embedded JSON as valid JS', () => {
|
|
429
|
+
const result = formatReactTheme(mockDesign);
|
|
430
|
+
// Extract the JSON object from the template
|
|
431
|
+
const jsonMatch = result.match(/export const theme = ({[\s\S]+?});/);
|
|
432
|
+
assert.ok(jsonMatch, 'Should contain a theme object');
|
|
433
|
+
// The JSON should be parseable
|
|
434
|
+
const parsed = JSON.parse(jsonMatch[1]);
|
|
435
|
+
assert.ok(parsed.colors);
|
|
436
|
+
assert.ok(parsed.fonts);
|
|
437
|
+
});
|
|
438
|
+
});
|
|
439
|
+
|
|
440
|
+
// ── formatShadcnTheme ───────────────────────────────────────────
|
|
441
|
+
|
|
442
|
+
describe('formatShadcnTheme', () => {
|
|
443
|
+
it('returns a string', () => {
|
|
444
|
+
const result = formatShadcnTheme(mockDesign);
|
|
445
|
+
assert.equal(typeof result, 'string');
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
it('contains @layer base', () => {
|
|
449
|
+
const result = formatShadcnTheme(mockDesign);
|
|
450
|
+
assert.ok(result.includes('@layer base'));
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
it('contains :root', () => {
|
|
454
|
+
const result = formatShadcnTheme(mockDesign);
|
|
455
|
+
assert.ok(result.includes(':root'));
|
|
456
|
+
});
|
|
457
|
+
|
|
458
|
+
it('contains --primary variable', () => {
|
|
459
|
+
const result = formatShadcnTheme(mockDesign);
|
|
460
|
+
assert.ok(result.includes('--primary:'));
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
it('contains --background variable', () => {
|
|
464
|
+
const result = formatShadcnTheme(mockDesign);
|
|
465
|
+
assert.ok(result.includes('--background:'));
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
it('contains --radius variable', () => {
|
|
469
|
+
const result = formatShadcnTheme(mockDesign);
|
|
470
|
+
assert.ok(result.includes('--radius:'));
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
it('contains shadcn/ui comment', () => {
|
|
474
|
+
const result = formatShadcnTheme(mockDesign);
|
|
475
|
+
assert.ok(result.includes('shadcn/ui'));
|
|
476
|
+
});
|
|
477
|
+
});
|