@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,214 @@
1
+ /**
2
+ * TypeScript types for Fragment JSON files.
3
+ * These types correspond to the JSON schemas in ./schema/
4
+ */
5
+
6
+ /**
7
+ * Figma design links and mappings
8
+ */
9
+ export interface FragmentFigma {
10
+ /** Figma file URL */
11
+ file?: string;
12
+ /** Default Figma node ID for this component */
13
+ nodeId?: string;
14
+ /** Mapping of variant names to Figma node IDs */
15
+ variants?: Record<string, string>;
16
+ }
17
+
18
+ /**
19
+ * Anti-pattern with optional alternative
20
+ */
21
+ export interface FragmentDoNotItem {
22
+ /** What not to do */
23
+ text: string;
24
+ /** Component name to use instead */
25
+ instead?: string;
26
+ }
27
+
28
+ /**
29
+ * Usage pattern with code example
30
+ */
31
+ export interface FragmentPattern {
32
+ /** Pattern name */
33
+ name: string;
34
+ /** Code example */
35
+ code: string;
36
+ /** When to use this pattern */
37
+ description?: string;
38
+ }
39
+
40
+ /**
41
+ * Usage guidelines for AI agents and developers
42
+ */
43
+ export interface FragmentUsage {
44
+ /** Scenarios when this component should be used */
45
+ when?: string[];
46
+ /** Anti-patterns and what to use instead */
47
+ doNot?: (string | FragmentDoNotItem)[];
48
+ /** Common usage patterns with code examples */
49
+ patterns?: FragmentPattern[];
50
+ }
51
+
52
+ /**
53
+ * Accessibility requirements and guidelines
54
+ */
55
+ export interface FragmentAccessibility {
56
+ /** ARIA role this component implements */
57
+ role?: string;
58
+ /** Accessibility requirements */
59
+ requirements?: string[];
60
+ /** Keyboard interaction patterns (key -> description) */
61
+ keyboard?: Record<string, string>;
62
+ }
63
+
64
+ /**
65
+ * Relationships to other components
66
+ */
67
+ export interface FragmentRelated {
68
+ /** Similar components that might be alternatives */
69
+ similar?: string[];
70
+ /** Components commonly used together with this one */
71
+ composedWith?: string[];
72
+ /** Parent components or patterns where this is commonly used */
73
+ usedIn?: string[];
74
+ }
75
+
76
+ /**
77
+ * Administrative metadata
78
+ */
79
+ export interface FragmentMeta {
80
+ /** Team or person responsible for this component */
81
+ owner?: string;
82
+ /** Component lifecycle status */
83
+ status?: "draft" | "experimental" | "beta" | "stable" | "deprecated";
84
+ /** Version when this component was introduced */
85
+ since?: string;
86
+ /** Version when this component was deprecated */
87
+ deprecatedSince?: string;
88
+ /** Why this component was deprecated and what to use instead */
89
+ deprecatedReason?: string;
90
+ /** Tags for categorization and search */
91
+ tags?: string[];
92
+ }
93
+
94
+ /**
95
+ * Fragment JSON file structure (.fragment.json)
96
+ * Contains enrichment metadata for a component
97
+ */
98
+ export interface Fragment {
99
+ /** JSON Schema reference */
100
+ $schema?: string;
101
+ /** Component name (must match the component export name) */
102
+ name: string;
103
+ /** Brief description of the component's purpose */
104
+ description?: string;
105
+ /** Figma design links and mappings */
106
+ figma?: FragmentFigma;
107
+ /** Usage guidelines for AI agents and developers */
108
+ usage?: FragmentUsage;
109
+ /** Accessibility requirements and guidelines */
110
+ accessibility?: FragmentAccessibility;
111
+ /** Relationships to other components */
112
+ related?: FragmentRelated;
113
+ /** Administrative metadata */
114
+ meta?: FragmentMeta;
115
+ }
116
+
117
+ /**
118
+ * Prop entry in the registry
119
+ */
120
+ export interface RegistryPropEntry {
121
+ /** TypeScript type (e.g., 'string', 'boolean', enum values) */
122
+ type?: string;
123
+ /** Simplified type category */
124
+ typeKind?: "string" | "number" | "boolean" | "enum" | "object" | "array" | "function" | "node" | "element" | "union" | "unknown";
125
+ /** For enum types, the allowed values */
126
+ options?: string[];
127
+ /** Default value if specified */
128
+ default?: unknown;
129
+ /** Whether this prop is required */
130
+ required?: boolean;
131
+ /** Prop description from JSDoc or TypeScript */
132
+ description?: string;
133
+ }
134
+
135
+ /**
136
+ * Component entry in the registry (simplified - focuses on paths and enrichment)
137
+ */
138
+ export interface RegistryComponentEntry {
139
+ /** Relative path to the component source file */
140
+ path: string;
141
+ /** Relative path to the .fragment.json file (if exists) */
142
+ fragmentPath?: string;
143
+ /** Relative path to the .stories.tsx file (if exists) */
144
+ storyPath?: string;
145
+ /** Component category (inferred from directory or fragment) */
146
+ category?: string;
147
+ /** Component lifecycle status (from fragment) */
148
+ status?: "draft" | "experimental" | "beta" | "stable" | "deprecated";
149
+ /** Component description (from fragment or JSDoc) */
150
+ description?: string;
151
+ /** Has human-authored enrichment (fragment file exists with content beyond skeleton) */
152
+ hasEnrichment?: boolean;
153
+ /** Extracted prop definitions - only included if config.registry.includeProps is true */
154
+ props?: Record<string, RegistryPropEntry>;
155
+ /** Named exports from the component file */
156
+ exports?: string[];
157
+ /** Component dependencies (other components used) */
158
+ dependencies?: string[];
159
+ /** Merged fragment enrichment data - only included if config.registry.embedFragments is true */
160
+ fragment?: Fragment;
161
+ }
162
+
163
+ /**
164
+ * Minimal component index (.fragments/index.json)
165
+ * Ultra-light name → path mapping for quick lookups
166
+ */
167
+ export interface FragmentIndex {
168
+ /** Schema version */
169
+ version: string;
170
+ /** When this index was generated */
171
+ generatedAt: string;
172
+ /** Simple name → path mapping */
173
+ components: Record<string, string>;
174
+ /** Categories for grouping */
175
+ categories?: Record<string, string[]>;
176
+ }
177
+
178
+ /**
179
+ * Registry file structure (.fragments/registry.json)
180
+ * Component index with resolved paths and optional metadata
181
+ */
182
+ export interface FragmentRegistry {
183
+ /** JSON Schema reference */
184
+ $schema?: string;
185
+ /** Schema version */
186
+ version: string;
187
+ /** When this registry was generated */
188
+ generatedAt: string;
189
+ /** Component count for quick reference */
190
+ componentCount: number;
191
+ /** Component index keyed by component name */
192
+ components: Record<string, RegistryComponentEntry>;
193
+ /** Components grouped by category */
194
+ categories?: Record<string, string[]>;
195
+ }
196
+
197
+ /**
198
+ * Context file options for generation
199
+ */
200
+ export interface FragmentContextOptions {
201
+ /** Output format */
202
+ format?: "markdown" | "json";
203
+ /** Compact mode - minimal output for token efficiency */
204
+ compact?: boolean;
205
+ /** What to include in the output */
206
+ include?: {
207
+ /** Include prop details (default: true) */
208
+ props?: boolean;
209
+ /** Include code examples (default: false) */
210
+ code?: boolean;
211
+ /** Include related components (default: true) */
212
+ relations?: boolean;
213
+ };
214
+ }
@@ -0,0 +1,389 @@
1
+ /**
2
+ * Context.md Generator
3
+ * Generates a consolidated AI-ready context file from the fragment registry
4
+ *
5
+ * Philosophy: Focus on human-authored semantic knowledge that AI can't get from source.
6
+ * - Usage guidelines: When to use, when not to use
7
+ * - Relationships: Similar components, alternatives
8
+ * - Accessibility: Requirements and keyboard patterns
9
+ * - DO NOT: Duplicate prop types (AI can read TypeScript directly)
10
+ */
11
+
12
+ import type {
13
+ FragmentRegistry,
14
+ RegistryComponentEntry,
15
+ RegistryPropEntry,
16
+ FragmentDoNotItem,
17
+ FragmentContextOptions,
18
+ } from "../fragment-types.js";
19
+
20
+ /**
21
+ * Result of context generation
22
+ */
23
+ export interface ContextGeneratorResult {
24
+ /** Generated markdown content */
25
+ content: string;
26
+ /** Estimated token count */
27
+ tokenEstimate: number;
28
+ /** Component count */
29
+ componentCount: number;
30
+ }
31
+
32
+ /**
33
+ * Generate context.md from a fragment registry
34
+ */
35
+ export function generateContextMd(
36
+ registry: FragmentRegistry,
37
+ options: FragmentContextOptions = {}
38
+ ): ContextGeneratorResult {
39
+ const {
40
+ format = "markdown",
41
+ compact = false,
42
+ include = { props: true, relations: true, code: false },
43
+ } = options;
44
+
45
+ if (format === "json") {
46
+ return generateJsonContext(registry, include, compact);
47
+ }
48
+
49
+ return generateMarkdownContext(registry, include, compact);
50
+ }
51
+
52
+ /**
53
+ * Generate markdown context
54
+ */
55
+ function generateMarkdownContext(
56
+ registry: FragmentRegistry,
57
+ include: { props?: boolean; relations?: boolean; code?: boolean },
58
+ compact: boolean
59
+ ): ContextGeneratorResult {
60
+ const lines: string[] = [];
61
+ const componentNames = Object.keys(registry.components).sort();
62
+ const componentCount = componentNames.length;
63
+
64
+ // Header
65
+ lines.push("# Component Library Context");
66
+ lines.push(`Generated: ${new Date().toISOString().split("T")[0]} | Components: ${componentCount}`);
67
+ lines.push("");
68
+
69
+ // Quick reference table
70
+ lines.push("## Quick Reference");
71
+ lines.push("");
72
+ lines.push("| Component | Path | Category | Status |");
73
+ lines.push("|-----------|------|----------|--------|");
74
+
75
+ for (const name of componentNames) {
76
+ const entry = registry.components[name];
77
+ const status = entry.status || "stable";
78
+ const category = entry.category || "-";
79
+ lines.push(`| ${name} | ${entry.path} | ${category} | ${status} |`);
80
+ }
81
+
82
+ lines.push("");
83
+
84
+ // In compact mode, stop here
85
+ if (compact) {
86
+ const content = lines.join("\n");
87
+ return {
88
+ content,
89
+ tokenEstimate: estimateTokens(content),
90
+ componentCount,
91
+ };
92
+ }
93
+
94
+ // Full documentation for each component
95
+ lines.push("---");
96
+ lines.push("");
97
+
98
+ for (const name of componentNames) {
99
+ const entry = registry.components[name];
100
+ const fragment = entry.fragment;
101
+
102
+ lines.push(`## ${name}`);
103
+ lines.push(`**Path:** \`${entry.path}\``);
104
+
105
+ if (entry.category) {
106
+ lines.push(`**Category:** ${entry.category} | **Status:** ${entry.status || "stable"}`);
107
+ }
108
+ lines.push("");
109
+
110
+ // Description
111
+ if (entry.description || fragment?.description) {
112
+ lines.push("### Description");
113
+ lines.push(entry.description || fragment?.description || "");
114
+ lines.push("");
115
+ }
116
+
117
+ // Usage guidelines
118
+ if (fragment?.usage) {
119
+ if (fragment.usage.when && fragment.usage.when.length > 0) {
120
+ lines.push("### When to Use");
121
+ for (const when of fragment.usage.when) {
122
+ lines.push(`- ${when}`);
123
+ }
124
+ lines.push("");
125
+ }
126
+
127
+ if (fragment.usage.doNot && fragment.usage.doNot.length > 0) {
128
+ lines.push("### Do Not");
129
+ for (const doNotItem of fragment.usage.doNot) {
130
+ if (typeof doNotItem === "string") {
131
+ lines.push(`- ${doNotItem}`);
132
+ } else {
133
+ const item = doNotItem as FragmentDoNotItem;
134
+ if (item.instead) {
135
+ // Resolve component path
136
+ const alternativePath = resolveComponentPath(item.instead, registry);
137
+ if (alternativePath) {
138
+ lines.push(`- ${item.text} → use **${item.instead}** (\`${alternativePath}\`)`);
139
+ } else {
140
+ lines.push(`- ${item.text} → use **${item.instead}**`);
141
+ }
142
+ } else {
143
+ lines.push(`- ${item.text}`);
144
+ }
145
+ }
146
+ }
147
+ lines.push("");
148
+ }
149
+
150
+ if (include.code && fragment.usage.patterns && fragment.usage.patterns.length > 0) {
151
+ lines.push("### Patterns");
152
+ for (const pattern of fragment.usage.patterns) {
153
+ lines.push(`**${pattern.name}**`);
154
+ if (pattern.description) {
155
+ lines.push(pattern.description);
156
+ }
157
+ lines.push("```tsx");
158
+ lines.push(pattern.code);
159
+ lines.push("```");
160
+ lines.push("");
161
+ }
162
+ }
163
+ }
164
+
165
+ // Props
166
+ if (include.props && entry.props && Object.keys(entry.props).length > 0) {
167
+ lines.push("### Props");
168
+ lines.push("| Prop | Type | Default | Description |");
169
+ lines.push("|------|------|---------|-------------|");
170
+
171
+ for (const [propName, prop] of Object.entries(entry.props)) {
172
+ const type = formatPropType(prop);
173
+ const defaultVal = prop.default !== undefined ? `\`${JSON.stringify(prop.default)}\`` : "-";
174
+ const desc = prop.description || "-";
175
+ const required = prop.required ? " (required)" : "";
176
+ lines.push(`| ${propName}${required} | ${type} | ${defaultVal} | ${desc} |`);
177
+ }
178
+ lines.push("");
179
+ }
180
+
181
+ // Accessibility
182
+ if (fragment?.accessibility) {
183
+ lines.push("### Accessibility");
184
+ if (fragment.accessibility.role) {
185
+ lines.push(`**Role:** ${fragment.accessibility.role}`);
186
+ }
187
+ if (fragment.accessibility.requirements && fragment.accessibility.requirements.length > 0) {
188
+ for (const req of fragment.accessibility.requirements) {
189
+ lines.push(`- ${req}`);
190
+ }
191
+ }
192
+ if (fragment.accessibility.keyboard) {
193
+ lines.push("");
194
+ lines.push("**Keyboard:**");
195
+ for (const [key, action] of Object.entries(fragment.accessibility.keyboard)) {
196
+ lines.push(`- \`${key}\`: ${action}`);
197
+ }
198
+ }
199
+ lines.push("");
200
+ }
201
+
202
+ // Related components
203
+ if (include.relations && fragment?.related) {
204
+ lines.push("### Related");
205
+ if (fragment.related.similar && fragment.related.similar.length > 0) {
206
+ const resolved = fragment.related.similar.map((comp) => {
207
+ const path = resolveComponentPath(comp, registry);
208
+ return path ? `${comp} (\`${path}\`)` : comp;
209
+ });
210
+ lines.push(`- **Similar:** ${resolved.join(", ")}`);
211
+ }
212
+ if (fragment.related.composedWith && fragment.related.composedWith.length > 0) {
213
+ const resolved = fragment.related.composedWith.map((comp) => {
214
+ const path = resolveComponentPath(comp, registry);
215
+ return path ? `${comp} (\`${path}\`)` : comp;
216
+ });
217
+ lines.push(`- **Composed with:** ${resolved.join(", ")}`);
218
+ }
219
+ if (fragment.related.usedIn && fragment.related.usedIn.length > 0) {
220
+ const resolved = fragment.related.usedIn.map((comp) => {
221
+ const path = resolveComponentPath(comp, registry);
222
+ return path ? `${comp} (\`${path}\`)` : comp;
223
+ });
224
+ lines.push(`- **Used in:** ${resolved.join(", ")}`);
225
+ }
226
+ lines.push("");
227
+ }
228
+
229
+ lines.push("---");
230
+ lines.push("");
231
+ }
232
+
233
+ const content = lines.join("\n");
234
+ return {
235
+ content,
236
+ tokenEstimate: estimateTokens(content),
237
+ componentCount,
238
+ };
239
+ }
240
+
241
+ /**
242
+ * Generate JSON context
243
+ */
244
+ function generateJsonContext(
245
+ registry: FragmentRegistry,
246
+ include: { props?: boolean; relations?: boolean; code?: boolean },
247
+ compact: boolean
248
+ ): ContextGeneratorResult {
249
+ const componentNames = Object.keys(registry.components).sort();
250
+ const componentCount = componentNames.length;
251
+
252
+ interface JsonComponent {
253
+ path: string;
254
+ category?: string;
255
+ status?: string;
256
+ description?: string;
257
+ whenToUse?: string[];
258
+ doNot?: Array<{ text: string; instead?: string; insteadPath?: string }>;
259
+ props?: Record<string, {
260
+ type: string;
261
+ required?: boolean;
262
+ default?: unknown;
263
+ description?: string;
264
+ }>;
265
+ related?: {
266
+ similar?: Array<{ name: string; path?: string }>;
267
+ composedWith?: Array<{ name: string; path?: string }>;
268
+ usedIn?: Array<{ name: string; path?: string }>;
269
+ };
270
+ }
271
+
272
+ const components: Record<string, JsonComponent> = {};
273
+
274
+ for (const name of componentNames) {
275
+ const entry = registry.components[name];
276
+ const fragment = entry.fragment;
277
+
278
+ const component: JsonComponent = {
279
+ path: entry.path,
280
+ };
281
+
282
+ if (entry.category) component.category = entry.category;
283
+ if (entry.status) component.status = entry.status;
284
+ if (entry.description || fragment?.description) {
285
+ component.description = entry.description || fragment?.description;
286
+ }
287
+
288
+ if (!compact && fragment?.usage) {
289
+ if (fragment.usage.when && fragment.usage.when.length > 0) {
290
+ component.whenToUse = fragment.usage.when;
291
+ }
292
+
293
+ if (fragment.usage.doNot && fragment.usage.doNot.length > 0) {
294
+ component.doNot = fragment.usage.doNot.map((item) => {
295
+ if (typeof item === "string") {
296
+ return { text: item };
297
+ }
298
+ const doNotItem = item as FragmentDoNotItem;
299
+ const result: { text: string; instead?: string; insteadPath?: string } = {
300
+ text: doNotItem.text,
301
+ };
302
+ if (doNotItem.instead) {
303
+ result.instead = doNotItem.instead;
304
+ result.insteadPath = resolveComponentPath(doNotItem.instead, registry);
305
+ }
306
+ return result;
307
+ });
308
+ }
309
+ }
310
+
311
+ if (!compact && include.props && entry.props) {
312
+ component.props = {};
313
+ for (const [propName, prop] of Object.entries(entry.props)) {
314
+ component.props[propName] = {
315
+ type: formatPropType(prop),
316
+ };
317
+ if (prop.required) component.props[propName].required = true;
318
+ if (prop.default !== undefined) component.props[propName].default = prop.default;
319
+ if (prop.description) component.props[propName].description = prop.description;
320
+ }
321
+ }
322
+
323
+ if (!compact && include.relations && fragment?.related) {
324
+ component.related = {};
325
+ if (fragment.related.similar) {
326
+ component.related.similar = fragment.related.similar.map((name) => ({
327
+ name,
328
+ path: resolveComponentPath(name, registry),
329
+ }));
330
+ }
331
+ if (fragment.related.composedWith) {
332
+ component.related.composedWith = fragment.related.composedWith.map((name) => ({
333
+ name,
334
+ path: resolveComponentPath(name, registry),
335
+ }));
336
+ }
337
+ if (fragment.related.usedIn) {
338
+ component.related.usedIn = fragment.related.usedIn.map((name) => ({
339
+ name,
340
+ path: resolveComponentPath(name, registry),
341
+ }));
342
+ }
343
+ }
344
+
345
+ components[name] = component;
346
+ }
347
+
348
+ const output = {
349
+ version: "1.0",
350
+ generatedAt: registry.generatedAt,
351
+ componentCount,
352
+ categories: registry.categories,
353
+ components,
354
+ };
355
+
356
+ const content = JSON.stringify(output, null, 2);
357
+ return {
358
+ content,
359
+ tokenEstimate: estimateTokens(content),
360
+ componentCount,
361
+ };
362
+ }
363
+
364
+ /**
365
+ * Resolve component name to path
366
+ */
367
+ function resolveComponentPath(
368
+ componentName: string,
369
+ registry: FragmentRegistry
370
+ ): string | undefined {
371
+ return registry.components[componentName]?.path;
372
+ }
373
+
374
+ /**
375
+ * Format prop type for display
376
+ */
377
+ function formatPropType(prop: RegistryPropEntry): string {
378
+ if (prop.options && prop.options.length > 0) {
379
+ return prop.options.map((o) => `"${o}"`).join(" \\| ");
380
+ }
381
+ return prop.type || "unknown";
382
+ }
383
+
384
+ /**
385
+ * Estimate token count (rough: ~4 chars per token)
386
+ */
387
+ function estimateTokens(text: string): number {
388
+ return Math.ceil(text.length / 4);
389
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Generator utilities for Fragments
3
+ * These are Node.js-only APIs for generating registry and context files
4
+ */
5
+
6
+ export {
7
+ extractPropsFromFile,
8
+ extractPropsFromSource,
9
+ type ExtractedProps,
10
+ } from "./typescript-extractor.js";
11
+
12
+ export {
13
+ generateRegistry,
14
+ resolveComponentPath,
15
+ getComponentsByCategory,
16
+ type RegistryGeneratorOptions,
17
+ type RegistryGeneratorResult,
18
+ } from "./registry.js";
19
+
20
+ export {
21
+ generateContextMd,
22
+ type ContextGeneratorResult,
23
+ } from "./context.js";