@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,380 @@
1
+ import type { CompiledSegment, CompiledRecipe, PropDefinition } from "./types.js";
2
+
3
+ /**
4
+ * Placeholder patterns to filter out from usage text.
5
+ * These are generated by the migrate tool and provide no value.
6
+ */
7
+ const PLACEHOLDER_PATTERNS = [
8
+ /^\w+ component is needed$/i,
9
+ /^Alternative component is more appropriate$/i,
10
+ /^Use \w+ when you need/i,
11
+ ];
12
+
13
+ /**
14
+ * Filter out placeholder text from usage arrays
15
+ */
16
+ function filterPlaceholders(items: string[]): string[] {
17
+ return items.filter(item =>
18
+ !PLACEHOLDER_PATTERNS.some(pattern => pattern.test(item.trim()))
19
+ );
20
+ }
21
+
22
+ /**
23
+ * Options for context generation
24
+ */
25
+ export interface ContextOptions {
26
+ /** Output format */
27
+ format?: "markdown" | "json";
28
+
29
+ /** What to include in the output */
30
+ include?: {
31
+ /** Include prop details (default: true) */
32
+ props?: boolean;
33
+ /** Include variant list (default: true) */
34
+ variants?: boolean;
35
+ /** Include usage guidelines (default: true) */
36
+ usage?: boolean;
37
+ /** Include related components (default: false) */
38
+ relations?: boolean;
39
+ /** Include code examples (default: false) */
40
+ code?: boolean;
41
+ };
42
+
43
+ /** Compact mode - minimal output for token efficiency */
44
+ compact?: boolean;
45
+ }
46
+
47
+ /**
48
+ * Result of context generation
49
+ */
50
+ export interface ContextResult {
51
+ /** The generated context content */
52
+ content: string;
53
+ /** Estimated token count */
54
+ tokenEstimate: number;
55
+ }
56
+
57
+ /**
58
+ * Generate AI-ready context from compiled segments and optional recipes
59
+ */
60
+ export function generateContext(
61
+ segments: CompiledSegment[],
62
+ options: ContextOptions = {},
63
+ recipes?: CompiledRecipe[]
64
+ ): ContextResult {
65
+ const format = options.format ?? "markdown";
66
+ const compact = options.compact ?? false;
67
+
68
+ const include = {
69
+ props: options.include?.props ?? true,
70
+ variants: options.include?.variants ?? true,
71
+ usage: options.include?.usage ?? true,
72
+ relations: options.include?.relations ?? false,
73
+ code: options.include?.code ?? false,
74
+ };
75
+
76
+ // Sort segments by category, then name
77
+ const sorted = [...segments].sort((a, b) => {
78
+ const catCompare = a.meta.category.localeCompare(b.meta.category);
79
+ if (catCompare !== 0) return catCompare;
80
+ return a.meta.name.localeCompare(b.meta.name);
81
+ });
82
+
83
+ if (format === "json") {
84
+ return generateJsonContext(sorted, include, compact, recipes);
85
+ }
86
+
87
+ return generateMarkdownContext(sorted, include, compact, recipes);
88
+ }
89
+
90
+ /**
91
+ * Generate markdown context
92
+ */
93
+ function generateMarkdownContext(
94
+ segments: CompiledSegment[],
95
+ include: Required<NonNullable<ContextOptions["include"]>>,
96
+ compact: boolean,
97
+ recipes?: CompiledRecipe[]
98
+ ): ContextResult {
99
+ const lines: string[] = [];
100
+
101
+ lines.push("# Design System Reference");
102
+ lines.push("");
103
+
104
+ // Quick reference table
105
+ lines.push("## Quick Reference");
106
+ lines.push("");
107
+ lines.push("| Component | Category | Use For |");
108
+ lines.push("|-----------|----------|---------|");
109
+
110
+ for (const segment of segments) {
111
+ const filteredWhen = filterPlaceholders(segment.usage.when);
112
+ const useFor = filteredWhen.slice(0, 2).join(", ") || segment.meta.description;
113
+ lines.push(`| ${segment.meta.name} | ${segment.meta.category} | ${truncate(useFor, 50)} |`);
114
+ }
115
+
116
+ lines.push("");
117
+
118
+ // If compact mode, stop here
119
+ if (compact) {
120
+ const content = lines.join("\n");
121
+ return {
122
+ content,
123
+ tokenEstimate: estimateTokens(content),
124
+ };
125
+ }
126
+
127
+ // Full component documentation
128
+ lines.push("## Components");
129
+ lines.push("");
130
+
131
+ for (const segment of segments) {
132
+ lines.push(`### ${segment.meta.name}`);
133
+ lines.push("");
134
+
135
+ // Status line
136
+ const statusParts = [`**Category:** ${segment.meta.category}`];
137
+ if (segment.meta.status) {
138
+ statusParts.push(`**Status:** ${segment.meta.status}`);
139
+ }
140
+ lines.push(statusParts.join(" | "));
141
+ lines.push("");
142
+
143
+ if (segment.meta.description) {
144
+ lines.push(segment.meta.description);
145
+ lines.push("");
146
+ }
147
+
148
+ // Usage guidelines (filter out placeholder text)
149
+ const whenFiltered = filterPlaceholders(segment.usage.when);
150
+ const whenNotFiltered = filterPlaceholders(segment.usage.whenNot);
151
+
152
+ if (include.usage && (whenFiltered.length > 0 || whenNotFiltered.length > 0)) {
153
+ if (whenFiltered.length > 0) {
154
+ lines.push("**When to use:**");
155
+ for (const when of whenFiltered) {
156
+ lines.push(`- ${when}`);
157
+ }
158
+ lines.push("");
159
+ }
160
+
161
+ if (whenNotFiltered.length > 0) {
162
+ lines.push("**When NOT to use:**");
163
+ for (const whenNot of whenNotFiltered) {
164
+ lines.push(`- ${whenNot}`);
165
+ }
166
+ lines.push("");
167
+ }
168
+ }
169
+
170
+ // Props
171
+ if (include.props && Object.keys(segment.props).length > 0) {
172
+ lines.push("**Props:**");
173
+ for (const [name, prop] of Object.entries(segment.props)) {
174
+ lines.push(`- \`${name}\`: ${formatPropType(prop)}${prop.required ? " (required)" : ""}`);
175
+ }
176
+ lines.push("");
177
+ }
178
+
179
+ // Variants
180
+ if (include.variants && segment.variants.length > 0) {
181
+ const variantNames = segment.variants.map((v) => v.name).join(", ");
182
+ lines.push(`**Variants:** ${variantNames}`);
183
+ lines.push("");
184
+
185
+ // Code examples
186
+ if (include.code) {
187
+ for (const variant of segment.variants) {
188
+ if (variant.code) {
189
+ lines.push(`*${variant.name}:*`);
190
+ lines.push("```tsx");
191
+ lines.push(variant.code);
192
+ lines.push("```");
193
+ lines.push("");
194
+ }
195
+ }
196
+ }
197
+ }
198
+
199
+ // Relations
200
+ if (include.relations && segment.relations && segment.relations.length > 0) {
201
+ lines.push("**Related:**");
202
+ for (const relation of segment.relations) {
203
+ lines.push(`- ${relation.component} (${relation.relationship}): ${relation.note}`);
204
+ }
205
+ lines.push("");
206
+ }
207
+
208
+ lines.push("---");
209
+ lines.push("");
210
+ }
211
+
212
+ // Recipes section
213
+ if (recipes && recipes.length > 0) {
214
+ lines.push("## Recipes");
215
+ lines.push("");
216
+ lines.push("Composition patterns showing how components wire together.");
217
+ lines.push("");
218
+
219
+ for (const recipe of recipes) {
220
+ lines.push(`### ${recipe.name}`);
221
+ lines.push("");
222
+ lines.push(recipe.description);
223
+ lines.push("");
224
+ lines.push(`**Category:** ${recipe.category}`);
225
+ lines.push(`**Components:** ${recipe.components.join(", ")}`);
226
+ if (recipe.tags && recipe.tags.length > 0) {
227
+ lines.push(`**Tags:** ${recipe.tags.join(", ")}`);
228
+ }
229
+ lines.push("");
230
+ lines.push("```tsx");
231
+ lines.push(recipe.code);
232
+ lines.push("```");
233
+ lines.push("");
234
+ lines.push("---");
235
+ lines.push("");
236
+ }
237
+ }
238
+
239
+ const content = lines.join("\n");
240
+ return {
241
+ content,
242
+ tokenEstimate: estimateTokens(content),
243
+ };
244
+ }
245
+
246
+ /**
247
+ * Generate JSON context
248
+ */
249
+ function generateJsonContext(
250
+ segments: CompiledSegment[],
251
+ include: Required<NonNullable<ContextOptions["include"]>>,
252
+ compact: boolean,
253
+ recipes?: CompiledRecipe[]
254
+ ): ContextResult {
255
+ const categories = [...new Set(segments.map((s) => s.meta.category))].sort();
256
+
257
+ interface JsonComponent {
258
+ category: string;
259
+ description: string;
260
+ status?: string;
261
+ whenToUse?: string[];
262
+ whenNotToUse?: string[];
263
+ props?: Record<string, { type: string; required?: boolean; default?: unknown; description: string }>;
264
+ variants?: string[];
265
+ relations?: Array<{ component: string; relationship: string; note: string }>;
266
+ }
267
+
268
+ const components: Record<string, JsonComponent> = {};
269
+
270
+ for (const segment of segments) {
271
+ const component: JsonComponent = {
272
+ category: segment.meta.category,
273
+ description: segment.meta.description,
274
+ };
275
+
276
+ if (segment.meta.status) {
277
+ component.status = segment.meta.status;
278
+ }
279
+
280
+ if (!compact) {
281
+ if (include.usage) {
282
+ const whenFiltered = filterPlaceholders(segment.usage.when);
283
+ const whenNotFiltered = filterPlaceholders(segment.usage.whenNot);
284
+ if (whenFiltered.length > 0) {
285
+ component.whenToUse = whenFiltered;
286
+ }
287
+ if (whenNotFiltered.length > 0) {
288
+ component.whenNotToUse = whenNotFiltered;
289
+ }
290
+ }
291
+
292
+ if (include.props && Object.keys(segment.props).length > 0) {
293
+ component.props = {};
294
+ for (const [name, prop] of Object.entries(segment.props)) {
295
+ component.props[name] = {
296
+ type: formatPropType(prop),
297
+ description: prop.description,
298
+ };
299
+ if (prop.required) {
300
+ component.props[name].required = true;
301
+ }
302
+ if (prop.default !== undefined) {
303
+ component.props[name].default = prop.default;
304
+ }
305
+ }
306
+ }
307
+
308
+ if (include.variants && segment.variants.length > 0) {
309
+ component.variants = segment.variants.map((v) => v.name);
310
+ }
311
+
312
+ if (include.relations && segment.relations && segment.relations.length > 0) {
313
+ component.relations = segment.relations.map((r) => ({
314
+ component: r.component,
315
+ relationship: r.relationship,
316
+ note: r.note,
317
+ }));
318
+ }
319
+ }
320
+
321
+ components[segment.meta.name] = component;
322
+ }
323
+
324
+ // Build recipes map
325
+ const recipesMap = recipes && recipes.length > 0
326
+ ? Object.fromEntries(recipes.map(r => [r.name, {
327
+ description: r.description,
328
+ category: r.category,
329
+ components: r.components,
330
+ code: r.code,
331
+ tags: r.tags,
332
+ }]))
333
+ : undefined;
334
+
335
+ const output = {
336
+ version: "1.0",
337
+ generatedAt: new Date().toISOString(),
338
+ summary: {
339
+ totalComponents: segments.length,
340
+ categories,
341
+ ...(recipesMap && { totalRecipes: recipes!.length }),
342
+ },
343
+ components,
344
+ ...(recipesMap && { recipes: recipesMap }),
345
+ };
346
+
347
+ const content = JSON.stringify(output, null, 2);
348
+ return {
349
+ content,
350
+ tokenEstimate: estimateTokens(content),
351
+ };
352
+ }
353
+
354
+ /**
355
+ * Format a prop type for display
356
+ */
357
+ function formatPropType(prop: PropDefinition): string {
358
+ if (prop.type === "enum" && prop.values) {
359
+ return prop.values.map((v) => `"${v}"`).join(" | ");
360
+ }
361
+ if (prop.default !== undefined) {
362
+ return `${prop.type} (default: ${JSON.stringify(prop.default)})`;
363
+ }
364
+ return prop.type;
365
+ }
366
+
367
+ /**
368
+ * Truncate string to max length
369
+ */
370
+ function truncate(str: string, maxLength: number): string {
371
+ if (str.length <= maxLength) return str;
372
+ return str.slice(0, maxLength - 3) + "...";
373
+ }
374
+
375
+ /**
376
+ * Estimate token count (rough approximation: ~4 chars per token)
377
+ */
378
+ function estimateTokens(text: string): number {
379
+ return Math.ceil(text.length / 4);
380
+ }
@@ -0,0 +1,137 @@
1
+ import type { SegmentDefinition, CompiledSegment, SegmentComponent, RecipeDefinition, CompiledRecipe } from './types.js';
2
+ import { segmentDefinitionSchema, recipeDefinitionSchema } from './schema.js';
3
+
4
+ /**
5
+ * Define a segment for a component.
6
+ *
7
+ * This is the main API for creating segment documentation.
8
+ * It provides runtime validation and type safety.
9
+ *
10
+ * @example
11
+ * ```tsx
12
+ * import { defineSegment } from '@fragments/core';
13
+ * import { Button } from './Button';
14
+ *
15
+ * export default defineSegment({
16
+ * component: Button,
17
+ * meta: {
18
+ * name: 'Button',
19
+ * description: 'Primary action trigger',
20
+ * category: 'actions',
21
+ * },
22
+ * usage: {
23
+ * when: ['User needs to trigger an action'],
24
+ * whenNot: ['Navigation without side effects'],
25
+ * },
26
+ * props: {
27
+ * variant: {
28
+ * type: 'enum',
29
+ * values: ['primary', 'secondary'],
30
+ * default: 'primary',
31
+ * description: 'Visual style',
32
+ * },
33
+ * },
34
+ * variants: [
35
+ * {
36
+ * name: 'Default',
37
+ * description: 'Default button',
38
+ * render: () => <Button>Click me</Button>,
39
+ * },
40
+ * ],
41
+ * });
42
+ * ```
43
+ */
44
+ export function defineSegment<TProps>(
45
+ definition: SegmentDefinition<TProps>
46
+ ): SegmentDefinition<TProps> {
47
+ // Validate at runtime in development
48
+ if (process.env.NODE_ENV !== 'production') {
49
+ const result = segmentDefinitionSchema.safeParse(definition);
50
+ if (!result.success) {
51
+ const errors = result.error.errors
52
+ .map((e) => ` - ${e.path.join('.')}: ${e.message}`)
53
+ .join('\n');
54
+ throw new Error(
55
+ `Invalid segment definition for "${definition.meta?.name || 'unknown'}":\n${errors}`
56
+ );
57
+ }
58
+ }
59
+
60
+ return definition;
61
+ }
62
+
63
+ /**
64
+ * Alias for defineSegment - use this for new projects.
65
+ * @see defineSegment
66
+ */
67
+ export const defineFragment = defineSegment;
68
+
69
+ /**
70
+ * Compile a segment definition to JSON-serializable format.
71
+ * Used for generating fragments.json for AI consumption.
72
+ */
73
+ export function compileSegment(
74
+ definition: SegmentDefinition,
75
+ filePath: string
76
+ ): CompiledSegment {
77
+ return {
78
+ filePath,
79
+ meta: definition.meta,
80
+ usage: definition.usage,
81
+ props: definition.props,
82
+ relations: definition.relations,
83
+ variants: definition.variants.map((v) => ({
84
+ name: v.name,
85
+ description: v.description,
86
+ code: v.code,
87
+ figma: v.figma,
88
+ })),
89
+ contract: definition.contract,
90
+ _generated: definition._generated,
91
+ };
92
+ }
93
+
94
+ /**
95
+ * Define a composition recipe.
96
+ *
97
+ * Recipes are pure data describing how design system components
98
+ * wire together for common use cases.
99
+ */
100
+ export function defineRecipe(definition: RecipeDefinition): RecipeDefinition {
101
+ if (process.env.NODE_ENV !== 'production') {
102
+ const result = recipeDefinitionSchema.safeParse(definition);
103
+ if (!result.success) {
104
+ const errors = result.error.errors
105
+ .map((e) => ` - ${e.path.join('.')}: ${e.message}`)
106
+ .join('\n');
107
+ throw new Error(
108
+ `Invalid recipe definition for "${definition.name || 'unknown'}":\n${errors}`
109
+ );
110
+ }
111
+ }
112
+
113
+ return definition;
114
+ }
115
+
116
+ /**
117
+ * Compile a recipe definition to JSON-serializable format.
118
+ */
119
+ export function compileRecipe(
120
+ definition: RecipeDefinition,
121
+ filePath: string
122
+ ): CompiledRecipe {
123
+ return {
124
+ filePath,
125
+ name: definition.name,
126
+ description: definition.description,
127
+ category: definition.category,
128
+ components: definition.components,
129
+ code: definition.code,
130
+ tags: definition.tags,
131
+ };
132
+ }
133
+
134
+ /**
135
+ * Type helper for extracting props type from a component
136
+ */
137
+ export type InferProps<T> = T extends SegmentComponent<infer P> ? P : never;