@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,255 @@
1
+ import type { CompiledSegment, RelationshipType } from "./types.js";
2
+
3
+ // --- Public types ---
4
+
5
+ export interface CompositionWarning {
6
+ type:
7
+ | "missing_parent"
8
+ | "missing_child"
9
+ | "missing_composition"
10
+ | "redundant_alternative"
11
+ | "deprecated"
12
+ | "experimental";
13
+ component: string;
14
+ message: string;
15
+ relatedComponent?: string;
16
+ }
17
+
18
+ export interface CompositionSuggestion {
19
+ component: string;
20
+ reason: string;
21
+ relationship: RelationshipType | "category_gap";
22
+ sourceComponent: string;
23
+ }
24
+
25
+ export interface CompositionGuideline {
26
+ component: string;
27
+ guideline: string;
28
+ }
29
+
30
+ export interface CompositionAnalysis {
31
+ /** The validated component names (filtered to those that exist) */
32
+ components: string[];
33
+
34
+ /** Components requested but not found in the registry */
35
+ unknown: string[];
36
+
37
+ /** Issues with the current selection */
38
+ warnings: CompositionWarning[];
39
+
40
+ /** Components to consider adding */
41
+ suggestions: CompositionSuggestion[];
42
+
43
+ /** Relevant usage guidelines for the selected components */
44
+ guidelines: CompositionGuideline[];
45
+ }
46
+
47
+ // --- Category affinities ---
48
+
49
+ const CATEGORY_AFFINITIES: Record<string, string[]> = {
50
+ forms: ["feedback"],
51
+ actions: ["feedback"],
52
+ };
53
+
54
+ // --- Main function ---
55
+
56
+ /**
57
+ * Analyzes a set of components as a composition group.
58
+ * Returns warnings about missing relations, usage conflicts,
59
+ * and suggestions for additional components.
60
+ *
61
+ * Browser-safe: no Node.js APIs used.
62
+ */
63
+ export function analyzeComposition(
64
+ segments: Record<string, CompiledSegment>,
65
+ componentNames: string[],
66
+ _context?: string
67
+ ): CompositionAnalysis {
68
+ const allNames = new Set(Object.keys(segments));
69
+
70
+ // 1. Validate names
71
+ const components: string[] = [];
72
+ const unknown: string[] = [];
73
+ for (const name of componentNames) {
74
+ if (allNames.has(name)) {
75
+ components.push(name);
76
+ } else {
77
+ unknown.push(name);
78
+ }
79
+ }
80
+
81
+ const selectedSet = new Set(components);
82
+ const warnings: CompositionWarning[] = [];
83
+ const suggestions: CompositionSuggestion[] = [];
84
+ const guidelines: CompositionGuideline[] = [];
85
+
86
+ // Track suggestions to avoid duplicates
87
+ const suggestedSet = new Set<string>();
88
+
89
+ for (const name of components) {
90
+ const segment = segments[name];
91
+
92
+ // 2. Relation checks
93
+ if (segment.relations) {
94
+ for (const rel of segment.relations) {
95
+ switch (rel.relationship) {
96
+ case "parent":
97
+ if (!selectedSet.has(rel.component)) {
98
+ warnings.push({
99
+ type: "missing_parent",
100
+ component: name,
101
+ message: `"${name}" expects to be wrapped by "${rel.component}"${rel.note ? `: ${rel.note}` : ""}`,
102
+ relatedComponent: rel.component,
103
+ });
104
+ }
105
+ break;
106
+
107
+ case "child":
108
+ if (!selectedSet.has(rel.component) && !suggestedSet.has(rel.component)) {
109
+ suggestions.push({
110
+ component: rel.component,
111
+ reason: `"${name}" typically contains "${rel.component}"${rel.note ? `: ${rel.note}` : ""}`,
112
+ relationship: "child",
113
+ sourceComponent: name,
114
+ });
115
+ suggestedSet.add(rel.component);
116
+ }
117
+ break;
118
+
119
+ case "composition":
120
+ if (!selectedSet.has(rel.component)) {
121
+ warnings.push({
122
+ type: "missing_composition",
123
+ component: name,
124
+ message: `"${name}" is typically used together with "${rel.component}"${rel.note ? `: ${rel.note}` : ""}`,
125
+ relatedComponent: rel.component,
126
+ });
127
+ }
128
+ break;
129
+
130
+ case "sibling":
131
+ if (!selectedSet.has(rel.component) && !suggestedSet.has(rel.component)) {
132
+ suggestions.push({
133
+ component: rel.component,
134
+ reason: `"${rel.component}" is a sibling of "${name}"${rel.note ? `: ${rel.note}` : ""}`,
135
+ relationship: "sibling",
136
+ sourceComponent: name,
137
+ });
138
+ suggestedSet.add(rel.component);
139
+ }
140
+ break;
141
+
142
+ case "alternative":
143
+ if (selectedSet.has(rel.component)) {
144
+ warnings.push({
145
+ type: "redundant_alternative",
146
+ component: name,
147
+ message: `"${name}" and "${rel.component}" are alternatives — using both may be redundant${rel.note ? `: ${rel.note}` : ""}`,
148
+ relatedComponent: rel.component,
149
+ });
150
+ }
151
+ break;
152
+ }
153
+ }
154
+ }
155
+
156
+ // 3. Usage conflict checks (whenNot)
157
+ if (segment.usage?.whenNot) {
158
+ for (const whenNotEntry of segment.usage.whenNot) {
159
+ const lower = whenNotEntry.toLowerCase();
160
+ for (const other of components) {
161
+ if (other !== name && lower.includes(other.toLowerCase())) {
162
+ guidelines.push({
163
+ component: name,
164
+ guideline: `Potential conflict with "${other}": ${whenNotEntry}`,
165
+ });
166
+ }
167
+ }
168
+ }
169
+ }
170
+
171
+ // 4. Status warnings
172
+ if (segment.meta.status === "deprecated") {
173
+ warnings.push({
174
+ type: "deprecated",
175
+ component: name,
176
+ message: segment.meta.description
177
+ ? `"${name}" is deprecated: ${segment.meta.description}`
178
+ : `"${name}" is deprecated`,
179
+ });
180
+ } else if (segment.meta.status === "experimental") {
181
+ warnings.push({
182
+ type: "experimental",
183
+ component: name,
184
+ message: `"${name}" is experimental and may change without notice`,
185
+ });
186
+ }
187
+ }
188
+
189
+ // 5. Category gap analysis
190
+ const selectedCategories = new Set(
191
+ components.map((name) => segments[name].meta.category)
192
+ );
193
+
194
+ for (const [category, affinities] of Object.entries(CATEGORY_AFFINITIES)) {
195
+ if (!selectedCategories.has(category)) continue;
196
+
197
+ for (const neededCategory of affinities) {
198
+ if (selectedCategories.has(neededCategory)) continue;
199
+
200
+ // Find the best component from the needed category
201
+ const candidate = findBestCategoryCandidate(
202
+ segments,
203
+ neededCategory,
204
+ selectedSet,
205
+ suggestedSet
206
+ );
207
+ if (candidate) {
208
+ suggestions.push({
209
+ component: candidate,
210
+ reason: `Compositions using "${category}" components often benefit from a "${neededCategory}" component`,
211
+ relationship: "category_gap",
212
+ sourceComponent: components.find(
213
+ (n) => segments[n].meta.category === category
214
+ )!,
215
+ });
216
+ suggestedSet.add(candidate);
217
+ }
218
+ }
219
+ }
220
+
221
+ return { components, unknown, warnings, suggestions, guidelines };
222
+ }
223
+
224
+ /**
225
+ * Find the best candidate component from a given category.
226
+ * Prefers stable components and avoids already-selected or already-suggested ones.
227
+ */
228
+ function findBestCategoryCandidate(
229
+ segments: Record<string, CompiledSegment>,
230
+ category: string,
231
+ selectedSet: Set<string>,
232
+ suggestedSet: Set<string>
233
+ ): string | null {
234
+ let best: string | null = null;
235
+ let bestScore = -1;
236
+
237
+ for (const [name, segment] of Object.entries(segments)) {
238
+ if (segment.meta.category !== category) continue;
239
+ if (selectedSet.has(name) || suggestedSet.has(name)) continue;
240
+
241
+ const status = segment.meta.status ?? "stable";
242
+ let score = 0;
243
+ if (status === "stable") score = 3;
244
+ else if (status === "beta") score = 2;
245
+ else if (status === "experimental") score = 1;
246
+ // deprecated gets 0
247
+
248
+ if (score > bestScore) {
249
+ bestScore = score;
250
+ best = name;
251
+ }
252
+ }
253
+
254
+ return best;
255
+ }
@@ -0,0 +1,84 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { resolve, dirname } from 'node:path';
3
+ import { createJiti } from 'jiti';
4
+ import { BRAND } from './constants.js';
5
+ import type { SegmentsConfig } from './types.js';
6
+ import { segmentsConfigSchema } from './schema.js';
7
+
8
+ const DEFAULT_CONFIG: SegmentsConfig = {
9
+ include: [
10
+ `src/**/*${BRAND.fileExtension}`, // *.segment.tsx files
11
+ 'src/**/*.stories.tsx', // Storybook stories (auto-converted)
12
+ ],
13
+ exclude: ['**/node_modules/**'],
14
+ components: ['src/**/index.tsx', 'src/**/*.tsx'],
15
+ framework: 'react',
16
+ };
17
+
18
+ /**
19
+ * Find the config file in the current directory or parent directories.
20
+ * Checks for both the current config file name and the legacy name.
21
+ */
22
+ export function findConfigFile(startDir: string = process.cwd()): string | null {
23
+ let currentDir = startDir;
24
+
25
+ while (currentDir !== dirname(currentDir)) {
26
+ // Check for current config file name first
27
+ const configPath = resolve(currentDir, BRAND.configFile);
28
+ if (existsSync(configPath)) {
29
+ return configPath;
30
+ }
31
+ // Also check for legacy config file name
32
+ const legacyConfigPath = resolve(currentDir, BRAND.legacyConfigFile);
33
+ if (existsSync(legacyConfigPath)) {
34
+ return legacyConfigPath;
35
+ }
36
+ currentDir = dirname(currentDir);
37
+ }
38
+
39
+ return null;
40
+ }
41
+
42
+ /**
43
+ * Load and validate the config file
44
+ */
45
+ export async function loadConfig(configPath?: string): Promise<{
46
+ config: SegmentsConfig;
47
+ configDir: string;
48
+ }> {
49
+ const resolvedPath = configPath ?? findConfigFile();
50
+
51
+ if (!resolvedPath) {
52
+ return {
53
+ config: DEFAULT_CONFIG,
54
+ configDir: process.cwd(),
55
+ };
56
+ }
57
+
58
+ try {
59
+ // Use jiti to load TypeScript config files
60
+ const jiti = createJiti(import.meta.url, {
61
+ interopDefault: true,
62
+ });
63
+ const rawConfig = await jiti.import(resolvedPath);
64
+
65
+ const result = segmentsConfigSchema.safeParse(rawConfig);
66
+
67
+ if (!result.success) {
68
+ const errors = result.error.errors
69
+ .map((e) => ` - ${e.path.join('.')}: ${e.message}`)
70
+ .join('\n');
71
+ throw new Error(`Invalid config in ${resolvedPath}:\n${errors}`);
72
+ }
73
+
74
+ return {
75
+ config: { ...DEFAULT_CONFIG, ...result.data },
76
+ configDir: dirname(resolvedPath),
77
+ };
78
+ } catch (error) {
79
+ if (error instanceof Error && error.message.includes('Invalid config')) {
80
+ throw error;
81
+ }
82
+ throw new Error(`Failed to load config from ${resolvedPath}: ${error}`);
83
+ }
84
+ }
@@ -0,0 +1,111 @@
1
+ /**
2
+ * Brand constants for easy rebranding if domain availability requires it.
3
+ * All naming throughout the codebase should reference these constants.
4
+ */
5
+ export const BRAND = {
6
+ /** Display name (e.g., "Fragments") */
7
+ name: "Fragments",
8
+
9
+ /** Lowercase name for file paths and CLI (e.g., "fragments") */
10
+ nameLower: "fragments",
11
+
12
+ /** File extension for fragment definition files (e.g., ".fragment.tsx") */
13
+ fileExtension: ".fragment.tsx",
14
+
15
+ /** Legacy file extension for segments (still supported for migration) */
16
+ legacyFileExtension: ".segment.tsx",
17
+
18
+ /** JSON file extension for compiled output */
19
+ jsonExtension: ".fragment.json",
20
+
21
+ /** Default output file name (e.g., "fragments.json") */
22
+ outFile: "fragments.json",
23
+
24
+ /** Config file name (e.g., "fragments.config.ts") */
25
+ configFile: "fragments.config.ts",
26
+
27
+ /** Legacy config file name (still supported for migration) */
28
+ legacyConfigFile: "segments.config.ts",
29
+
30
+ /** CLI command name (e.g., "fragments") */
31
+ cliCommand: "fragments",
32
+
33
+ /** Package scope (e.g., "@fragments") */
34
+ packageScope: "@fragments",
35
+
36
+ /** Directory for storing fragments, registry, and cache */
37
+ dataDir: ".fragments",
38
+
39
+ /** Components subdirectory within .fragments/ */
40
+ componentsDir: "components",
41
+
42
+ /** Registry file name */
43
+ registryFile: "registry.json",
44
+
45
+ /** Context file name (AI-ready markdown) */
46
+ contextFile: "context.md",
47
+
48
+ /** Screenshots subdirectory */
49
+ screenshotsDir: "screenshots",
50
+
51
+ /** Cache subdirectory (gitignored) */
52
+ cacheDir: "cache",
53
+
54
+ /** Diff output subdirectory (gitignored) */
55
+ diffDir: "diff",
56
+
57
+ /** Manifest filename */
58
+ manifestFile: "manifest.json",
59
+
60
+ /** Prefix for localStorage keys (e.g., "fragments-") */
61
+ storagePrefix: "fragments-",
62
+
63
+ /** Static viewer HTML file name */
64
+ viewerHtmlFile: "fragments-viewer.html",
65
+
66
+ /** MCP tool name prefix (e.g., "fragments_") */
67
+ mcpToolPrefix: "fragments_",
68
+
69
+ /** File extension for recipe definition files */
70
+ recipeFileExtension: ".recipe.ts",
71
+
72
+ /** Vite plugin namespace */
73
+ vitePluginNamespace: "fragments-core-shim",
74
+ } as const;
75
+
76
+ export type Brand = typeof BRAND;
77
+
78
+ /**
79
+ * Default configuration values for the service.
80
+ * These can be overridden in fragments.config.ts
81
+ */
82
+ export const DEFAULTS = {
83
+ /** Default viewport dimensions */
84
+ viewport: {
85
+ width: 1280,
86
+ height: 800,
87
+ },
88
+
89
+ /** Default diff threshold (percentage) */
90
+ diffThreshold: 5,
91
+
92
+ /** Browser pool size */
93
+ poolSize: 3,
94
+
95
+ /** Idle timeout before browser shutdown (ms) - 5 minutes */
96
+ idleTimeoutMs: 5 * 60 * 1000,
97
+
98
+ /** Delay after render before capture (ms) */
99
+ captureDelayMs: 100,
100
+
101
+ /** Font loading timeout (ms) */
102
+ fontTimeoutMs: 3000,
103
+
104
+ /** Default theme */
105
+ theme: "light" as const,
106
+
107
+ /** Dev server port */
108
+ port: 6006,
109
+ } as const;
110
+
111
+ export type Defaults = typeof DEFAULTS;