@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,349 @@
1
+ /**
2
+ * Patch Generator
3
+ *
4
+ * Generates unified diff patches to fix token compliance issues.
5
+ * Takes style comparison results and produces patches that replace
6
+ * hardcoded CSS values with design token references.
7
+ */
8
+
9
+ import type { EnhancedStyleDiffItem, TokenUsageSummary } from "../core/index.js";
10
+ import { TokenRegistryManager } from "./token-registry.js";
11
+
12
+ /**
13
+ * A patch represents a file modification in unified diff format
14
+ */
15
+ export interface Patch {
16
+ /** File path relative to project root */
17
+ file: string;
18
+
19
+ /** Unified diff content */
20
+ diff: string;
21
+
22
+ /** Line number where the change starts */
23
+ lineNumber?: number;
24
+
25
+ /** Type of style syntax being modified */
26
+ styleType: "inline" | "styled-components" | "emotion" | "css-module" | "css";
27
+ }
28
+
29
+ /**
30
+ * Result of patch generation for a component
31
+ */
32
+ export interface PatchGenerationResult {
33
+ /** Generated patches */
34
+ patches: Patch[];
35
+
36
+ /** Human-readable summary */
37
+ summary: string;
38
+
39
+ /** Number of hardcoded values that could be fixed */
40
+ fixableCount: number;
41
+
42
+ /** Number of hardcoded values that couldn't be fixed (no matching token) */
43
+ unfixableCount: number;
44
+ }
45
+
46
+ /**
47
+ * Options for patch generation
48
+ */
49
+ export interface PatchGenerationOptions {
50
+ /** Source file path (if known from segment metadata) */
51
+ sourceFile?: string;
52
+
53
+ /** Style type hint */
54
+ styleType?: Patch["styleType"];
55
+
56
+ /** Number of context lines to include in diff (default: 3) */
57
+ contextLines?: number;
58
+
59
+ /** Theme context for token matching */
60
+ theme?: string;
61
+ }
62
+
63
+ /**
64
+ * Generate token patches for a component's hardcoded styles
65
+ *
66
+ * @param componentName - Name of the component
67
+ * @param styleDiffs - Style comparison results with rendered vs figma values
68
+ * @param registry - Token registry for token lookup
69
+ * @param options - Generation options
70
+ */
71
+ export function generateTokenPatches(
72
+ componentName: string,
73
+ styleDiffs: Array<{
74
+ property: string;
75
+ figma: string;
76
+ rendered: string;
77
+ match: boolean;
78
+ }>,
79
+ registry: TokenRegistryManager,
80
+ options: PatchGenerationOptions = {}
81
+ ): PatchGenerationResult {
82
+ const { theme = "default", contextLines = 3 } = options;
83
+
84
+ // Get token usage summary with hardcoded properties
85
+ const summary = registry.calculateUsageSummary(styleDiffs, theme);
86
+
87
+ if (summary.hardcodedProperties.length === 0) {
88
+ return {
89
+ patches: [],
90
+ summary: `No hardcoded values found in ${componentName}. Token compliance is at ${summary.compliancePercent}%.`,
91
+ fixableCount: 0,
92
+ unfixableCount: 0,
93
+ };
94
+ }
95
+
96
+ const patches: Patch[] = [];
97
+ let fixableCount = 0;
98
+ let unfixableCount = 0;
99
+
100
+ // Group fixes by file (if we have file info)
101
+ const fixesByFile = new Map<string, EnhancedStyleDiffItem[]>();
102
+
103
+ for (const item of summary.hardcodedProperties) {
104
+ if (!item.suggestedFix) {
105
+ unfixableCount++;
106
+ continue;
107
+ }
108
+
109
+ fixableCount++;
110
+
111
+ // For now, group under the source file or a placeholder
112
+ const file = options.sourceFile || `${componentName}.tsx`;
113
+ const existing = fixesByFile.get(file) || [];
114
+ existing.push(item);
115
+ fixesByFile.set(file, existing);
116
+ }
117
+
118
+ // Generate patches for each file
119
+ for (const [file, fixes] of fixesByFile) {
120
+ const patch = generateUnifiedDiff(file, fixes, options);
121
+ if (patch) {
122
+ patches.push(patch);
123
+ }
124
+ }
125
+
126
+ // Generate summary
127
+ const summaryLines: string[] = [];
128
+ summaryLines.push(`Token Patch Summary for ${componentName}:`);
129
+ summaryLines.push(`- Compliance: ${summary.compliancePercent}%`);
130
+ summaryLines.push(`- Fixable issues: ${fixableCount}`);
131
+ if (unfixableCount > 0) {
132
+ summaryLines.push(`- Unfixable issues: ${unfixableCount} (no matching token found)`);
133
+ }
134
+ summaryLines.push(`- Patches generated: ${patches.length}`);
135
+
136
+ return {
137
+ patches,
138
+ summary: summaryLines.join("\n"),
139
+ fixableCount,
140
+ unfixableCount,
141
+ };
142
+ }
143
+
144
+ /**
145
+ * Generate a unified diff for a set of fixes in a single file
146
+ */
147
+ function generateUnifiedDiff(
148
+ file: string,
149
+ fixes: EnhancedStyleDiffItem[],
150
+ options: PatchGenerationOptions
151
+ ): Patch | null {
152
+ if (fixes.length === 0) return null;
153
+
154
+ const { styleType = detectStyleType(file) } = options;
155
+ const diffLines: string[] = [];
156
+
157
+ // Diff header
158
+ diffLines.push(`--- a/${file}`);
159
+ diffLines.push(`+++ b/${file}`);
160
+
161
+ // Generate hunks for each fix
162
+ // In a real implementation, we'd read the actual file and generate proper diffs
163
+ // For now, we generate synthetic diffs showing the transformation
164
+ for (const fix of fixes) {
165
+ if (!fix.suggestedFix) continue;
166
+
167
+ const cssProperty = toCssProperty(fix.property);
168
+ const hunk = generateHunk(cssProperty, fix.rendered, fix.suggestedFix, styleType);
169
+ diffLines.push(...hunk);
170
+ }
171
+
172
+ return {
173
+ file,
174
+ diff: diffLines.join("\n"),
175
+ styleType,
176
+ };
177
+ }
178
+
179
+ /**
180
+ * Generate a diff hunk for a single property fix
181
+ */
182
+ function generateHunk(
183
+ cssProperty: string,
184
+ currentValue: string,
185
+ suggestedFix: NonNullable<EnhancedStyleDiffItem["suggestedFix"]>,
186
+ styleType: Patch["styleType"]
187
+ ): string[] {
188
+ const lines: string[] = [];
189
+
190
+ // The hunk format depends on the style type
191
+ switch (styleType) {
192
+ case "inline":
193
+ // React inline style: { backgroundColor: '#0051c2' }
194
+ const camelProp = toCamelCase(cssProperty);
195
+ lines.push(`@@ -1,1 +1,1 @@ inline style`);
196
+ lines.push(`- ${camelProp}: '${currentValue}',`);
197
+ lines.push(`+ ${camelProp}: 'var(${suggestedFix.tokenName})',`);
198
+ break;
199
+
200
+ case "styled-components":
201
+ case "emotion":
202
+ // CSS-in-JS: background-color: #0051c2;
203
+ lines.push(`@@ -1,1 +1,1 @@ ${styleType}`);
204
+ lines.push(`- ${cssProperty}: ${currentValue};`);
205
+ lines.push(`+ ${cssProperty}: var(${suggestedFix.tokenName});`);
206
+ break;
207
+
208
+ case "css-module":
209
+ case "css":
210
+ default:
211
+ // Plain CSS: background-color: #0051c2;
212
+ lines.push(`@@ -1,1 +1,1 @@ css`);
213
+ lines.push(`- ${cssProperty}: ${currentValue};`);
214
+ lines.push(`+ ${cssProperty}: var(${suggestedFix.tokenName});`);
215
+ break;
216
+ }
217
+
218
+ return lines;
219
+ }
220
+
221
+ /**
222
+ * Detect the style type from file extension and name
223
+ */
224
+ function detectStyleType(file: string): Patch["styleType"] {
225
+ if (file.endsWith(".css")) return "css";
226
+ if (file.endsWith(".module.css") || file.endsWith(".module.scss")) return "css-module";
227
+ if (file.includes("styled") || file.includes("emotion")) return "styled-components";
228
+ // Default to inline for TSX/JSX files
229
+ if (file.endsWith(".tsx") || file.endsWith(".jsx")) return "inline";
230
+ return "css";
231
+ }
232
+
233
+ /**
234
+ * Convert camelCase to kebab-case
235
+ */
236
+ function toCssProperty(prop: string): string {
237
+ return prop.replace(/([A-Z])/g, "-$1").toLowerCase();
238
+ }
239
+
240
+ /**
241
+ * Convert kebab-case to camelCase
242
+ */
243
+ function toCamelCase(prop: string): string {
244
+ return prop.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
245
+ }
246
+
247
+ /**
248
+ * Generate patches for CSS-in-JS patterns
249
+ *
250
+ * Handles common patterns:
251
+ * - styled-components: styled.div`...`
252
+ * - emotion: css`...` or css={{...}}
253
+ * - inline: style={{...}}
254
+ */
255
+ export function generateCSSInJSPatches(
256
+ sourceCode: string,
257
+ fixes: Array<{
258
+ property: string;
259
+ currentValue: string;
260
+ tokenName: string;
261
+ }>,
262
+ options: { fileName?: string } = {}
263
+ ): Patch[] {
264
+ const patches: Patch[] = [];
265
+ const fileName = options.fileName || "component.tsx";
266
+
267
+ for (const fix of fixes) {
268
+ // Find the property in source code
269
+ const cssProperty = toCssProperty(fix.property);
270
+ const camelProperty = toCamelCase(cssProperty);
271
+
272
+ // Try to find inline style pattern: propertyName: 'value' or propertyName: "value"
273
+ const inlinePattern = new RegExp(
274
+ `(${camelProperty}\\s*:\\s*)(['"]?)${escapeRegExp(fix.currentValue)}\\2`,
275
+ "g"
276
+ );
277
+
278
+ // Try to find CSS pattern: property-name: value;
279
+ const cssPattern = new RegExp(
280
+ `(${cssProperty}\\s*:\\s*)${escapeRegExp(fix.currentValue)}(\\s*;)`,
281
+ "g"
282
+ );
283
+
284
+ let match;
285
+ const lines = sourceCode.split("\n");
286
+
287
+ // Check inline style pattern
288
+ for (let i = 0; i < lines.length; i++) {
289
+ const line = lines[i];
290
+
291
+ if (inlinePattern.test(line)) {
292
+ const newLine = line.replace(
293
+ inlinePattern,
294
+ `$1'var(${fix.tokenName})'`
295
+ );
296
+
297
+ patches.push({
298
+ file: fileName,
299
+ diff: generateLineDiff(fileName, i + 1, line, newLine),
300
+ lineNumber: i + 1,
301
+ styleType: "inline",
302
+ });
303
+ } else if (cssPattern.test(line)) {
304
+ const newLine = line.replace(
305
+ cssPattern,
306
+ `$1var(${fix.tokenName})$2`
307
+ );
308
+
309
+ patches.push({
310
+ file: fileName,
311
+ diff: generateLineDiff(fileName, i + 1, line, newLine),
312
+ lineNumber: i + 1,
313
+ styleType: "css",
314
+ });
315
+ }
316
+
317
+ // Reset regex state
318
+ inlinePattern.lastIndex = 0;
319
+ cssPattern.lastIndex = 0;
320
+ }
321
+ }
322
+
323
+ return patches;
324
+ }
325
+
326
+ /**
327
+ * Generate a unified diff for a single line change
328
+ */
329
+ function generateLineDiff(
330
+ file: string,
331
+ lineNumber: number,
332
+ oldLine: string,
333
+ newLine: string
334
+ ): string {
335
+ return [
336
+ `--- a/${file}`,
337
+ `+++ b/${file}`,
338
+ `@@ -${lineNumber},1 +${lineNumber},1 @@`,
339
+ `-${oldLine}`,
340
+ `+${newLine}`,
341
+ ].join("\n");
342
+ }
343
+
344
+ /**
345
+ * Escape special regex characters
346
+ */
347
+ function escapeRegExp(string: string): string {
348
+ return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
349
+ }