@fragments-sdk/cli 0.3.3 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/dist/bin.js +18 -13
  2. package/dist/bin.js.map +1 -1
  3. package/dist/{chunk-MUZ6CM66.js → chunk-5JNME72P.js} +3 -3
  4. package/dist/{chunk-MUZ6CM66.js.map → chunk-5JNME72P.js.map} +1 -1
  5. package/dist/{chunk-XHNKNI6J.js → chunk-AW7MWOUH.js} +9 -1
  6. package/dist/chunk-AW7MWOUH.js.map +1 -0
  7. package/dist/{chunk-LY2CFFPY.js → chunk-FYIYMXGA.js} +2 -2
  8. package/dist/{chunk-3OTEW66K.js → chunk-LDKNZ55O.js} +4 -4
  9. package/dist/{chunk-BSCG3IP7.js → chunk-NOTYONHY.js} +2 -2
  10. package/dist/{chunk-D6VXWI45.js → chunk-ODXQAQQX.js} +8 -6
  11. package/dist/chunk-ODXQAQQX.js.map +1 -0
  12. package/dist/{chunk-PMGI7ATF.js → chunk-OZQ7Z6C3.js} +31 -2
  13. package/dist/chunk-OZQ7Z6C3.js.map +1 -0
  14. package/dist/{core-DWKLGY4N.js → core-F3VT277E.js} +5 -3
  15. package/dist/{generate-3LBZANQ3.js → generate-PNIUR75D.js} +4 -4
  16. package/dist/index.d.ts +16 -0
  17. package/dist/index.js +6 -6
  18. package/dist/{init-NKIUCYTG.js → init-ON6WYG66.js} +4 -4
  19. package/dist/mcp-bin.js +3 -3
  20. package/dist/scan-E6U644RS.js +12 -0
  21. package/dist/{service-QSZMZJBJ.js → service-U7AR2PC2.js} +4 -4
  22. package/dist/{static-viewer-MIPGZ4Z7.js → static-viewer-QL2SCWYB.js} +4 -4
  23. package/dist/{test-ZCTR4LBB.js → test-PBPKJ4WJ.js} +3 -3
  24. package/dist/{tokens-5JQ5IOR2.js → tokens-4J4PRIGT.js} +5 -5
  25. package/dist/{viewer-D7QC4GM2.js → viewer-6VCZMA3T.js} +13 -13
  26. package/package.json +1 -1
  27. package/src/bin.ts +7 -1
  28. package/src/build.ts +2 -0
  29. package/src/core/index.ts +4 -0
  30. package/src/core/parser.ts +54 -1
  31. package/src/core/schema.ts +11 -0
  32. package/src/core/types.ts +24 -0
  33. package/src/migrate/bin.ts +7 -1
  34. package/src/migrate/report.ts +1 -1
  35. package/src/service/report.ts +1 -1
  36. package/src/theme/__tests__/generator.test.ts +412 -0
  37. package/src/theme/__tests__/presets.test.ts +169 -0
  38. package/src/theme/__tests__/schema.test.ts +463 -0
  39. package/src/theme/__tests__/serializer.test.ts +326 -0
  40. package/src/theme/generator.ts +355 -0
  41. package/src/theme/index.ts +61 -0
  42. package/src/theme/presets.ts +189 -0
  43. package/src/theme/schema.ts +193 -0
  44. package/src/theme/serializer.ts +123 -0
  45. package/src/theme/types.ts +210 -0
  46. package/src/viewer/styles/globals.css +1 -1
  47. package/dist/chunk-D6VXWI45.js.map +0 -1
  48. package/dist/chunk-PMGI7ATF.js.map +0 -1
  49. package/dist/chunk-XHNKNI6J.js.map +0 -1
  50. package/dist/scan-3ZAOVO4U.js +0 -12
  51. /package/dist/{chunk-LY2CFFPY.js.map → chunk-FYIYMXGA.js.map} +0 -0
  52. /package/dist/{chunk-3OTEW66K.js.map → chunk-LDKNZ55O.js.map} +0 -0
  53. /package/dist/{chunk-BSCG3IP7.js.map → chunk-NOTYONHY.js.map} +0 -0
  54. /package/dist/{core-DWKLGY4N.js.map → core-F3VT277E.js.map} +0 -0
  55. /package/dist/{generate-3LBZANQ3.js.map → generate-PNIUR75D.js.map} +0 -0
  56. /package/dist/{init-NKIUCYTG.js.map → init-ON6WYG66.js.map} +0 -0
  57. /package/dist/{scan-3ZAOVO4U.js.map → scan-E6U644RS.js.map} +0 -0
  58. /package/dist/{service-QSZMZJBJ.js.map → service-U7AR2PC2.js.map} +0 -0
  59. /package/dist/{static-viewer-MIPGZ4Z7.js.map → static-viewer-QL2SCWYB.js.map} +0 -0
  60. /package/dist/{test-ZCTR4LBB.js.map → test-PBPKJ4WJ.js.map} +0 -0
  61. /package/dist/{tokens-5JQ5IOR2.js.map → tokens-4J4PRIGT.js.map} +0 -0
  62. /package/dist/{viewer-D7QC4GM2.js.map → viewer-6VCZMA3T.js.map} +0 -0
@@ -0,0 +1,463 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import {
3
+ themeConfigSchema,
4
+ validateThemeConfig,
5
+ themeColorsSchema,
6
+ themeSurfacesSchema,
7
+ themeTextSchema,
8
+ themeBordersSchema,
9
+ themeTypographySchema,
10
+ themeRadiusSchema,
11
+ themeShadowsSchema,
12
+ themeDarkModeSchema,
13
+ } from "../schema.js";
14
+ import type { ThemeConfig } from "../types.js";
15
+
16
+ describe("ThemeConfig Schema", () => {
17
+ describe("validation", () => {
18
+ it("should accept valid minimal theme (name only)", () => {
19
+ const theme: ThemeConfig = {
20
+ name: "My Theme",
21
+ };
22
+
23
+ const result = themeConfigSchema.safeParse(theme);
24
+ expect(result.success).toBe(true);
25
+ });
26
+
27
+ it("should require name field", () => {
28
+ const theme = {};
29
+
30
+ const result = themeConfigSchema.safeParse(theme);
31
+ expect(result.success).toBe(false);
32
+ if (!result.success) {
33
+ expect(result.error.issues[0].path).toContain("name");
34
+ }
35
+ });
36
+
37
+ it("should validate hex color format (#rrggbb)", () => {
38
+ const theme: ThemeConfig = {
39
+ name: "Test",
40
+ colors: {
41
+ accent: "#6366f1",
42
+ },
43
+ };
44
+
45
+ const result = themeConfigSchema.safeParse(theme);
46
+ expect(result.success).toBe(true);
47
+ });
48
+
49
+ it("should validate 3-digit hex color format (#rgb)", () => {
50
+ const theme: ThemeConfig = {
51
+ name: "Test",
52
+ colors: {
53
+ accent: "#f00",
54
+ },
55
+ };
56
+
57
+ const result = themeConfigSchema.safeParse(theme);
58
+ expect(result.success).toBe(true);
59
+ });
60
+
61
+ it("should validate rgb/rgba color format", () => {
62
+ const theme: ThemeConfig = {
63
+ name: "Test",
64
+ colors: {
65
+ accent: "rgb(99, 102, 241)",
66
+ accentHover: "rgba(79, 70, 229, 0.9)",
67
+ },
68
+ };
69
+
70
+ const result = themeConfigSchema.safeParse(theme);
71
+ expect(result.success).toBe(true);
72
+ });
73
+
74
+ it("should validate hsl/hsla color format", () => {
75
+ const theme: ThemeConfig = {
76
+ name: "Test",
77
+ colors: {
78
+ accent: "hsl(239, 84%, 67%)",
79
+ accentHover: "hsla(243, 75%, 59%, 0.9)",
80
+ },
81
+ };
82
+
83
+ const result = themeConfigSchema.safeParse(theme);
84
+ expect(result.success).toBe(true);
85
+ });
86
+
87
+ it("should reject invalid color strings", () => {
88
+ const theme = {
89
+ name: "Test",
90
+ colors: {
91
+ accent: "not-a-color",
92
+ },
93
+ };
94
+
95
+ const result = themeConfigSchema.safeParse(theme);
96
+ expect(result.success).toBe(false);
97
+ });
98
+
99
+ it("should validate size formats (px, rem)", () => {
100
+ const theme: ThemeConfig = {
101
+ name: "Test",
102
+ radius: {
103
+ sm: "4px",
104
+ md: "0.375rem",
105
+ lg: "8px",
106
+ },
107
+ };
108
+
109
+ const result = themeConfigSchema.safeParse(theme);
110
+ expect(result.success).toBe(true);
111
+ });
112
+
113
+ it("should validate font weight range (100-900)", () => {
114
+ const theme: ThemeConfig = {
115
+ name: "Test",
116
+ typography: {
117
+ fontWeightNormal: 400,
118
+ fontWeightMedium: 500,
119
+ fontWeightSemibold: 600,
120
+ },
121
+ };
122
+
123
+ const result = themeConfigSchema.safeParse(theme);
124
+ expect(result.success).toBe(true);
125
+ });
126
+
127
+ it("should reject font weight outside range", () => {
128
+ const theme = {
129
+ name: "Test",
130
+ typography: {
131
+ fontWeightNormal: 1000,
132
+ },
133
+ };
134
+
135
+ const result = themeConfigSchema.safeParse(theme);
136
+ expect(result.success).toBe(false);
137
+ });
138
+
139
+ it("should accept extends: 'default'", () => {
140
+ const theme: ThemeConfig = {
141
+ name: "Test",
142
+ extends: "default",
143
+ };
144
+
145
+ const result = themeConfigSchema.safeParse(theme);
146
+ expect(result.success).toBe(true);
147
+ });
148
+
149
+ it("should accept version field", () => {
150
+ const theme: ThemeConfig = {
151
+ name: "Test",
152
+ version: "1.0.0",
153
+ };
154
+
155
+ const result = themeConfigSchema.safeParse(theme);
156
+ expect(result.success).toBe(true);
157
+ });
158
+ });
159
+
160
+ describe("colors schema", () => {
161
+ it("should validate all color fields", () => {
162
+ const colors = {
163
+ accent: "#6366f1",
164
+ accentHover: "#4f46e5",
165
+ accentActive: "#4338ca",
166
+ danger: "#ef4444",
167
+ dangerHover: "#dc2626",
168
+ success: "#22c55e",
169
+ warning: "#f59e0b",
170
+ info: "#3b82f6",
171
+ dangerBg: "rgba(239, 68, 68, 0.1)",
172
+ successBg: "rgba(34, 197, 94, 0.1)",
173
+ warningBg: "rgba(245, 158, 11, 0.1)",
174
+ infoBg: "rgba(59, 130, 246, 0.1)",
175
+ };
176
+
177
+ const result = themeColorsSchema.safeParse(colors);
178
+ expect(result.success).toBe(true);
179
+ });
180
+
181
+ it("should allow partial colors", () => {
182
+ const colors = {
183
+ accent: "#6366f1",
184
+ };
185
+
186
+ const result = themeColorsSchema.safeParse(colors);
187
+ expect(result.success).toBe(true);
188
+ });
189
+ });
190
+
191
+ describe("surfaces schema", () => {
192
+ it("should validate all surface fields", () => {
193
+ const surfaces = {
194
+ bgPrimary: "#ffffff",
195
+ bgSecondary: "#f8fafc",
196
+ bgTertiary: "#f1f5f9",
197
+ bgElevated: "#ffffff",
198
+ bgHover: "rgba(0, 0, 0, 0.04)",
199
+ bgActive: "rgba(0, 0, 0, 0.06)",
200
+ };
201
+
202
+ const result = themeSurfacesSchema.safeParse(surfaces);
203
+ expect(result.success).toBe(true);
204
+ });
205
+ });
206
+
207
+ describe("text schema", () => {
208
+ it("should validate all text fields", () => {
209
+ const text = {
210
+ primary: "#0f172a",
211
+ secondary: "#64748b",
212
+ tertiary: "#94a3b8",
213
+ inverse: "#ffffff",
214
+ };
215
+
216
+ const result = themeTextSchema.safeParse(text);
217
+ expect(result.success).toBe(true);
218
+ });
219
+ });
220
+
221
+ describe("borders schema", () => {
222
+ it("should validate all border fields", () => {
223
+ const borders = {
224
+ default: "#e2e8f0",
225
+ strong: "#cbd5e1",
226
+ };
227
+
228
+ const result = themeBordersSchema.safeParse(borders);
229
+ expect(result.success).toBe(true);
230
+ });
231
+ });
232
+
233
+ describe("typography schema", () => {
234
+ it("should validate all typography fields", () => {
235
+ const typography = {
236
+ fontSans: "system-ui, -apple-system, sans-serif",
237
+ fontMono: "'SF Mono', monospace",
238
+ fontWeightNormal: 400,
239
+ fontWeightMedium: 500,
240
+ fontWeightSemibold: 600,
241
+ };
242
+
243
+ const result = themeTypographySchema.safeParse(typography);
244
+ expect(result.success).toBe(true);
245
+ });
246
+ });
247
+
248
+ describe("radius schema", () => {
249
+ it("should validate all radius fields", () => {
250
+ const radius = {
251
+ sm: "0.25rem",
252
+ md: "0.375rem",
253
+ lg: "0.5rem",
254
+ full: "9999px",
255
+ };
256
+
257
+ const result = themeRadiusSchema.safeParse(radius);
258
+ expect(result.success).toBe(true);
259
+ });
260
+ });
261
+
262
+ describe("shadows schema", () => {
263
+ it("should validate all shadow fields", () => {
264
+ const shadows = {
265
+ sm: "0 1px 2px 0 rgba(0, 0, 0, 0.05)",
266
+ md: "0 4px 6px -1px rgba(0, 0, 0, 0.1)",
267
+ };
268
+
269
+ const result = themeShadowsSchema.safeParse(shadows);
270
+ expect(result.success).toBe(true);
271
+ });
272
+ });
273
+
274
+ describe("dark mode", () => {
275
+ it("should validate dark mode surface overrides", () => {
276
+ const theme: ThemeConfig = {
277
+ name: "Test",
278
+ dark: {
279
+ surfaces: {
280
+ bgPrimary: "#0f172a",
281
+ bgSecondary: "#1e293b",
282
+ },
283
+ },
284
+ };
285
+
286
+ const result = themeConfigSchema.safeParse(theme);
287
+ expect(result.success).toBe(true);
288
+ });
289
+
290
+ it("should validate dark mode text overrides", () => {
291
+ const theme: ThemeConfig = {
292
+ name: "Test",
293
+ dark: {
294
+ text: {
295
+ primary: "#f8fafc",
296
+ secondary: "#94a3b8",
297
+ },
298
+ },
299
+ };
300
+
301
+ const result = themeConfigSchema.safeParse(theme);
302
+ expect(result.success).toBe(true);
303
+ });
304
+
305
+ it("should validate full dark mode config", () => {
306
+ const darkMode = {
307
+ surfaces: {
308
+ bgPrimary: "#0f172a",
309
+ bgSecondary: "#1e293b",
310
+ bgTertiary: "#334155",
311
+ bgElevated: "#1e293b",
312
+ bgHover: "rgba(255, 255, 255, 0.06)",
313
+ bgActive: "rgba(255, 255, 255, 0.08)",
314
+ },
315
+ text: {
316
+ primary: "#f8fafc",
317
+ secondary: "#94a3b8",
318
+ tertiary: "#64748b",
319
+ inverse: "#0f172a",
320
+ },
321
+ borders: {
322
+ default: "#334155",
323
+ strong: "#475569",
324
+ },
325
+ shadows: {
326
+ sm: "0 1px 2px 0 rgba(0, 0, 0, 0.3)",
327
+ md: "0 4px 6px -1px rgba(0, 0, 0, 0.4)",
328
+ },
329
+ dangerBg: "rgba(239, 68, 68, 0.15)",
330
+ successBg: "rgba(34, 197, 94, 0.15)",
331
+ warningBg: "rgba(245, 158, 11, 0.15)",
332
+ infoBg: "rgba(59, 130, 246, 0.15)",
333
+ backdrop: "rgba(0, 0, 0, 0.7)",
334
+ };
335
+
336
+ const result = themeDarkModeSchema.safeParse(darkMode);
337
+ expect(result.success).toBe(true);
338
+ });
339
+ });
340
+
341
+ describe("validateThemeConfig helper", () => {
342
+ it("should return parsed theme for valid input", () => {
343
+ const theme = {
344
+ name: "Test Theme",
345
+ colors: {
346
+ accent: "#6366f1",
347
+ },
348
+ };
349
+
350
+ const result = validateThemeConfig(theme);
351
+ expect(result.success).toBe(true);
352
+ if (result.success) {
353
+ expect(result.data.name).toBe("Test Theme");
354
+ expect(result.data.colors?.accent).toBe("#6366f1");
355
+ }
356
+ });
357
+
358
+ it("should return error for invalid input", () => {
359
+ const theme = {
360
+ colors: {
361
+ accent: "invalid",
362
+ },
363
+ };
364
+
365
+ const result = validateThemeConfig(theme);
366
+ expect(result.success).toBe(false);
367
+ if (!result.success) {
368
+ expect(result.error).toBeDefined();
369
+ }
370
+ });
371
+ });
372
+
373
+ describe("complex theme validation", () => {
374
+ it("should validate a complete theme config", () => {
375
+ const theme: ThemeConfig = {
376
+ name: "Complete Theme",
377
+ version: "1.0.0",
378
+ extends: "default",
379
+ colors: {
380
+ accent: "#6366f1",
381
+ accentHover: "#4f46e5",
382
+ accentActive: "#4338ca",
383
+ danger: "#ef4444",
384
+ dangerHover: "#dc2626",
385
+ success: "#22c55e",
386
+ warning: "#f59e0b",
387
+ info: "#3b82f6",
388
+ dangerBg: "rgba(239, 68, 68, 0.1)",
389
+ successBg: "rgba(34, 197, 94, 0.1)",
390
+ warningBg: "rgba(245, 158, 11, 0.1)",
391
+ infoBg: "rgba(59, 130, 246, 0.1)",
392
+ },
393
+ surfaces: {
394
+ bgPrimary: "#ffffff",
395
+ bgSecondary: "#f8fafc",
396
+ bgTertiary: "#f1f5f9",
397
+ bgElevated: "#ffffff",
398
+ bgHover: "rgba(0, 0, 0, 0.04)",
399
+ bgActive: "rgba(0, 0, 0, 0.06)",
400
+ },
401
+ text: {
402
+ primary: "#0f172a",
403
+ secondary: "#64748b",
404
+ tertiary: "#94a3b8",
405
+ inverse: "#ffffff",
406
+ },
407
+ borders: {
408
+ default: "#e2e8f0",
409
+ strong: "#cbd5e1",
410
+ },
411
+ typography: {
412
+ fontSans: "system-ui, -apple-system, sans-serif",
413
+ fontMono: "'SF Mono', monospace",
414
+ fontWeightNormal: 400,
415
+ fontWeightMedium: 500,
416
+ fontWeightSemibold: 600,
417
+ },
418
+ radius: {
419
+ sm: "0.25rem",
420
+ md: "0.375rem",
421
+ lg: "0.5rem",
422
+ full: "9999px",
423
+ },
424
+ shadows: {
425
+ sm: "0 1px 2px 0 rgba(0, 0, 0, 0.05)",
426
+ md: "0 4px 6px -1px rgba(0, 0, 0, 0.1)",
427
+ },
428
+ dark: {
429
+ surfaces: {
430
+ bgPrimary: "#0f172a",
431
+ bgSecondary: "#1e293b",
432
+ bgTertiary: "#334155",
433
+ bgElevated: "#1e293b",
434
+ bgHover: "rgba(255, 255, 255, 0.06)",
435
+ bgActive: "rgba(255, 255, 255, 0.08)",
436
+ },
437
+ text: {
438
+ primary: "#f8fafc",
439
+ secondary: "#94a3b8",
440
+ tertiary: "#64748b",
441
+ inverse: "#0f172a",
442
+ },
443
+ borders: {
444
+ default: "#334155",
445
+ strong: "#475569",
446
+ },
447
+ shadows: {
448
+ sm: "0 1px 2px 0 rgba(0, 0, 0, 0.3)",
449
+ md: "0 4px 6px -1px rgba(0, 0, 0, 0.4)",
450
+ },
451
+ dangerBg: "rgba(239, 68, 68, 0.15)",
452
+ successBg: "rgba(34, 197, 94, 0.15)",
453
+ warningBg: "rgba(245, 158, 11, 0.15)",
454
+ infoBg: "rgba(59, 130, 246, 0.15)",
455
+ backdrop: "rgba(0, 0, 0, 0.7)",
456
+ },
457
+ };
458
+
459
+ const result = themeConfigSchema.safeParse(theme);
460
+ expect(result.success).toBe(true);
461
+ });
462
+ });
463
+ });