@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,196 @@
1
+ /**
2
+ * Storybook Configuration Detection
3
+ *
4
+ * Auto-detects Storybook configuration files and extracts
5
+ * story patterns for migration.
6
+ */
7
+
8
+ import { existsSync } from "node:fs";
9
+ import { readFile } from "node:fs/promises";
10
+ import { join, dirname } from "node:path";
11
+ import fg from "fast-glob";
12
+ import type { StorybookConfig } from "./types.js";
13
+
14
+ /**
15
+ * Common Storybook config file names in order of preference
16
+ */
17
+ const CONFIG_FILES = [
18
+ ".storybook/main.ts",
19
+ ".storybook/main.mts",
20
+ ".storybook/main.js",
21
+ ".storybook/main.mjs",
22
+ ".storybook/main.cjs",
23
+ ];
24
+
25
+ /**
26
+ * Default story patterns if none found in config
27
+ */
28
+ const DEFAULT_PATTERNS = [
29
+ "**/*.stories.@(ts|tsx|js|jsx|mdx)",
30
+ "**/*.story.@(ts|tsx|js|jsx|mdx)",
31
+ ];
32
+
33
+ /**
34
+ * Detect Storybook configuration in a project directory.
35
+ */
36
+ export async function detectStorybookConfig(
37
+ projectRoot: string
38
+ ): Promise<StorybookConfig | null> {
39
+ // Try each config file
40
+ for (const configFile of CONFIG_FILES) {
41
+ const configPath = join(projectRoot, configFile);
42
+ if (existsSync(configPath)) {
43
+ return parseStorybookConfig(configPath);
44
+ }
45
+ }
46
+
47
+ return null;
48
+ }
49
+
50
+ /**
51
+ * Parse a Storybook configuration file.
52
+ */
53
+ async function parseStorybookConfig(
54
+ configPath: string
55
+ ): Promise<StorybookConfig> {
56
+ const errors: string[] = [];
57
+ let storyPatterns: string[] = [];
58
+ let framework: string | undefined;
59
+ let builder: string | undefined;
60
+
61
+ try {
62
+ const content = await readFile(configPath, "utf-8");
63
+
64
+ // Extract stories array/patterns
65
+ // Pattern: stories: ['../src/**/*.stories.@(ts|tsx)']
66
+ const storiesMatch = content.match(
67
+ /stories:\s*\[([^\]]+)\]/s
68
+ );
69
+
70
+ if (storiesMatch) {
71
+ // Parse the array content
72
+ const storiesContent = storiesMatch[1];
73
+ const patterns = storiesContent
74
+ .split(",")
75
+ .map((p) => p.trim())
76
+ .map((p) => {
77
+ // Remove quotes and template literals
78
+ const cleaned = p.replace(/^['"`]|['"`]$/g, "").trim();
79
+ return cleaned;
80
+ })
81
+ .filter((p) => p && !p.startsWith("//") && !p.startsWith("{"));
82
+
83
+ if (patterns.length > 0) {
84
+ storyPatterns = patterns;
85
+ }
86
+ }
87
+
88
+ // Extract framework
89
+ // Pattern: framework: '@storybook/react-vite' or framework: { name: '...' }
90
+ const frameworkMatch = content.match(
91
+ /framework:\s*['"`]([^'"`]+)['"`]/
92
+ );
93
+ if (frameworkMatch) {
94
+ framework = frameworkMatch[1];
95
+ } else {
96
+ const frameworkNameMatch = content.match(
97
+ /framework:\s*\{[^}]*name:\s*['"`]([^'"`]+)['"`]/
98
+ );
99
+ if (frameworkNameMatch) {
100
+ framework = frameworkNameMatch[1];
101
+ }
102
+ }
103
+
104
+ // Extract builder if specified
105
+ // Pattern: builder: 'vite' or core: { builder: 'vite' }
106
+ const builderMatch = content.match(
107
+ /builder:\s*['"`]([^'"`]+)['"`]/
108
+ );
109
+ if (builderMatch) {
110
+ builder = builderMatch[1];
111
+ }
112
+ } catch (error) {
113
+ errors.push(
114
+ `Failed to parse config: ${error instanceof Error ? error.message : String(error)}`
115
+ );
116
+ }
117
+
118
+ // Use defaults if no patterns found
119
+ if (storyPatterns.length === 0) {
120
+ storyPatterns = DEFAULT_PATTERNS;
121
+ errors.push("No story patterns found in config, using defaults");
122
+ }
123
+
124
+ return {
125
+ configPath,
126
+ storyPatterns,
127
+ framework,
128
+ builder,
129
+ valid: errors.length === 0 || storyPatterns.length > 0,
130
+ errors: errors.length > 0 ? errors : undefined,
131
+ };
132
+ }
133
+
134
+ /**
135
+ * Discover story files using detected or provided patterns.
136
+ */
137
+ export async function discoverStoryFiles(
138
+ projectRoot: string,
139
+ patterns?: string[]
140
+ ): Promise<string[]> {
141
+ // If no patterns provided, try to detect from config
142
+ if (!patterns || patterns.length === 0) {
143
+ const config = await detectStorybookConfig(projectRoot);
144
+ patterns = config?.storyPatterns ?? DEFAULT_PATTERNS;
145
+ }
146
+
147
+ // Resolve patterns relative to project root
148
+ const configDir = join(projectRoot, ".storybook");
149
+ const resolvedPatterns = patterns.map((p) => {
150
+ // If pattern starts with ../, resolve from .storybook dir
151
+ if (p.startsWith("../")) {
152
+ return join(configDir, p);
153
+ }
154
+ // Otherwise resolve from project root
155
+ return join(projectRoot, p);
156
+ });
157
+
158
+ // Find matching files
159
+ const files = await fg(resolvedPatterns, {
160
+ cwd: projectRoot,
161
+ absolute: true,
162
+ ignore: [
163
+ "**/node_modules/**",
164
+ "**/dist/**",
165
+ "**/build/**",
166
+ "**/.storybook/**",
167
+ ],
168
+ });
169
+
170
+ return files.sort();
171
+ }
172
+
173
+ /**
174
+ * Get a summary of detected Storybook setup.
175
+ */
176
+ export async function getStorybookSummary(
177
+ projectRoot: string
178
+ ): Promise<{
179
+ detected: boolean;
180
+ config?: StorybookConfig;
181
+ storyCount: number;
182
+ storyFiles: string[];
183
+ }> {
184
+ const config = await detectStorybookConfig(projectRoot);
185
+ const storyFiles = await discoverStoryFiles(
186
+ projectRoot,
187
+ config?.storyPatterns
188
+ );
189
+
190
+ return {
191
+ detected: config !== null,
192
+ config: config ?? undefined,
193
+ storyCount: storyFiles.length,
194
+ storyFiles,
195
+ };
196
+ }
@@ -0,0 +1,45 @@
1
+ /**
2
+ * @fragments/migrate
3
+ *
4
+ * Storybook to Segments migration tool.
5
+ *
6
+ * Provides automated conversion of Storybook CSF files to Segments format,
7
+ * preserving component documentation while adding AI-first capabilities.
8
+ */
9
+
10
+ // Migration engine
11
+ export { migrate } from "./migrate.js";
12
+
13
+ // Parser
14
+ export {
15
+ parseStoryFile,
16
+ parseStoryContent,
17
+ storyNameToTitle,
18
+ extractCategory,
19
+ __testing,
20
+ } from "./parser.js";
21
+
22
+ // Converter
23
+ export { convertToSegment } from "./converter.js";
24
+
25
+ // Report generator
26
+ export { generateMigrationReport } from "./report.js";
27
+
28
+ // Storybook detection
29
+ export {
30
+ detectStorybookConfig,
31
+ discoverStoryFiles,
32
+ getStorybookSummary,
33
+ } from "./detect.js";
34
+
35
+ // Types
36
+ export type {
37
+ ParsedMeta,
38
+ ParsedArgType,
39
+ ParsedStory,
40
+ ParsedStoryFile,
41
+ ConversionResult,
42
+ MigrationReport,
43
+ MigrateOptions,
44
+ StorybookConfig,
45
+ } from "./types.js";
@@ -0,0 +1,163 @@
1
+ /**
2
+ * Migration Engine
3
+ *
4
+ * Orchestrates the full migration pipeline:
5
+ * 1. Discover Storybook files
6
+ * 2. Parse CSF format
7
+ * 3. Convert to Segments
8
+ * 4. Write output files
9
+ * 5. Generate report
10
+ */
11
+
12
+ import { readdir, writeFile, mkdir, access, stat } from "node:fs/promises";
13
+ import { join, dirname, relative } from "node:path";
14
+ import fg from "fast-glob";
15
+ import type {
16
+ MigrateOptions,
17
+ MigrationReport,
18
+ ConversionResult,
19
+ } from "./types.js";
20
+ import { parseStoryFile } from "./parser.js";
21
+ import { convertToSegment } from "./converter.js";
22
+ import { generateMigrationReport } from "./report.js";
23
+
24
+ /**
25
+ * Run the migration process.
26
+ */
27
+ export async function migrate(options: MigrateOptions): Promise<MigrationReport> {
28
+ const startTime = Date.now();
29
+
30
+ // Discover story files
31
+ const storyFiles = await discoverStoryFiles(options.from);
32
+
33
+ const results: ConversionResult[] = [];
34
+ const categories = new Set<string>();
35
+ let totalTodos = 0;
36
+ let totalVariants = 0;
37
+
38
+ // Process each story file
39
+ for (const storyFile of storyFiles) {
40
+ try {
41
+ // Parse
42
+ const parsed = await parseStoryFile(storyFile);
43
+
44
+ // Convert
45
+ const result = convertToSegment(parsed);
46
+
47
+ // Determine output path
48
+ const outputDir = options.to ?? dirname(storyFile);
49
+ const outputFile = join(outputDir, relative(dirname(storyFile), result.outputFile));
50
+ result.outputFile = outputFile;
51
+
52
+ // Check if file exists
53
+ if (!options.overwrite) {
54
+ try {
55
+ await access(outputFile);
56
+ result.warnings.push(`Output file already exists: ${outputFile}`);
57
+ if (!options.dryRun) {
58
+ result.success = false;
59
+ results.push(result);
60
+ continue;
61
+ }
62
+ } catch {
63
+ // File doesn't exist, proceed
64
+ }
65
+ }
66
+
67
+ // Write file (unless dry run)
68
+ if (!options.dryRun) {
69
+ await mkdir(dirname(outputFile), { recursive: true });
70
+ await writeFile(outputFile, result.code);
71
+ }
72
+
73
+ // Track stats
74
+ categories.add(result.category);
75
+ totalTodos += result.todos.length;
76
+ totalVariants += result.variantCount;
77
+
78
+ results.push(result);
79
+ } catch (error) {
80
+ results.push({
81
+ sourceFile: storyFile,
82
+ outputFile: "",
83
+ code: "",
84
+ componentName: "",
85
+ category: "",
86
+ variantCount: 0,
87
+ propCount: 0,
88
+ confidence: 0,
89
+ todos: [],
90
+ warnings: [error instanceof Error ? error.message : String(error)],
91
+ success: false,
92
+ });
93
+ }
94
+ }
95
+
96
+ // Calculate stats
97
+ const successfulConversions = results.filter((r) => r.success).length;
98
+ const failedConversions = results.filter((r) => !r.success).length;
99
+
100
+ // Build report
101
+ const report: MigrationReport = {
102
+ timestamp: new Date(),
103
+ sourceDir: options.from,
104
+ outputDir: options.to ?? options.from,
105
+ totalStoryFiles: storyFiles.length,
106
+ successfulConversions,
107
+ failedConversions,
108
+ totalVariants,
109
+ totalTodos,
110
+ results,
111
+ categories: Array.from(categories).sort(),
112
+ };
113
+
114
+ // Generate HTML report if requested
115
+ if (options.report) {
116
+ const reportHtml = generateMigrationReport(report);
117
+ const reportPath = options.reportPath ?? join(options.from, "migration-report.html");
118
+ await mkdir(dirname(reportPath), { recursive: true });
119
+ await writeFile(reportPath, reportHtml);
120
+ }
121
+
122
+ return report;
123
+ }
124
+
125
+ /**
126
+ * Discover Storybook story files in a directory.
127
+ */
128
+ async function discoverStoryFiles(fromPath: string): Promise<string[]> {
129
+ // Support both file and directory paths
130
+ const stats = await stat(fromPath);
131
+
132
+ if (stats.isFile()) {
133
+ // Single file
134
+ if (isStoryFile(fromPath)) {
135
+ return [fromPath];
136
+ }
137
+ return [];
138
+ }
139
+
140
+ // Directory - find all story files
141
+ const patterns = [
142
+ "**/*.stories.tsx",
143
+ "**/*.stories.ts",
144
+ "**/*.stories.jsx",
145
+ "**/*.stories.js",
146
+ ];
147
+
148
+ const files = await fg(patterns, {
149
+ cwd: fromPath,
150
+ ignore: ["**/node_modules/**", "**/.storybook/**"],
151
+ absolute: false,
152
+ });
153
+
154
+ // Return absolute paths, sorted
155
+ return files.map((f) => join(fromPath, f)).sort();
156
+ }
157
+
158
+ /**
159
+ * Check if a file is a Storybook story file.
160
+ */
161
+ function isStoryFile(filePath: string): boolean {
162
+ return /\.stories\.(tsx?|jsx?)$/.test(filePath);
163
+ }