@fragments-sdk/cli 0.2.2

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 (259) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +106 -0
  3. package/dist/bin.d.ts +1 -0
  4. package/dist/bin.js +4783 -0
  5. package/dist/bin.js.map +1 -0
  6. package/dist/chunk-4FDQSGKX.js +786 -0
  7. package/dist/chunk-4FDQSGKX.js.map +1 -0
  8. package/dist/chunk-7H2MMGYG.js +369 -0
  9. package/dist/chunk-7H2MMGYG.js.map +1 -0
  10. package/dist/chunk-BSCG3IP7.js +619 -0
  11. package/dist/chunk-BSCG3IP7.js.map +1 -0
  12. package/dist/chunk-LY2CFFPY.js +898 -0
  13. package/dist/chunk-LY2CFFPY.js.map +1 -0
  14. package/dist/chunk-MUZ6CM66.js +6636 -0
  15. package/dist/chunk-MUZ6CM66.js.map +1 -0
  16. package/dist/chunk-OAENNG3G.js +1489 -0
  17. package/dist/chunk-OAENNG3G.js.map +1 -0
  18. package/dist/chunk-XHNKNI6J.js +235 -0
  19. package/dist/chunk-XHNKNI6J.js.map +1 -0
  20. package/dist/core-DWKLGY4N.js +68 -0
  21. package/dist/core-DWKLGY4N.js.map +1 -0
  22. package/dist/generate-4LQNJ7SX.js +249 -0
  23. package/dist/generate-4LQNJ7SX.js.map +1 -0
  24. package/dist/index.d.ts +775 -0
  25. package/dist/index.js +41 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/init-EMVI47QG.js +416 -0
  28. package/dist/init-EMVI47QG.js.map +1 -0
  29. package/dist/mcp-bin.d.ts +1 -0
  30. package/dist/mcp-bin.js +1117 -0
  31. package/dist/mcp-bin.js.map +1 -0
  32. package/dist/scan-4YPRF7FV.js +12 -0
  33. package/dist/scan-4YPRF7FV.js.map +1 -0
  34. package/dist/service-QSZMZJBJ.js +208 -0
  35. package/dist/service-QSZMZJBJ.js.map +1 -0
  36. package/dist/static-viewer-MIPGZ4Z7.js +12 -0
  37. package/dist/static-viewer-MIPGZ4Z7.js.map +1 -0
  38. package/dist/test-SQ5ZHXWU.js +1067 -0
  39. package/dist/test-SQ5ZHXWU.js.map +1 -0
  40. package/dist/tokens-HSGMYK64.js +173 -0
  41. package/dist/tokens-HSGMYK64.js.map +1 -0
  42. package/dist/viewer-YRF4SQE4.js +11101 -0
  43. package/dist/viewer-YRF4SQE4.js.map +1 -0
  44. package/package.json +107 -0
  45. package/src/ai.ts +266 -0
  46. package/src/analyze.ts +265 -0
  47. package/src/bin.ts +916 -0
  48. package/src/build.ts +248 -0
  49. package/src/commands/a11y.ts +302 -0
  50. package/src/commands/add.ts +313 -0
  51. package/src/commands/audit.ts +195 -0
  52. package/src/commands/baseline.ts +221 -0
  53. package/src/commands/build.ts +144 -0
  54. package/src/commands/compare.ts +337 -0
  55. package/src/commands/context.ts +107 -0
  56. package/src/commands/dev.ts +107 -0
  57. package/src/commands/enhance.ts +858 -0
  58. package/src/commands/generate.ts +391 -0
  59. package/src/commands/init.ts +531 -0
  60. package/src/commands/link/figma.ts +645 -0
  61. package/src/commands/link/index.ts +10 -0
  62. package/src/commands/link/storybook.ts +267 -0
  63. package/src/commands/list.ts +49 -0
  64. package/src/commands/metrics.ts +114 -0
  65. package/src/commands/reset.ts +242 -0
  66. package/src/commands/scan.ts +537 -0
  67. package/src/commands/storygen.ts +207 -0
  68. package/src/commands/tokens.ts +251 -0
  69. package/src/commands/validate.ts +93 -0
  70. package/src/commands/verify.ts +215 -0
  71. package/src/core/composition.test.ts +262 -0
  72. package/src/core/composition.ts +255 -0
  73. package/src/core/config.ts +84 -0
  74. package/src/core/constants.ts +111 -0
  75. package/src/core/context.ts +380 -0
  76. package/src/core/defineSegment.ts +137 -0
  77. package/src/core/discovery.ts +337 -0
  78. package/src/core/figma.ts +263 -0
  79. package/src/core/fragment-types.ts +214 -0
  80. package/src/core/generators/context.ts +389 -0
  81. package/src/core/generators/index.ts +23 -0
  82. package/src/core/generators/registry.ts +364 -0
  83. package/src/core/generators/typescript-extractor.ts +374 -0
  84. package/src/core/importAnalyzer.ts +217 -0
  85. package/src/core/index.ts +149 -0
  86. package/src/core/loader.ts +155 -0
  87. package/src/core/node.ts +63 -0
  88. package/src/core/parser.ts +551 -0
  89. package/src/core/previewLoader.ts +172 -0
  90. package/src/core/schema/fragment.schema.json +189 -0
  91. package/src/core/schema/registry.schema.json +137 -0
  92. package/src/core/schema.ts +182 -0
  93. package/src/core/storyAdapter.test.ts +571 -0
  94. package/src/core/storyAdapter.ts +761 -0
  95. package/src/core/token-types.ts +287 -0
  96. package/src/core/types.ts +754 -0
  97. package/src/diff.ts +323 -0
  98. package/src/index.ts +43 -0
  99. package/src/mcp/__tests__/projectFields.test.ts +130 -0
  100. package/src/mcp/bin.ts +36 -0
  101. package/src/mcp/index.ts +8 -0
  102. package/src/mcp/server.ts +1310 -0
  103. package/src/mcp/utils.ts +54 -0
  104. package/src/mcp-bin.ts +36 -0
  105. package/src/migrate/__tests__/argTypes/argTypes.test.ts +189 -0
  106. package/src/migrate/__tests__/args/args.test.ts +452 -0
  107. package/src/migrate/__tests__/meta/meta.test.ts +198 -0
  108. package/src/migrate/__tests__/stories/stories.test.ts +278 -0
  109. package/src/migrate/__tests__/utils/utils.test.ts +371 -0
  110. package/src/migrate/__tests__/values/values.test.ts +303 -0
  111. package/src/migrate/bin.ts +108 -0
  112. package/src/migrate/converter.ts +658 -0
  113. package/src/migrate/detect.ts +196 -0
  114. package/src/migrate/index.ts +45 -0
  115. package/src/migrate/migrate.ts +163 -0
  116. package/src/migrate/parser.ts +1136 -0
  117. package/src/migrate/report.ts +624 -0
  118. package/src/migrate/types.ts +169 -0
  119. package/src/screenshot.ts +249 -0
  120. package/src/service/__tests__/ast-utils.test.ts +426 -0
  121. package/src/service/__tests__/enhance-scanner.test.ts +200 -0
  122. package/src/service/__tests__/figma/figma.test.ts +652 -0
  123. package/src/service/__tests__/metrics-store.test.ts +409 -0
  124. package/src/service/__tests__/patch-generator.test.ts +186 -0
  125. package/src/service/__tests__/props-extractor.test.ts +365 -0
  126. package/src/service/__tests__/token-registry.test.ts +267 -0
  127. package/src/service/analytics.ts +659 -0
  128. package/src/service/ast-utils.ts +444 -0
  129. package/src/service/browser-pool.ts +339 -0
  130. package/src/service/capture.ts +267 -0
  131. package/src/service/diff.ts +279 -0
  132. package/src/service/enhance/aggregator.ts +489 -0
  133. package/src/service/enhance/cache.ts +275 -0
  134. package/src/service/enhance/codebase-scanner.ts +357 -0
  135. package/src/service/enhance/context-generator.ts +529 -0
  136. package/src/service/enhance/doc-extractor.ts +523 -0
  137. package/src/service/enhance/index.ts +131 -0
  138. package/src/service/enhance/props-extractor.ts +665 -0
  139. package/src/service/enhance/scanner.ts +445 -0
  140. package/src/service/enhance/storybook-parser.ts +552 -0
  141. package/src/service/enhance/types.ts +346 -0
  142. package/src/service/enhance/variant-renderer.ts +479 -0
  143. package/src/service/figma.ts +1008 -0
  144. package/src/service/index.ts +249 -0
  145. package/src/service/metrics-store.ts +333 -0
  146. package/src/service/patch-generator.ts +349 -0
  147. package/src/service/report.ts +854 -0
  148. package/src/service/storage.ts +401 -0
  149. package/src/service/token-fixes.ts +281 -0
  150. package/src/service/token-parser.ts +504 -0
  151. package/src/service/token-registry.ts +721 -0
  152. package/src/service/utils.ts +172 -0
  153. package/src/setup.ts +241 -0
  154. package/src/shared/command-wrapper.ts +81 -0
  155. package/src/shared/dev-server-client.ts +199 -0
  156. package/src/shared/index.ts +8 -0
  157. package/src/shared/segment-loader.ts +59 -0
  158. package/src/shared/types.ts +147 -0
  159. package/src/static-viewer.ts +715 -0
  160. package/src/test/discovery.ts +172 -0
  161. package/src/test/index.ts +281 -0
  162. package/src/test/reporters/console.ts +194 -0
  163. package/src/test/reporters/json.ts +190 -0
  164. package/src/test/reporters/junit.ts +186 -0
  165. package/src/test/runner.ts +598 -0
  166. package/src/test/types.ts +245 -0
  167. package/src/test/watch.ts +200 -0
  168. package/src/validators.ts +152 -0
  169. package/src/viewer/__tests__/jsx-parser.test.ts +502 -0
  170. package/src/viewer/__tests__/render-utils.test.ts +232 -0
  171. package/src/viewer/__tests__/style-utils.test.ts +404 -0
  172. package/src/viewer/bin.ts +86 -0
  173. package/src/viewer/cli/health.ts +256 -0
  174. package/src/viewer/cli/index.ts +33 -0
  175. package/src/viewer/cli/scan.ts +124 -0
  176. package/src/viewer/cli/utils.ts +174 -0
  177. package/src/viewer/components/AccessibilityPanel.tsx +1404 -0
  178. package/src/viewer/components/ActionCapture.tsx +172 -0
  179. package/src/viewer/components/ActionsPanel.tsx +371 -0
  180. package/src/viewer/components/App.tsx +638 -0
  181. package/src/viewer/components/BottomPanel.tsx +224 -0
  182. package/src/viewer/components/CodePanel.tsx +589 -0
  183. package/src/viewer/components/CommandPalette.tsx +336 -0
  184. package/src/viewer/components/ComponentGraph.tsx +394 -0
  185. package/src/viewer/components/ComponentHeader.tsx +85 -0
  186. package/src/viewer/components/ContractPanel.tsx +234 -0
  187. package/src/viewer/components/ErrorBoundary.tsx +85 -0
  188. package/src/viewer/components/FigmaEmbed.tsx +231 -0
  189. package/src/viewer/components/FragmentEditor.tsx +485 -0
  190. package/src/viewer/components/HealthDashboard.tsx +452 -0
  191. package/src/viewer/components/HmrStatusIndicator.tsx +71 -0
  192. package/src/viewer/components/Icons.tsx +417 -0
  193. package/src/viewer/components/InteractionsPanel.tsx +720 -0
  194. package/src/viewer/components/IsolatedPreviewFrame.tsx +321 -0
  195. package/src/viewer/components/IsolatedRender.tsx +111 -0
  196. package/src/viewer/components/KeyboardShortcutsHelp.tsx +89 -0
  197. package/src/viewer/components/LandingPage.tsx +441 -0
  198. package/src/viewer/components/Layout.tsx +22 -0
  199. package/src/viewer/components/LeftSidebar.tsx +391 -0
  200. package/src/viewer/components/MultiViewportPreview.tsx +429 -0
  201. package/src/viewer/components/PreviewArea.tsx +404 -0
  202. package/src/viewer/components/PreviewFrameHost.tsx +310 -0
  203. package/src/viewer/components/PreviewPane.tsx +150 -0
  204. package/src/viewer/components/PreviewToolbar.tsx +176 -0
  205. package/src/viewer/components/PropsEditor.tsx +512 -0
  206. package/src/viewer/components/PropsTable.tsx +98 -0
  207. package/src/viewer/components/RelationsSection.tsx +57 -0
  208. package/src/viewer/components/ResizablePanel.tsx +328 -0
  209. package/src/viewer/components/RightSidebar.tsx +118 -0
  210. package/src/viewer/components/ScreenshotButton.tsx +90 -0
  211. package/src/viewer/components/Sidebar.tsx +169 -0
  212. package/src/viewer/components/SkeletonLoader.tsx +156 -0
  213. package/src/viewer/components/StoryRenderer.tsx +128 -0
  214. package/src/viewer/components/ThemeProvider.tsx +96 -0
  215. package/src/viewer/components/Toast.tsx +67 -0
  216. package/src/viewer/components/TokenStylePanel.tsx +708 -0
  217. package/src/viewer/components/UsageSection.tsx +95 -0
  218. package/src/viewer/components/VariantMatrix.tsx +350 -0
  219. package/src/viewer/components/VariantRenderer.tsx +131 -0
  220. package/src/viewer/components/VariantTabs.tsx +84 -0
  221. package/src/viewer/components/ViewportSelector.tsx +165 -0
  222. package/src/viewer/components/_future/CreatePage.tsx +836 -0
  223. package/src/viewer/composition-renderer.ts +381 -0
  224. package/src/viewer/constants/index.ts +1 -0
  225. package/src/viewer/constants/ui.ts +185 -0
  226. package/src/viewer/entry.tsx +299 -0
  227. package/src/viewer/hooks/index.ts +2 -0
  228. package/src/viewer/hooks/useA11yCache.ts +383 -0
  229. package/src/viewer/hooks/useA11yService.ts +498 -0
  230. package/src/viewer/hooks/useActions.ts +138 -0
  231. package/src/viewer/hooks/useAppState.ts +124 -0
  232. package/src/viewer/hooks/useFigmaIntegration.ts +132 -0
  233. package/src/viewer/hooks/useHmrStatus.ts +109 -0
  234. package/src/viewer/hooks/useKeyboardShortcuts.ts +222 -0
  235. package/src/viewer/hooks/usePreviewBridge.ts +347 -0
  236. package/src/viewer/hooks/useScrollSpy.ts +78 -0
  237. package/src/viewer/hooks/useUrlState.ts +330 -0
  238. package/src/viewer/hooks/useViewSettings.ts +125 -0
  239. package/src/viewer/index.html +28 -0
  240. package/src/viewer/index.ts +14 -0
  241. package/src/viewer/intelligence/healthReport.ts +505 -0
  242. package/src/viewer/intelligence/styleDrift.ts +340 -0
  243. package/src/viewer/intelligence/usageScanner.ts +309 -0
  244. package/src/viewer/jsx-parser.ts +485 -0
  245. package/src/viewer/postcss.config.js +6 -0
  246. package/src/viewer/preview-frame-entry.tsx +25 -0
  247. package/src/viewer/preview-frame.html +109 -0
  248. package/src/viewer/render-template.html +68 -0
  249. package/src/viewer/render-utils.ts +170 -0
  250. package/src/viewer/server.ts +276 -0
  251. package/src/viewer/style-utils.ts +414 -0
  252. package/src/viewer/styles/globals.css +355 -0
  253. package/src/viewer/tailwind.config.js +37 -0
  254. package/src/viewer/types/a11y.ts +197 -0
  255. package/src/viewer/utils/a11y-fixes.ts +471 -0
  256. package/src/viewer/utils/actionExport.ts +372 -0
  257. package/src/viewer/utils/colorSchemes.ts +201 -0
  258. package/src/viewer/utils/detectRelationships.ts +256 -0
  259. package/src/viewer/vite-plugin.ts +2143 -0
@@ -0,0 +1,786 @@
1
+ import { createRequire } from 'module'; const require = createRequire(import.meta.url);
2
+ import {
3
+ BrowserPool,
4
+ CaptureEngine,
5
+ DiffEngine,
6
+ StorageManager,
7
+ analyzeDesignSystem,
8
+ formatMs,
9
+ generateHtmlReport,
10
+ getGrade
11
+ } from "./chunk-MUZ6CM66.js";
12
+ import {
13
+ discoverComponentFiles,
14
+ discoverRecipeFiles,
15
+ discoverSegmentFiles,
16
+ extractComponentName,
17
+ generateContextMd,
18
+ generateRegistry,
19
+ loadSegmentFile,
20
+ parseSegmentFile
21
+ } from "./chunk-OAENNG3G.js";
22
+ import {
23
+ compileRecipe
24
+ } from "./chunk-LY2CFFPY.js";
25
+ import {
26
+ BRAND,
27
+ DEFAULTS,
28
+ segmentDefinitionSchema
29
+ } from "./chunk-XHNKNI6J.js";
30
+
31
+ // src/validators.ts
32
+ async function validateSchema(config, configDir) {
33
+ const files = await discoverSegmentFiles(config, configDir);
34
+ const errors = [];
35
+ const warnings = [];
36
+ for (const file of files) {
37
+ try {
38
+ const segment = await loadSegmentFile(file.absolutePath);
39
+ if (!segment) {
40
+ errors.push({
41
+ file: file.relativePath,
42
+ message: "No default export found",
43
+ details: `Segment files must have a default export from defineSegment()`
44
+ });
45
+ continue;
46
+ }
47
+ const result = segmentDefinitionSchema.safeParse(segment);
48
+ if (!result.success) {
49
+ const details = result.error.errors.map((e) => `${e.path.join(".")}: ${e.message}`).join("; ");
50
+ errors.push({
51
+ file: file.relativePath,
52
+ message: "Invalid segment schema",
53
+ details
54
+ });
55
+ }
56
+ } catch (error) {
57
+ errors.push({
58
+ file: file.relativePath,
59
+ message: "Failed to load segment file",
60
+ details: error instanceof Error ? error.message : String(error)
61
+ });
62
+ }
63
+ }
64
+ return {
65
+ valid: errors.length === 0,
66
+ errors,
67
+ warnings
68
+ };
69
+ }
70
+ async function validateCoverage(config, configDir) {
71
+ const segmentFiles = await discoverSegmentFiles(config, configDir);
72
+ const componentFiles = await discoverComponentFiles(config, configDir);
73
+ const errors = [];
74
+ const warnings = [];
75
+ const documentedComponents = /* @__PURE__ */ new Set();
76
+ for (const file of segmentFiles) {
77
+ try {
78
+ const segment = await loadSegmentFile(file.absolutePath);
79
+ if (segment?.meta?.name) {
80
+ documentedComponents.add(segment.meta.name);
81
+ }
82
+ } catch {
83
+ }
84
+ }
85
+ for (const file of componentFiles) {
86
+ const componentName = extractComponentName(file.relativePath);
87
+ const segmentPath = file.relativePath.replace(
88
+ /\.(tsx?|jsx?)$/,
89
+ BRAND.fileExtension
90
+ );
91
+ const hasSegmentFile = segmentFiles.some(
92
+ (s) => s.relativePath === segmentPath
93
+ );
94
+ if (!hasSegmentFile && !documentedComponents.has(componentName)) {
95
+ warnings.push({
96
+ file: file.relativePath,
97
+ message: `Component "${componentName}" has no segment documentation`
98
+ });
99
+ }
100
+ }
101
+ return {
102
+ valid: errors.length === 0,
103
+ errors,
104
+ warnings
105
+ };
106
+ }
107
+ async function validateAll(config, configDir) {
108
+ const [schemaResult, coverageResult] = await Promise.all([
109
+ validateSchema(config, configDir),
110
+ validateCoverage(config, configDir)
111
+ ]);
112
+ return {
113
+ valid: schemaResult.valid && coverageResult.valid,
114
+ errors: [...schemaResult.errors, ...coverageResult.errors],
115
+ warnings: [...schemaResult.warnings, ...coverageResult.warnings]
116
+ };
117
+ }
118
+
119
+ // src/build.ts
120
+ import { readFile, writeFile, mkdir } from "fs/promises";
121
+ import { resolve, join } from "path";
122
+ async function buildSegments(config, configDir) {
123
+ const files = await discoverSegmentFiles(config, configDir);
124
+ const errors = [];
125
+ const warnings = [];
126
+ const segments = {};
127
+ for (const file of files) {
128
+ try {
129
+ const content = await readFile(file.absolutePath, "utf-8");
130
+ const parsed = parseSegmentFile(content, file.relativePath);
131
+ for (const warning of parsed.warnings) {
132
+ warnings.push({ file: file.relativePath, warning });
133
+ }
134
+ if (!parsed.meta.name) {
135
+ errors.push({
136
+ file: file.relativePath,
137
+ error: "Missing meta.name in fragment definition"
138
+ });
139
+ continue;
140
+ }
141
+ const compiled = {
142
+ filePath: file.relativePath,
143
+ meta: {
144
+ name: parsed.meta.name,
145
+ description: parsed.meta.description ?? "",
146
+ category: parsed.meta.category ?? "Uncategorized",
147
+ status: parsed.meta.status,
148
+ tags: parsed.meta.tags,
149
+ since: parsed.meta.since,
150
+ figma: parsed.meta.figma
151
+ },
152
+ usage: {
153
+ when: parsed.usage.when ?? [],
154
+ whenNot: parsed.usage.whenNot ?? [],
155
+ guidelines: parsed.usage.guidelines,
156
+ accessibility: parsed.usage.accessibility
157
+ },
158
+ props: Object.fromEntries(
159
+ Object.entries(parsed.props).map(([name, prop]) => [
160
+ name,
161
+ {
162
+ type: prop.type ?? "custom",
163
+ description: prop.description ?? "",
164
+ default: prop.default,
165
+ required: prop.required,
166
+ values: prop.values,
167
+ constraints: prop.constraints
168
+ }
169
+ ])
170
+ ),
171
+ relations: parsed.relations.map((rel) => ({
172
+ component: rel.component,
173
+ relationship: rel.relationship,
174
+ note: rel.note
175
+ })),
176
+ variants: parsed.variants.map((v) => ({
177
+ name: v.name,
178
+ description: v.description,
179
+ ...v.code && { code: v.code },
180
+ ...v.figma && { figma: v.figma },
181
+ ...v.args && { args: v.args }
182
+ }))
183
+ };
184
+ segments[parsed.meta.name] = compiled;
185
+ } catch (error) {
186
+ errors.push({
187
+ file: file.relativePath,
188
+ error: error instanceof Error ? error.message : String(error)
189
+ });
190
+ }
191
+ }
192
+ const recipes = {};
193
+ try {
194
+ const recipeFiles = await discoverRecipeFiles(configDir, config.exclude);
195
+ for (const file of recipeFiles) {
196
+ try {
197
+ let raw = await loadSegmentFile(file.absolutePath);
198
+ if (raw && "default" in raw && typeof raw.default === "object") {
199
+ raw = raw.default;
200
+ }
201
+ const def = raw;
202
+ if (def && typeof def === "object" && "name" in def && "code" in def && "components" in def) {
203
+ const compiled = compileRecipe(def, file.relativePath);
204
+ recipes[compiled.name] = compiled;
205
+ }
206
+ } catch (error) {
207
+ warnings.push({
208
+ file: file.relativePath,
209
+ warning: `Failed to load recipe: ${error instanceof Error ? error.message : String(error)}`
210
+ });
211
+ }
212
+ }
213
+ } catch {
214
+ }
215
+ const output = {
216
+ version: "1.0.0",
217
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
218
+ segments,
219
+ ...Object.keys(recipes).length > 0 && { recipes }
220
+ };
221
+ const outputPath = resolve(configDir, config.outFile ?? BRAND.outFile);
222
+ await writeFile(outputPath, JSON.stringify(output));
223
+ return {
224
+ success: errors.length === 0,
225
+ outputPath,
226
+ segmentCount: Object.keys(segments).length,
227
+ errors,
228
+ warnings
229
+ };
230
+ }
231
+ async function buildFragmentsDir(config, configDir) {
232
+ const fragmentsDir = join(configDir, BRAND.dataDir);
233
+ const componentsDir = join(fragmentsDir, BRAND.componentsDir);
234
+ await mkdir(fragmentsDir, { recursive: true });
235
+ await mkdir(componentsDir, { recursive: true });
236
+ const registryResult = await generateRegistry({
237
+ projectRoot: configDir,
238
+ componentPatterns: config.components || ["src/**/*.tsx", "src/**/*.ts"],
239
+ storyPatterns: config.include || ["src/**/*.stories.tsx"],
240
+ fragmentsDir,
241
+ registryOptions: config.registry || {}
242
+ });
243
+ const errors = [...registryResult.errors];
244
+ const warnings = [...registryResult.warnings];
245
+ const indexPath = join(fragmentsDir, "index.json");
246
+ await writeFile(indexPath, JSON.stringify(registryResult.index, null, 2));
247
+ const registryPath = join(fragmentsDir, BRAND.registryFile);
248
+ await writeFile(registryPath, JSON.stringify(registryResult.registry, null, 2));
249
+ const contextResult = generateContextMd(registryResult.registry, {
250
+ format: "markdown",
251
+ compact: false,
252
+ include: {
253
+ props: false,
254
+ // AI can read TypeScript directly
255
+ relations: true,
256
+ code: false
257
+ }
258
+ });
259
+ const contextPath = join(fragmentsDir, BRAND.contextFile);
260
+ await writeFile(contextPath, contextResult.content);
261
+ return {
262
+ success: errors.length === 0,
263
+ indexPath,
264
+ registryPath,
265
+ contextPath,
266
+ componentCount: registryResult.registry.componentCount,
267
+ errors,
268
+ warnings
269
+ };
270
+ }
271
+
272
+ // src/screenshot.ts
273
+ import pc from "picocolors";
274
+ async function runScreenshotCommand(config, configDir, options = {}) {
275
+ const startTime = Date.now();
276
+ const errors = [];
277
+ const storage = new StorageManager({
278
+ projectRoot: configDir,
279
+ viewport: options.width && options.height ? { width: options.width, height: options.height } : config.screenshots?.viewport
280
+ });
281
+ await storage.initialize();
282
+ const segmentFiles = await discoverSegmentFiles(config, configDir);
283
+ if (segmentFiles.length === 0) {
284
+ console.log(pc.yellow("No segment files found."));
285
+ return {
286
+ success: true,
287
+ captured: 0,
288
+ skipped: 0,
289
+ errors: [],
290
+ totalTimeMs: Date.now() - startTime
291
+ };
292
+ }
293
+ const segments = [];
294
+ for (const file of segmentFiles) {
295
+ try {
296
+ const segment = await loadSegmentFile(file.absolutePath);
297
+ if (segment) {
298
+ segments.push({ path: file.relativePath, segment });
299
+ }
300
+ } catch (error) {
301
+ errors.push({
302
+ component: file.relativePath,
303
+ variant: "",
304
+ error: error instanceof Error ? error.message : String(error)
305
+ });
306
+ }
307
+ }
308
+ const filteredSegments = options.component ? segments.filter((s) => s.segment.meta.name === options.component) : segments;
309
+ if (options.component && filteredSegments.length === 0) {
310
+ console.log(pc.yellow(`Component "${options.component}" not found.`));
311
+ return {
312
+ success: false,
313
+ captured: 0,
314
+ skipped: 0,
315
+ errors: [],
316
+ totalTimeMs: Date.now() - startTime
317
+ };
318
+ }
319
+ const variantsToCapture = [];
320
+ for (const { segment } of filteredSegments) {
321
+ const variants = options.variant ? segment.variants.filter((v) => v.name === options.variant) : segment.variants;
322
+ for (const variant of variants) {
323
+ variantsToCapture.push({
324
+ component: segment.meta.name,
325
+ variant: variant.name,
326
+ render: variant.render
327
+ });
328
+ }
329
+ }
330
+ if (variantsToCapture.length === 0) {
331
+ console.log(pc.yellow("No variants to capture."));
332
+ return {
333
+ success: true,
334
+ captured: 0,
335
+ skipped: 0,
336
+ errors: [],
337
+ totalTimeMs: Date.now() - startTime
338
+ };
339
+ }
340
+ const theme = options.theme ?? DEFAULTS.theme;
341
+ const viewport = {
342
+ width: options.width ?? config.screenshots?.viewport?.width ?? DEFAULTS.viewport.width,
343
+ height: options.height ?? config.screenshots?.viewport?.height ?? DEFAULTS.viewport.height
344
+ };
345
+ console.log(pc.cyan(`
346
+ ${BRAND.name} Screenshot
347
+ `));
348
+ console.log(pc.dim(`Capturing variants (theme: ${theme}, viewport: ${viewport.width}x${viewport.height}):
349
+ `));
350
+ const pool = new BrowserPool({
351
+ viewport
352
+ });
353
+ const viewerPort = DEFAULTS.port;
354
+ const baseUrl = `http://localhost:${viewerPort}`;
355
+ const captureEngine = new CaptureEngine(pool, baseUrl);
356
+ let captured = 0;
357
+ let skipped = 0;
358
+ const captureOptions = {
359
+ theme,
360
+ viewport,
361
+ delay: config.screenshots?.delay ?? DEFAULTS.captureDelayMs
362
+ };
363
+ try {
364
+ console.log(pc.dim("Starting browser..."));
365
+ await pool.warmup();
366
+ console.log(pc.dim("Browser ready.\n"));
367
+ for (const { component, variant } of variantsToCapture) {
368
+ const hasExisting = storage.hasBaseline(component, variant, theme);
369
+ if (hasExisting && !options.update) {
370
+ console.log(` ${pc.dim("\u25CB")} ${component}/${variant} ${pc.dim("(skipped)")}`);
371
+ skipped++;
372
+ continue;
373
+ }
374
+ try {
375
+ const screenshot = await captureEngine.captureVariant(
376
+ component,
377
+ variant,
378
+ captureOptions
379
+ );
380
+ await storage.saveBaseline(screenshot);
381
+ const totalTime = screenshot.metadata.renderTimeMs + screenshot.metadata.captureTimeMs;
382
+ console.log(
383
+ ` ${pc.green("\u2713")} ${component}/${variant} ${pc.dim(formatMs(totalTime))}`
384
+ );
385
+ captured++;
386
+ } catch (error) {
387
+ const errorMsg = error instanceof Error ? error.message : String(error);
388
+ console.log(` ${pc.red("\u2717")} ${component}/${variant} ${pc.dim(errorMsg)}`);
389
+ errors.push({ component, variant, error: errorMsg });
390
+ }
391
+ }
392
+ } finally {
393
+ await pool.shutdown();
394
+ }
395
+ const totalTimeMs = Date.now() - startTime;
396
+ console.log();
397
+ if (errors.length === 0) {
398
+ console.log(pc.green(`\u2713 Captured ${captured} screenshot(s) in ${formatMs(totalTimeMs)}`));
399
+ } else {
400
+ console.log(pc.yellow(`\u26A0 Captured ${captured} screenshot(s) with ${errors.length} error(s)`));
401
+ }
402
+ if (skipped > 0) {
403
+ console.log(pc.dim(` ${skipped} skipped (use --update to recapture)`));
404
+ }
405
+ console.log(pc.dim(` Stored in ${storage.screenshotsDirPath}
406
+ `));
407
+ return {
408
+ success: errors.length === 0,
409
+ captured,
410
+ skipped,
411
+ errors,
412
+ totalTimeMs
413
+ };
414
+ }
415
+
416
+ // src/diff.ts
417
+ import pc2 from "picocolors";
418
+ async function runDiffCommand(config, configDir, options = {}) {
419
+ const startTime = Date.now();
420
+ const results = [];
421
+ const storage = new StorageManager({
422
+ projectRoot: configDir,
423
+ viewport: config.screenshots?.viewport
424
+ });
425
+ await storage.initialize();
426
+ const threshold = options.threshold ?? config.screenshots?.threshold ?? DEFAULTS.diffThreshold;
427
+ const diffEngine = new DiffEngine(threshold);
428
+ const segmentFiles = await discoverSegmentFiles(config, configDir);
429
+ if (segmentFiles.length === 0) {
430
+ console.log(pc2.yellow("No segment files found."));
431
+ return {
432
+ success: true,
433
+ total: 0,
434
+ passed: 0,
435
+ failed: 0,
436
+ missing: 0,
437
+ results: [],
438
+ totalTimeMs: Date.now() - startTime
439
+ };
440
+ }
441
+ const segments = [];
442
+ for (const file of segmentFiles) {
443
+ try {
444
+ const segment = await loadSegmentFile(file.absolutePath);
445
+ if (segment) {
446
+ segments.push({ path: file.relativePath, segment });
447
+ }
448
+ } catch {
449
+ }
450
+ }
451
+ const filteredSegments = options.component ? segments.filter((s) => s.segment.meta.name === options.component) : segments;
452
+ if (options.component && filteredSegments.length === 0) {
453
+ console.log(pc2.yellow(`Component "${options.component}" not found.`));
454
+ return {
455
+ success: false,
456
+ total: 0,
457
+ passed: 0,
458
+ failed: 0,
459
+ missing: 0,
460
+ results: [],
461
+ totalTimeMs: Date.now() - startTime
462
+ };
463
+ }
464
+ const variantsToDiff = [];
465
+ for (const { segment } of filteredSegments) {
466
+ const variants = options.variant ? segment.variants.filter((v) => v.name === options.variant) : segment.variants;
467
+ for (const variant of variants) {
468
+ variantsToDiff.push({
469
+ component: segment.meta.name,
470
+ variant: variant.name
471
+ });
472
+ }
473
+ }
474
+ if (variantsToDiff.length === 0) {
475
+ console.log(pc2.yellow("No variants to compare."));
476
+ return {
477
+ success: true,
478
+ total: 0,
479
+ passed: 0,
480
+ failed: 0,
481
+ missing: 0,
482
+ results: [],
483
+ totalTimeMs: Date.now() - startTime
484
+ };
485
+ }
486
+ const theme = options.theme ?? DEFAULTS.theme;
487
+ const viewport = config.screenshots?.viewport ?? DEFAULTS.viewport;
488
+ console.log(pc2.cyan(`
489
+ ${BRAND.name} Diff
490
+ `));
491
+ console.log(pc2.dim(`Comparing against baselines (theme: ${theme}, threshold: ${threshold}%):
492
+ `));
493
+ const pool = new BrowserPool({
494
+ viewport
495
+ });
496
+ const viewerPort = DEFAULTS.port;
497
+ const baseUrl = `http://localhost:${viewerPort}`;
498
+ const captureEngine = new CaptureEngine(pool, baseUrl);
499
+ let passed = 0;
500
+ let failed = 0;
501
+ let missing = 0;
502
+ const captureOptions = {
503
+ theme,
504
+ viewport,
505
+ delay: config.screenshots?.delay ?? DEFAULTS.captureDelayMs
506
+ };
507
+ try {
508
+ await pool.warmup();
509
+ for (const { component, variant } of variantsToDiff) {
510
+ const baseline = await storage.loadBaseline(component, variant, theme);
511
+ if (!baseline) {
512
+ console.log(
513
+ ` ${pc2.yellow("?")} ${component}/${variant} ${pc2.dim("(no baseline)")}`
514
+ );
515
+ missing++;
516
+ continue;
517
+ }
518
+ try {
519
+ const current = await captureEngine.captureVariant(
520
+ component,
521
+ variant,
522
+ captureOptions
523
+ );
524
+ if (diffEngine.areIdentical(current, baseline)) {
525
+ console.log(` ${pc2.green("\u2713")} ${component}/${variant} ${pc2.dim("0.0%")}`);
526
+ results.push({
527
+ component,
528
+ variant,
529
+ theme,
530
+ result: {
531
+ matches: true,
532
+ diffPercentage: 0,
533
+ diffPixelCount: 0,
534
+ totalPixels: current.viewport.width * current.viewport.height,
535
+ changedRegions: [],
536
+ diffTimeMs: 0
537
+ }
538
+ });
539
+ passed++;
540
+ continue;
541
+ }
542
+ const diffResult = diffEngine.compare(current, baseline, { threshold });
543
+ if (diffResult.matches) {
544
+ console.log(
545
+ ` ${pc2.green("\u2713")} ${component}/${variant} ${pc2.dim(`${diffResult.diffPercentage}%`)}`
546
+ );
547
+ passed++;
548
+ } else {
549
+ let diffImagePath;
550
+ if (diffResult.diffImage) {
551
+ diffImagePath = await storage.saveDiff(
552
+ component,
553
+ variant,
554
+ theme,
555
+ diffResult.diffImage
556
+ );
557
+ }
558
+ console.log(
559
+ ` ${pc2.red("\u2717")} ${component}/${variant} ${pc2.yellow(`${diffResult.diffPercentage}%`)}` + (diffImagePath ? pc2.dim(` \u2192 ${diffImagePath}`) : "")
560
+ );
561
+ failed++;
562
+ results.push({
563
+ component,
564
+ variant,
565
+ theme,
566
+ result: diffResult,
567
+ diffImagePath
568
+ });
569
+ continue;
570
+ }
571
+ results.push({
572
+ component,
573
+ variant,
574
+ theme,
575
+ result: diffResult
576
+ });
577
+ } catch (error) {
578
+ const errorMsg = error instanceof Error ? error.message : String(error);
579
+ console.log(` ${pc2.red("!")} ${component}/${variant} ${pc2.dim(errorMsg)}`);
580
+ failed++;
581
+ }
582
+ }
583
+ } finally {
584
+ await pool.shutdown();
585
+ }
586
+ const totalTimeMs = Date.now() - startTime;
587
+ const total = passed + failed + missing;
588
+ console.log();
589
+ if (failed === 0 && missing === 0) {
590
+ console.log(pc2.green(`\u2713 All ${passed} variant(s) match baselines`));
591
+ } else if (failed > 0) {
592
+ console.log(pc2.red(`\u2717 ${failed} variant(s) differ from baselines`));
593
+ }
594
+ if (missing > 0) {
595
+ console.log(pc2.yellow(` ${missing} variant(s) have no baseline (run \`${BRAND.cliCommand} screenshot\`)`));
596
+ }
597
+ console.log(pc2.dim(` Completed in ${formatMs(totalTimeMs)}
598
+ `));
599
+ const success = failed === 0;
600
+ return {
601
+ success,
602
+ total,
603
+ passed,
604
+ failed,
605
+ missing,
606
+ results,
607
+ totalTimeMs
608
+ };
609
+ }
610
+
611
+ // src/analyze.ts
612
+ import { existsSync } from "fs";
613
+ import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
614
+ import { join as join2, dirname } from "path";
615
+ import pc3 from "picocolors";
616
+ async function runAnalyzeCommand(config, configDir, options = {}) {
617
+ const format = options.format ?? "html";
618
+ const minScore = options.minScore ?? 0;
619
+ console.log(pc3.cyan(`
620
+ ${BRAND.name} Analyzer
621
+ `));
622
+ const segmentsPath = join2(configDir, config.outFile ?? "segments.json");
623
+ if (!existsSync(segmentsPath)) {
624
+ console.log(pc3.red(`\u2717 No segments.json found. Run \`${BRAND.cliCommand} build\` first.
625
+ `));
626
+ return {
627
+ success: false,
628
+ analytics: createEmptyAnalytics()
629
+ };
630
+ }
631
+ console.log(pc3.dim("Analyzing design system...\n"));
632
+ const content = await readFile2(segmentsPath, "utf-8");
633
+ const data = JSON.parse(content);
634
+ const analytics = analyzeDesignSystem(data);
635
+ printConsoleSummary(analytics);
636
+ let outputPath;
637
+ if (format === "html" || format === "json") {
638
+ outputPath = options.output ?? getDefaultOutputPath(format, configDir);
639
+ await mkdir2(dirname(outputPath), { recursive: true });
640
+ if (format === "html") {
641
+ const html = generateHtmlReport(analytics);
642
+ await writeFile2(outputPath, html);
643
+ console.log(pc3.green(`\u2713 Report generated: ${outputPath}
644
+ `));
645
+ } else {
646
+ await writeFile2(outputPath, JSON.stringify(analytics, null, 2));
647
+ console.log(pc3.green(`\u2713 JSON report generated: ${outputPath}
648
+ `));
649
+ }
650
+ if (options.open && format === "html") {
651
+ await openInBrowser(outputPath);
652
+ }
653
+ }
654
+ const passedCi = analytics.summary.overallScore >= minScore;
655
+ if (options.ci) {
656
+ if (passedCi) {
657
+ console.log(
658
+ pc3.green(`\u2713 Score ${analytics.summary.overallScore} meets minimum threshold ${minScore}
659
+ `)
660
+ );
661
+ } else {
662
+ console.log(
663
+ pc3.red(
664
+ `\u2717 Score ${analytics.summary.overallScore} below minimum threshold ${minScore}
665
+ `
666
+ )
667
+ );
668
+ }
669
+ }
670
+ return {
671
+ success: !options.ci || passedCi,
672
+ analytics,
673
+ outputPath
674
+ };
675
+ }
676
+ function printConsoleSummary(analytics) {
677
+ const { summary, coverage, recommendations } = analytics;
678
+ const grade = getGrade(summary.overallScore);
679
+ console.log(
680
+ pc3.bold(
681
+ `Overall Score: ${colorizeScore(summary.overallScore)} (${grade})
682
+ `
683
+ )
684
+ );
685
+ console.log(pc3.dim("Summary"));
686
+ console.log(` Components: ${pc3.white(summary.totalComponents.toString())}`);
687
+ console.log(` Variants: ${pc3.white(summary.totalVariants.toString())}`);
688
+ console.log(` Props: ${pc3.white(summary.totalProps.toString())}`);
689
+ console.log(` Categories: ${pc3.white(summary.categories.join(", "))}`);
690
+ console.log();
691
+ console.log(pc3.dim("Coverage"));
692
+ console.log(` Description: ${formatCoverage(coverage.fields.description)}`);
693
+ console.log(` Usage when: ${formatCoverage(coverage.fields.usageWhen)}`);
694
+ console.log(` Usage whenNot:${formatCoverage(coverage.fields.usageWhenNot)}`);
695
+ console.log(` Guidelines: ${formatCoverage(coverage.fields.guidelines)}`);
696
+ console.log(` Relations: ${formatCoverage(coverage.fields.relations)}`);
697
+ console.log();
698
+ if (recommendations.length > 0) {
699
+ console.log(pc3.dim("Top Recommendations"));
700
+ for (const rec of recommendations.slice(0, 3)) {
701
+ const priority = rec.priority === "high" ? pc3.red(`[${rec.priority}]`) : rec.priority === "medium" ? pc3.yellow(`[${rec.priority}]`) : pc3.dim(`[${rec.priority}]`);
702
+ console.log(` ${priority} ${rec.title}`);
703
+ }
704
+ console.log();
705
+ }
706
+ }
707
+ function formatCoverage(field) {
708
+ const pct = colorizeScore(field.percentage);
709
+ return `${pct} (${field.covered}/${field.total})`;
710
+ }
711
+ function colorizeScore(score) {
712
+ if (score >= 80) return pc3.green(`${score}%`);
713
+ if (score >= 60) return pc3.yellow(`${score}%`);
714
+ return pc3.red(`${score}%`);
715
+ }
716
+ function getDefaultOutputPath(format, configDir) {
717
+ const filename = format === "html" ? "segments-report.html" : "segments-report.json";
718
+ return join2(configDir, filename);
719
+ }
720
+ async function openInBrowser(path) {
721
+ const { platform } = await import("os");
722
+ const { exec } = await import("child_process");
723
+ const os = platform();
724
+ const cmd = os === "darwin" ? `open "${path}"` : os === "win32" ? `start "" "${path}"` : `xdg-open "${path}"`;
725
+ exec(cmd);
726
+ }
727
+ function createEmptyAnalytics() {
728
+ return {
729
+ analyzedAt: /* @__PURE__ */ new Date(),
730
+ summary: {
731
+ totalComponents: 0,
732
+ totalVariants: 0,
733
+ totalProps: 0,
734
+ categories: [],
735
+ overallScore: 0
736
+ },
737
+ inventory: {
738
+ byCategory: {},
739
+ byStatus: {},
740
+ byVariantCount: [],
741
+ byPropCount: []
742
+ },
743
+ coverage: {
744
+ overall: 0,
745
+ fields: {
746
+ description: { covered: 0, total: 0, percentage: 0 },
747
+ usageWhen: { covered: 0, total: 0, percentage: 0 },
748
+ usageWhenNot: { covered: 0, total: 0, percentage: 0 },
749
+ guidelines: { covered: 0, total: 0, percentage: 0 },
750
+ accessibility: { covered: 0, total: 0, percentage: 0 },
751
+ relations: { covered: 0, total: 0, percentage: 0 },
752
+ propDescriptions: { covered: 0, total: 0, percentage: 0 },
753
+ propConstraints: { covered: 0, total: 0, percentage: 0 }
754
+ },
755
+ incomplete: []
756
+ },
757
+ quality: {
758
+ missingWhenNot: [],
759
+ isolated: [],
760
+ deprecated: [],
761
+ fewVariants: [],
762
+ undocumentedProps: [],
763
+ unconstrainedProps: []
764
+ },
765
+ distribution: {
766
+ variantsPerComponent: [],
767
+ propsPerComponent: [],
768
+ componentsPerCategory: [],
769
+ statusDistribution: [],
770
+ tagFrequency: []
771
+ },
772
+ recommendations: []
773
+ };
774
+ }
775
+
776
+ export {
777
+ validateSchema,
778
+ validateCoverage,
779
+ validateAll,
780
+ buildSegments,
781
+ buildFragmentsDir,
782
+ runScreenshotCommand,
783
+ runDiffCommand,
784
+ runAnalyzeCommand
785
+ };
786
+ //# sourceMappingURL=chunk-4FDQSGKX.js.map