@fragments-sdk/cli 0.5.2 → 0.7.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 (124) hide show
  1. package/dist/bin.js +996 -79
  2. package/dist/bin.js.map +1 -1
  3. package/dist/{chunk-ICAIQ57V.js → chunk-6JBGU74P.js} +5 -3
  4. package/dist/chunk-6JBGU74P.js.map +1 -0
  5. package/dist/chunk-7OPWMLOE.js +1625 -0
  6. package/dist/chunk-7OPWMLOE.js.map +1 -0
  7. package/dist/{chunk-2H2JAA3U.js → chunk-CVXKXVOY.js} +3 -3
  8. package/dist/{chunk-2H2JAA3U.js.map → chunk-CVXKXVOY.js.map} +1 -1
  9. package/dist/{chunk-IOJE35DZ.js → chunk-NWQ4CJOQ.js} +3 -3
  10. package/dist/{chunk-2DJH4F4P.js → chunk-RVRTRESS.js} +3 -3
  11. package/dist/{chunk-V7YLRR4C.js → chunk-TJ34N7C7.js} +41 -4
  12. package/dist/{chunk-V7YLRR4C.js.map → chunk-TJ34N7C7.js.map} +1 -1
  13. package/dist/{chunk-XNWDI6UT.js → chunk-XHUDJNN3.js} +5 -5
  14. package/dist/{core-DKHB7FYV.js → core-W2HYIQW6.js} +4 -4
  15. package/dist/{generate-KL24VZVD.js → generate-LMTISDIJ.js} +5 -5
  16. package/dist/index.d.ts +1 -0
  17. package/dist/index.js +15 -7
  18. package/dist/index.js.map +1 -1
  19. package/dist/{init-NION5S3M.js → init-7CHRKQ7P.js} +5 -5
  20. package/dist/mcp-bin.js +8 -220
  21. package/dist/mcp-bin.js.map +1 -1
  22. package/dist/scan-WY23TJCP.js +12 -0
  23. package/dist/{service-RWUMZ3EW.js → service-T2L7VLTE.js} +5 -5
  24. package/dist/static-viewer-GBR7YNF3.js +12 -0
  25. package/dist/{test-ECPEXFDN.js → test-OJRXNDO2.js} +4 -4
  26. package/dist/{tokens-ITADYVPF.js → tokens-3BWDESVM.js} +6 -6
  27. package/dist/viewer-SUFOISZM.js +1822 -0
  28. package/dist/viewer-SUFOISZM.js.map +1 -0
  29. package/package.json +6 -5
  30. package/src/bin.ts +31 -0
  31. package/src/build.ts +147 -13
  32. package/src/cli-commands.ts +18 -0
  33. package/src/commands/__tests__/a11y-scoring.test.ts +278 -0
  34. package/src/commands/a11y-report.ts +625 -0
  35. package/src/commands/a11y.ts +168 -14
  36. package/src/commands/build.ts +16 -0
  37. package/src/commands/graph.ts +274 -0
  38. package/src/core/auto-props.ts +464 -0
  39. package/src/core/composition.ts +64 -1
  40. package/src/core/graph-extractor.test.ts +542 -0
  41. package/src/core/graph-extractor.ts +601 -0
  42. package/src/core/importAnalyzer.ts +5 -0
  43. package/src/core/schema.ts +2 -0
  44. package/src/core/types.ts +3 -1
  45. package/src/index.ts +4 -0
  46. package/src/mcp/server.ts +13 -220
  47. package/src/theme/__tests__/component-contrast.test.ts +338 -0
  48. package/src/theme/__tests__/contrast-validation.test.ts +326 -0
  49. package/src/theme/contrast.test.ts +331 -0
  50. package/src/theme/contrast.ts +246 -0
  51. package/src/theme/generator.ts +213 -1
  52. package/src/theme/index.ts +16 -0
  53. package/src/theme/types.ts +51 -0
  54. package/src/viewer/__tests__/a11y-fixes.test.ts +358 -0
  55. package/src/viewer/__tests__/viewer-integration.test.ts +2 -7
  56. package/src/viewer/components/AccessibilityPanel.tsx +493 -433
  57. package/src/viewer/components/ActionCapture.tsx +1 -1
  58. package/src/viewer/components/ActionsPanel.tsx +142 -183
  59. package/src/viewer/components/App.tsx +276 -183
  60. package/src/viewer/components/BottomPanel.tsx +40 -80
  61. package/src/viewer/components/CodePanel.tsx +9 -87
  62. package/src/viewer/components/CommandPalette.tsx +117 -74
  63. package/src/viewer/components/ComponentGraph.tsx +143 -126
  64. package/src/viewer/components/ComponentHeader.tsx +46 -43
  65. package/src/viewer/components/ContractPanel.tsx +124 -117
  66. package/src/viewer/components/ErrorBoundary.tsx +47 -35
  67. package/src/viewer/components/FigmaEmbed.tsx +18 -13
  68. package/src/viewer/components/FragmentEditor.tsx +126 -63
  69. package/src/viewer/components/HealthDashboard.tsx +146 -171
  70. package/src/viewer/components/HmrStatusIndicator.tsx +31 -41
  71. package/src/viewer/components/Icons.tsx +151 -98
  72. package/src/viewer/components/InteractionsPanel.tsx +317 -264
  73. package/src/viewer/components/IsolatedPreviewFrame.tsx +52 -27
  74. package/src/viewer/components/IsolatedRender.tsx +12 -6
  75. package/src/viewer/components/KeyboardShortcutsHelp.tsx +34 -70
  76. package/src/viewer/components/LandingPage.tsx +285 -305
  77. package/src/viewer/components/Layout.tsx +12 -10
  78. package/src/viewer/components/LeftSidebar.tsx +103 -155
  79. package/src/viewer/components/MultiViewportPreview.tsx +254 -63
  80. package/src/viewer/components/PreviewArea.tsx +113 -44
  81. package/src/viewer/components/PreviewFrameHost.tsx +36 -6
  82. package/src/viewer/components/PreviewPane.tsx +2 -3
  83. package/src/viewer/components/PreviewToolbar.tsx +109 -105
  84. package/src/viewer/components/PropsEditor.tsx +154 -74
  85. package/src/viewer/components/PropsTable.tsx +95 -82
  86. package/src/viewer/components/RelationsSection.tsx +71 -40
  87. package/src/viewer/components/ResizablePanel.tsx +158 -55
  88. package/src/viewer/components/RightSidebar.tsx +46 -56
  89. package/src/viewer/components/ScreenshotButton.tsx +12 -12
  90. package/src/viewer/components/SkeletonLoader.tsx +99 -83
  91. package/src/viewer/components/StoryRenderer.tsx +4 -11
  92. package/src/viewer/components/Toast.tsx +3 -67
  93. package/src/viewer/components/TokenStylePanel.tsx +136 -118
  94. package/src/viewer/components/UsageSection.tsx +26 -26
  95. package/src/viewer/components/VariantMatrix.tsx +140 -47
  96. package/src/viewer/components/VariantTabs.tsx +24 -68
  97. package/src/viewer/components/ViewportSelector.tsx +121 -114
  98. package/src/viewer/constants/ui.ts +23 -22
  99. package/src/viewer/entry.tsx +8 -3
  100. package/src/viewer/index.ts +3 -6
  101. package/src/viewer/preview-frame.html +43 -18
  102. package/src/viewer/server.ts +7 -16
  103. package/src/viewer/styles/globals.css +46 -85
  104. package/src/viewer/utils/a11y-fixes.ts +53 -30
  105. package/dist/chunk-ICAIQ57V.js.map +0 -1
  106. package/dist/chunk-U4GQ2JTD.js +0 -832
  107. package/dist/chunk-U4GQ2JTD.js.map +0 -1
  108. package/dist/scan-ESEXV7LF.js +0 -12
  109. package/dist/static-viewer-O37MJ5B6.js +0 -12
  110. package/dist/viewer-YDGFDTK5.js +0 -11104
  111. package/dist/viewer-YDGFDTK5.js.map +0 -1
  112. package/src/viewer/postcss.config.js +0 -6
  113. package/src/viewer/tailwind.config.js +0 -37
  114. /package/dist/{chunk-IOJE35DZ.js.map → chunk-NWQ4CJOQ.js.map} +0 -0
  115. /package/dist/{chunk-2DJH4F4P.js.map → chunk-RVRTRESS.js.map} +0 -0
  116. /package/dist/{chunk-XNWDI6UT.js.map → chunk-XHUDJNN3.js.map} +0 -0
  117. /package/dist/{core-DKHB7FYV.js.map → core-W2HYIQW6.js.map} +0 -0
  118. /package/dist/{generate-KL24VZVD.js.map → generate-LMTISDIJ.js.map} +0 -0
  119. /package/dist/{init-NION5S3M.js.map → init-7CHRKQ7P.js.map} +0 -0
  120. /package/dist/{scan-ESEXV7LF.js.map → scan-WY23TJCP.js.map} +0 -0
  121. /package/dist/{service-RWUMZ3EW.js.map → service-T2L7VLTE.js.map} +0 -0
  122. /package/dist/{static-viewer-O37MJ5B6.js.map → static-viewer-GBR7YNF3.js.map} +0 -0
  123. /package/dist/{test-ECPEXFDN.js.map → test-OJRXNDO2.js.map} +0 -0
  124. /package/dist/{tokens-ITADYVPF.js.map → tokens-3BWDESVM.js.map} +0 -0
@@ -0,0 +1,18 @@
1
+ /**
2
+ * CLI command metadata — re-exported from @fragments-sdk/context for convenience.
3
+ *
4
+ * The source of truth lives in @fragments-sdk/context/cli-commands so that
5
+ * browser-safe consumers (like the docs site) can import without pulling in
6
+ * Node.js dependencies from the CLI package.
7
+ */
8
+ export {
9
+ CLI_COMMANDS,
10
+ CLI_COMMAND_CATEGORIES,
11
+ } from '@fragments-sdk/context/cli-commands';
12
+
13
+ export type {
14
+ CliCommandDef,
15
+ CliOptionDef,
16
+ CliCommandCategory,
17
+ CliCategoryInfo,
18
+ } from '@fragments-sdk/context/cli-commands';
@@ -0,0 +1,278 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { calculateA11yScore, formatGitHub } from '../a11y.js';
3
+ import type { A11ySummary, A11yComponentResult, A11yScore } from '../a11y.js';
4
+
5
+ function makeSummary(overrides?: Partial<A11ySummary>): A11ySummary {
6
+ return {
7
+ totalComponents: 5,
8
+ accessibleComponents: 5,
9
+ accessiblePercent: 100,
10
+ components: [],
11
+ totalViolations: 0,
12
+ totalCritical: 0,
13
+ totalSerious: 0,
14
+ totalModerate: 0,
15
+ totalMinor: 0,
16
+ passed: true,
17
+ ...overrides,
18
+ };
19
+ }
20
+
21
+ function makeComponent(overrides?: Partial<A11yComponentResult>): A11yComponentResult {
22
+ return {
23
+ component: 'Button',
24
+ results: [],
25
+ status: 'PASS',
26
+ totalViolations: 0,
27
+ totalCritical: 0,
28
+ totalSerious: 0,
29
+ ...overrides,
30
+ };
31
+ }
32
+
33
+ describe('calculateA11yScore', () => {
34
+ describe('score calculation', () => {
35
+ it('0 violations → 100', () => {
36
+ const score = calculateA11yScore(makeSummary());
37
+ expect(score.score).toBe(100);
38
+ });
39
+
40
+ it('critical violations deduct 10 each', () => {
41
+ const score = calculateA11yScore(makeSummary({ totalCritical: 3 }));
42
+ expect(score.score).toBe(70);
43
+ });
44
+
45
+ it('serious violations deduct 5 each', () => {
46
+ const score = calculateA11yScore(makeSummary({ totalSerious: 4 }));
47
+ expect(score.score).toBe(80);
48
+ });
49
+
50
+ it('moderate violations deduct 2 each', () => {
51
+ const score = calculateA11yScore(makeSummary({ totalModerate: 5 }));
52
+ expect(score.score).toBe(90);
53
+ });
54
+
55
+ it('minor violations deduct 1 each', () => {
56
+ const score = calculateA11yScore(makeSummary({ totalMinor: 10 }));
57
+ expect(score.score).toBe(90);
58
+ });
59
+
60
+ it('mixed violations: 2 critical + 3 serious + 5 moderate + 10 minor = 45', () => {
61
+ const score = calculateA11yScore(makeSummary({
62
+ totalCritical: 2,
63
+ totalSerious: 3,
64
+ totalModerate: 5,
65
+ totalMinor: 10,
66
+ }));
67
+ // 100 - 20 - 15 - 10 - 10 = 45
68
+ expect(score.score).toBe(45);
69
+ });
70
+
71
+ it('floors at 0 when deductions exceed 100', () => {
72
+ const score = calculateA11yScore(makeSummary({ totalCritical: 15 }));
73
+ expect(score.score).toBe(0);
74
+ });
75
+ });
76
+
77
+ describe('aaPercent', () => {
78
+ it('all clean components → 100%', () => {
79
+ const components = [
80
+ makeComponent({ component: 'Button' }),
81
+ makeComponent({ component: 'Input' }),
82
+ ];
83
+ const score = calculateA11yScore(makeSummary({
84
+ totalComponents: 2,
85
+ components,
86
+ }));
87
+ expect(score.aaPercent).toBe(100);
88
+ });
89
+
90
+ it('all failing components → 0%', () => {
91
+ const components = [
92
+ makeComponent({ component: 'Button', totalCritical: 2, totalSerious: 0 }),
93
+ makeComponent({ component: 'Input', totalCritical: 0, totalSerious: 1 }),
94
+ ];
95
+ const score = calculateA11yScore(makeSummary({
96
+ totalComponents: 2,
97
+ components,
98
+ }));
99
+ expect(score.aaPercent).toBe(0);
100
+ });
101
+
102
+ it('partial → correct percentage', () => {
103
+ const components = [
104
+ makeComponent({ component: 'Button' }), // passes AA
105
+ makeComponent({ component: 'Input', totalCritical: 1 }), // fails AA
106
+ makeComponent({ component: 'Card' }), // passes AA
107
+ makeComponent({ component: 'Dialog', totalSerious: 2 }), // fails AA
108
+ ];
109
+ const score = calculateA11yScore(makeSummary({
110
+ totalComponents: 4,
111
+ components,
112
+ }));
113
+ expect(score.aaPercent).toBe(50); // 2/4
114
+ });
115
+
116
+ it('0 components → 100%', () => {
117
+ const score = calculateA11yScore(makeSummary({ totalComponents: 0, components: [] }));
118
+ expect(score.aaPercent).toBe(100);
119
+ });
120
+ });
121
+
122
+ describe('aaaPercent', () => {
123
+ it('zero violations → 100%', () => {
124
+ const components = [
125
+ makeComponent({ component: 'Button', totalViolations: 0 }),
126
+ makeComponent({ component: 'Input', totalViolations: 0 }),
127
+ ];
128
+ const score = calculateA11yScore(makeSummary({
129
+ totalComponents: 2,
130
+ components,
131
+ }));
132
+ expect(score.aaaPercent).toBe(100);
133
+ });
134
+
135
+ it('any violations → 0% for affected components', () => {
136
+ const components = [
137
+ makeComponent({ component: 'Button', totalViolations: 1 }),
138
+ makeComponent({ component: 'Input', totalViolations: 0 }),
139
+ ];
140
+ const score = calculateA11yScore(makeSummary({
141
+ totalComponents: 2,
142
+ components,
143
+ }));
144
+ expect(score.aaaPercent).toBe(50);
145
+ });
146
+
147
+ it('all with violations → 0%', () => {
148
+ const components = [
149
+ makeComponent({ component: 'Button', totalViolations: 2 }),
150
+ makeComponent({ component: 'Input', totalViolations: 1 }),
151
+ ];
152
+ const score = calculateA11yScore(makeSummary({
153
+ totalComponents: 2,
154
+ components,
155
+ }));
156
+ expect(score.aaaPercent).toBe(0);
157
+ });
158
+
159
+ it('aaPercent >= aaaPercent', () => {
160
+ const components = [
161
+ makeComponent({ component: 'Button', totalViolations: 1, totalCritical: 0, totalSerious: 0 }),
162
+ makeComponent({ component: 'Input', totalViolations: 0, totalCritical: 0, totalSerious: 0 }),
163
+ ];
164
+ const score = calculateA11yScore(makeSummary({
165
+ totalComponents: 2,
166
+ components,
167
+ }));
168
+ expect(score.aaPercent).toBeGreaterThanOrEqual(score.aaaPercent);
169
+ });
170
+ });
171
+ });
172
+
173
+ describe('formatGitHub', () => {
174
+ describe('badges', () => {
175
+ it('passed=true → "passing" + brightgreen', () => {
176
+ const output = formatGitHub(makeSummary({ passed: true }));
177
+ expect(output).toContain('passing');
178
+ expect(output).toContain('brightgreen');
179
+ });
180
+
181
+ it('passed=false → "failing" + red', () => {
182
+ const output = formatGitHub(makeSummary({
183
+ passed: false,
184
+ totalCritical: 1,
185
+ components: [makeComponent({ component: 'Button', totalCritical: 1 })],
186
+ }));
187
+ expect(output).toContain('failing');
188
+ expect(output).toContain('-red)');
189
+ });
190
+ });
191
+
192
+ describe('content', () => {
193
+ it('includes markdown table header', () => {
194
+ const output = formatGitHub(makeSummary({
195
+ components: [makeComponent()],
196
+ }));
197
+ expect(output).toContain('| Component |');
198
+ expect(output).toContain('| Variants |');
199
+ });
200
+
201
+ it('includes component names in table rows', () => {
202
+ const components = [
203
+ makeComponent({ component: 'Button' }),
204
+ makeComponent({ component: 'Dialog' }),
205
+ ];
206
+ const output = formatGitHub(makeSummary({
207
+ totalComponents: 2,
208
+ components,
209
+ }));
210
+ expect(output).toContain('| Button |');
211
+ expect(output).toContain('| Dialog |');
212
+ });
213
+
214
+ it('includes blocking count when critical + serious > 0', () => {
215
+ const output = formatGitHub(makeSummary({
216
+ passed: false,
217
+ totalCritical: 2,
218
+ totalSerious: 3,
219
+ components: [makeComponent({ component: 'Button', totalCritical: 2, totalSerious: 3 })],
220
+ }));
221
+ expect(output).toContain('**Blocking:** 5');
222
+ });
223
+
224
+ it('no blocking line when 0 critical + serious', () => {
225
+ const output = formatGitHub(makeSummary({
226
+ totalCritical: 0,
227
+ totalSerious: 0,
228
+ totalModerate: 2,
229
+ components: [makeComponent({ component: 'Button', totalViolations: 2 })],
230
+ }));
231
+ expect(output).not.toContain('**Blocking:**');
232
+ });
233
+ });
234
+
235
+ describe('score', () => {
236
+ it('uses pre-calculated score when present', () => {
237
+ const preCalc: A11yScore = { score: 42, aaPercent: 50, aaaPercent: 25 };
238
+ const output = formatGitHub(makeSummary({ score: preCalc }));
239
+ expect(output).toContain('42/100');
240
+ expect(output).toContain('50%');
241
+ expect(output).toContain('25%');
242
+ });
243
+
244
+ it('calculates score when missing', () => {
245
+ const summary = makeSummary({ totalMinor: 5 });
246
+ delete summary.score;
247
+ const output = formatGitHub(summary);
248
+ // 100 - 5 = 95
249
+ expect(output).toContain('95/100');
250
+ });
251
+ });
252
+
253
+ describe('violations', () => {
254
+ it('summary line includes all 4 severity counts', () => {
255
+ const output = formatGitHub(makeSummary({
256
+ totalViolations: 10,
257
+ totalCritical: 1,
258
+ totalSerious: 2,
259
+ totalModerate: 3,
260
+ totalMinor: 4,
261
+ components: [makeComponent({ component: 'Button', totalViolations: 10, totalCritical: 1, totalSerious: 2 })],
262
+ }));
263
+ expect(output).toContain('1 critical');
264
+ expect(output).toContain('2 serious');
265
+ expect(output).toContain('3 moderate');
266
+ expect(output).toContain('4 minor');
267
+ });
268
+
269
+ it('shows accessible components fraction', () => {
270
+ const output = formatGitHub(makeSummary({
271
+ totalComponents: 10,
272
+ accessibleComponents: 8,
273
+ accessiblePercent: 80,
274
+ }));
275
+ expect(output).toContain('8/10 components accessible');
276
+ });
277
+ });
278
+ });