@fragments-sdk/cli 0.10.1 → 0.12.1

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 (223) hide show
  1. package/dist/ai-client-I6MDWNYA.js +21 -0
  2. package/dist/bin.js +292 -367
  3. package/dist/bin.js.map +1 -1
  4. package/dist/{chunk-PW7QTQA6.js → chunk-4OC7FTJB.js} +2 -2
  5. package/dist/{chunk-HRFUSSZI.js → chunk-AM4MRTMN.js} +2 -2
  6. package/dist/{chunk-5G3VZH43.js → chunk-GVDSFQ4E.js} +281 -351
  7. package/dist/chunk-GVDSFQ4E.js.map +1 -0
  8. package/dist/chunk-JJ2VRTBU.js +626 -0
  9. package/dist/chunk-JJ2VRTBU.js.map +1 -0
  10. package/dist/{chunk-D5PYOXEI.js → chunk-LVWFOLUZ.js} +148 -13
  11. package/dist/{chunk-D5PYOXEI.js.map → chunk-LVWFOLUZ.js.map} +1 -1
  12. package/dist/{chunk-WXSR2II7.js → chunk-OQKMEFOS.js} +58 -6
  13. package/dist/chunk-OQKMEFOS.js.map +1 -0
  14. package/dist/chunk-SXTKFDCR.js +104 -0
  15. package/dist/chunk-SXTKFDCR.js.map +1 -0
  16. package/dist/chunk-T5OMVL7E.js +443 -0
  17. package/dist/chunk-T5OMVL7E.js.map +1 -0
  18. package/dist/{chunk-ZM4ZQZWZ.js → chunk-TPWGL2XS.js} +39 -37
  19. package/dist/chunk-TPWGL2XS.js.map +1 -0
  20. package/dist/{chunk-OQO55NKV.js → chunk-WFS63PCW.js} +85 -11
  21. package/dist/chunk-WFS63PCW.js.map +1 -0
  22. package/dist/core/index.js +9 -1
  23. package/dist/{discovery-NEOY4MPN.js → discovery-ZJQSXF56.js} +3 -3
  24. package/dist/{generate-FBHSXR3D.js → generate-RJFS2JWA.js} +4 -4
  25. package/dist/index.js +7 -6
  26. package/dist/index.js.map +1 -1
  27. package/dist/init-ZSX3NRCZ.js +636 -0
  28. package/dist/init-ZSX3NRCZ.js.map +1 -0
  29. package/dist/mcp-bin.js +2 -2
  30. package/dist/{scan-CJF2DOQW.js → scan-3PMCJ4RB.js} +6 -6
  31. package/dist/scan-generate-SYU4PYZD.js +1115 -0
  32. package/dist/scan-generate-SYU4PYZD.js.map +1 -0
  33. package/dist/{service-TQYWY65E.js → service-VMGNJZ42.js} +3 -3
  34. package/dist/snapshot-XOISO2IS.js +139 -0
  35. package/dist/snapshot-XOISO2IS.js.map +1 -0
  36. package/dist/{static-viewer-NUBFPKWH.js → static-viewer-5GXH2MGE.js} +3 -3
  37. package/dist/static-viewer-5GXH2MGE.js.map +1 -0
  38. package/dist/{test-Z5LVO724.js → test-SI4NSHQX.js} +4 -4
  39. package/dist/{tokens-CE46OTMD.js → tokens-T6SIVUT5.js} +5 -5
  40. package/dist/{viewer-DNMNC5VS.js → viewer-7ZEAFBVN.js} +80 -58
  41. package/dist/viewer-7ZEAFBVN.js.map +1 -0
  42. package/package.json +6 -14
  43. package/src/ai-client.ts +156 -0
  44. package/src/bin.ts +74 -2
  45. package/src/build.ts +95 -33
  46. package/src/commands/__tests__/drift-sync.test.ts +252 -0
  47. package/src/commands/__tests__/scan-generate.test.ts +497 -45
  48. package/src/commands/enhance.ts +11 -35
  49. package/src/commands/init.ts +296 -193
  50. package/src/commands/scan-generate.ts +740 -139
  51. package/src/commands/scan.ts +37 -32
  52. package/src/commands/setup.ts +143 -52
  53. package/src/commands/snapshot.ts +197 -0
  54. package/src/commands/sync.ts +357 -0
  55. package/src/commands/validate.ts +43 -1
  56. package/src/core/component-extractor.test.ts +282 -0
  57. package/src/core/component-extractor.ts +1030 -0
  58. package/src/core/discovery.ts +93 -7
  59. package/src/service/enhance/props-extractor.ts +235 -13
  60. package/src/validators.ts +236 -0
  61. package/src/viewer/__tests__/viewer-integration.test.ts +85 -74
  62. package/src/viewer/server.ts +37 -22
  63. package/src/viewer/vite-plugin.ts +25 -9
  64. package/dist/chunk-5G3VZH43.js.map +0 -1
  65. package/dist/chunk-OQO55NKV.js.map +0 -1
  66. package/dist/chunk-WXSR2II7.js.map +0 -1
  67. package/dist/chunk-ZM4ZQZWZ.js.map +0 -1
  68. package/dist/init-NDQXUWDU.js +0 -796
  69. package/dist/init-NDQXUWDU.js.map +0 -1
  70. package/dist/scan-generate-SJAN5MVI.js +0 -691
  71. package/dist/scan-generate-SJAN5MVI.js.map +0 -1
  72. package/dist/viewer-DNMNC5VS.js.map +0 -1
  73. package/src/ai.ts +0 -266
  74. package/src/commands/init-framework.ts +0 -414
  75. package/src/mcp/bin.ts +0 -36
  76. package/src/migrate/bin.ts +0 -114
  77. package/src/theme/index.ts +0 -77
  78. package/src/viewer/__tests__/a11y-fixes.test.ts +0 -358
  79. package/src/viewer/__tests__/jsx-parser.test.ts +0 -502
  80. package/src/viewer/__tests__/render-utils.test.ts +0 -232
  81. package/src/viewer/__tests__/style-utils.test.ts +0 -404
  82. package/src/viewer/assets/fragments-logo.ts +0 -4
  83. package/src/viewer/assets/fragments_logo.png +0 -0
  84. package/src/viewer/bin.ts +0 -86
  85. package/src/viewer/cli/health.ts +0 -256
  86. package/src/viewer/cli/index.ts +0 -33
  87. package/src/viewer/cli/scan.ts +0 -124
  88. package/src/viewer/cli/utils.ts +0 -174
  89. package/src/viewer/components/AccessibilityPanel.tsx +0 -1457
  90. package/src/viewer/components/ActionCapture.tsx +0 -172
  91. package/src/viewer/components/ActionsPanel.tsx +0 -332
  92. package/src/viewer/components/AllVariantsPreview.tsx +0 -78
  93. package/src/viewer/components/App.tsx +0 -582
  94. package/src/viewer/components/BottomPanel.tsx +0 -288
  95. package/src/viewer/components/CodePanel.naming.test.tsx +0 -59
  96. package/src/viewer/components/CodePanel.tsx +0 -118
  97. package/src/viewer/components/CommandPalette.tsx +0 -392
  98. package/src/viewer/components/ComponentDocView.tsx +0 -164
  99. package/src/viewer/components/ComponentGraph.tsx +0 -380
  100. package/src/viewer/components/ComponentHeader.tsx +0 -88
  101. package/src/viewer/components/ContractPanel.tsx +0 -241
  102. package/src/viewer/components/EmptyVariantMessage.tsx +0 -54
  103. package/src/viewer/components/ErrorBoundary.tsx +0 -97
  104. package/src/viewer/components/FigmaEmbed.tsx +0 -238
  105. package/src/viewer/components/FragmentEditor.tsx +0 -525
  106. package/src/viewer/components/FragmentRenderer.tsx +0 -61
  107. package/src/viewer/components/HeaderSearch.tsx +0 -24
  108. package/src/viewer/components/HealthDashboard.tsx +0 -441
  109. package/src/viewer/components/HmrStatusIndicator.tsx +0 -61
  110. package/src/viewer/components/Icons.tsx +0 -479
  111. package/src/viewer/components/InteractionsPanel.tsx +0 -757
  112. package/src/viewer/components/IsolatedPreviewFrame.tsx +0 -346
  113. package/src/viewer/components/IsolatedRender.tsx +0 -113
  114. package/src/viewer/components/KeyboardShortcutsHelp.tsx +0 -53
  115. package/src/viewer/components/LandingPage.tsx +0 -421
  116. package/src/viewer/components/Layout.tsx +0 -27
  117. package/src/viewer/components/LeftSidebar.tsx +0 -472
  118. package/src/viewer/components/LoadErrorMessage.tsx +0 -102
  119. package/src/viewer/components/MultiViewportPreview.tsx +0 -522
  120. package/src/viewer/components/NoVariantsMessage.tsx +0 -59
  121. package/src/viewer/components/PanelShell.tsx +0 -161
  122. package/src/viewer/components/PerformancePanel.tsx +0 -304
  123. package/src/viewer/components/PreviewArea.tsx +0 -472
  124. package/src/viewer/components/PreviewAside.tsx +0 -168
  125. package/src/viewer/components/PreviewFrameHost.tsx +0 -303
  126. package/src/viewer/components/PreviewPane.tsx +0 -149
  127. package/src/viewer/components/PreviewToolbar.tsx +0 -80
  128. package/src/viewer/components/PropsEditor.tsx +0 -506
  129. package/src/viewer/components/PropsTable.tsx +0 -111
  130. package/src/viewer/components/RelationsSection.tsx +0 -88
  131. package/src/viewer/components/ResizablePanel.tsx +0 -271
  132. package/src/viewer/components/RightSidebar.tsx +0 -102
  133. package/src/viewer/components/RuntimeToolsRegistrar.tsx +0 -17
  134. package/src/viewer/components/ScreenshotButton.tsx +0 -90
  135. package/src/viewer/components/Sidebar.tsx +0 -169
  136. package/src/viewer/components/SkeletonLoader.tsx +0 -161
  137. package/src/viewer/components/ThemeProvider.tsx +0 -42
  138. package/src/viewer/components/Toast.tsx +0 -3
  139. package/src/viewer/components/TokenStylePanel.tsx +0 -699
  140. package/src/viewer/components/TopToolbar.tsx +0 -159
  141. package/src/viewer/components/UsageSection.tsx +0 -95
  142. package/src/viewer/components/VariantMatrix.tsx +0 -388
  143. package/src/viewer/components/VariantRenderer.tsx +0 -131
  144. package/src/viewer/components/VariantTabs.tsx +0 -40
  145. package/src/viewer/components/ViewerHeader.tsx +0 -69
  146. package/src/viewer/components/ViewerStateSync.tsx +0 -52
  147. package/src/viewer/components/ViewportSelector.tsx +0 -172
  148. package/src/viewer/components/WebMCPDevTools.tsx +0 -503
  149. package/src/viewer/components/WebMCPIntegration.tsx +0 -47
  150. package/src/viewer/components/WebMCPStatusIndicator.tsx +0 -60
  151. package/src/viewer/components/_future/CreatePage.tsx +0 -836
  152. package/src/viewer/components/viewer-utils.ts +0 -16
  153. package/src/viewer/composition-renderer.ts +0 -381
  154. package/src/viewer/constants/index.ts +0 -1
  155. package/src/viewer/constants/ui.ts +0 -166
  156. package/src/viewer/entry.tsx +0 -335
  157. package/src/viewer/hooks/index.ts +0 -2
  158. package/src/viewer/hooks/useA11yCache.ts +0 -383
  159. package/src/viewer/hooks/useA11yService.ts +0 -364
  160. package/src/viewer/hooks/useActions.ts +0 -138
  161. package/src/viewer/hooks/useAppState.ts +0 -147
  162. package/src/viewer/hooks/useCompiledFragments.ts +0 -42
  163. package/src/viewer/hooks/useFigmaIntegration.ts +0 -132
  164. package/src/viewer/hooks/useHmrStatus.ts +0 -109
  165. package/src/viewer/hooks/useKeyboardShortcuts.ts +0 -270
  166. package/src/viewer/hooks/usePreviewBridge.ts +0 -347
  167. package/src/viewer/hooks/useScrollSpy.ts +0 -78
  168. package/src/viewer/hooks/useUrlState.ts +0 -318
  169. package/src/viewer/hooks/useViewSettings.ts +0 -111
  170. package/src/viewer/index.html +0 -28
  171. package/src/viewer/intelligence/healthReport.ts +0 -505
  172. package/src/viewer/intelligence/styleDrift.ts +0 -340
  173. package/src/viewer/intelligence/usageScanner.ts +0 -309
  174. package/src/viewer/jsx-parser.ts +0 -486
  175. package/src/viewer/preview-frame-entry.tsx +0 -25
  176. package/src/viewer/preview-frame.html +0 -125
  177. package/src/viewer/public/favicon.ico +0 -0
  178. package/src/viewer/render-template.html +0 -68
  179. package/src/viewer/styles/globals.css +0 -278
  180. package/src/viewer/types/a11y.ts +0 -197
  181. package/src/viewer/utils/a11y-fixes.ts +0 -509
  182. package/src/viewer/utils/actionExport.ts +0 -372
  183. package/src/viewer/utils/colorSchemes.ts +0 -201
  184. package/src/viewer/utils/detectRelationships.ts +0 -256
  185. package/src/viewer/vendor/shared/src/ComponentDocContent.module.scss +0 -10
  186. package/src/viewer/vendor/shared/src/ComponentDocContent.module.scss.d.ts +0 -2
  187. package/src/viewer/vendor/shared/src/ComponentDocContent.tsx +0 -274
  188. package/src/viewer/vendor/shared/src/DocsHeaderBar.tsx +0 -129
  189. package/src/viewer/vendor/shared/src/DocsPageAsideHost.tsx +0 -89
  190. package/src/viewer/vendor/shared/src/DocsPageShell.tsx +0 -124
  191. package/src/viewer/vendor/shared/src/DocsSearchCommand.tsx +0 -99
  192. package/src/viewer/vendor/shared/src/DocsSidebarNav.tsx +0 -66
  193. package/src/viewer/vendor/shared/src/PropsTable.module.scss +0 -68
  194. package/src/viewer/vendor/shared/src/PropsTable.module.scss.d.ts +0 -2
  195. package/src/viewer/vendor/shared/src/PropsTable.tsx +0 -76
  196. package/src/viewer/vendor/shared/src/VariantPreviewCard.module.scss +0 -114
  197. package/src/viewer/vendor/shared/src/VariantPreviewCard.module.scss.d.ts +0 -2
  198. package/src/viewer/vendor/shared/src/VariantPreviewCard.tsx +0 -137
  199. package/src/viewer/vendor/shared/src/docs-data/index.ts +0 -32
  200. package/src/viewer/vendor/shared/src/docs-data/mcp-configs.ts +0 -72
  201. package/src/viewer/vendor/shared/src/docs-data/palettes.ts +0 -75
  202. package/src/viewer/vendor/shared/src/docs-data/setup-examples.ts +0 -55
  203. package/src/viewer/vendor/shared/src/docs-layout.scss +0 -28
  204. package/src/viewer/vendor/shared/src/docs-layout.scss.d.ts +0 -2
  205. package/src/viewer/vendor/shared/src/index.ts +0 -34
  206. package/src/viewer/vendor/shared/src/types.ts +0 -53
  207. package/src/viewer/webmcp/__tests__/analytics.test.ts +0 -108
  208. package/src/viewer/webmcp/analytics.ts +0 -165
  209. package/src/viewer/webmcp/index.ts +0 -3
  210. package/src/viewer/webmcp/posthog-bridge.ts +0 -39
  211. package/src/viewer/webmcp/runtime-tools.ts +0 -152
  212. package/src/viewer/webmcp/scan-utils.ts +0 -135
  213. package/src/viewer/webmcp/use-tool-analytics.ts +0 -69
  214. package/src/viewer/webmcp/viewer-state.ts +0 -45
  215. /package/dist/{discovery-NEOY4MPN.js.map → ai-client-I6MDWNYA.js.map} +0 -0
  216. /package/dist/{chunk-PW7QTQA6.js.map → chunk-4OC7FTJB.js.map} +0 -0
  217. /package/dist/{chunk-HRFUSSZI.js.map → chunk-AM4MRTMN.js.map} +0 -0
  218. /package/dist/{scan-CJF2DOQW.js.map → discovery-ZJQSXF56.js.map} +0 -0
  219. /package/dist/{generate-FBHSXR3D.js.map → generate-RJFS2JWA.js.map} +0 -0
  220. /package/dist/{service-TQYWY65E.js.map → scan-3PMCJ4RB.js.map} +0 -0
  221. /package/dist/{static-viewer-NUBFPKWH.js.map → service-VMGNJZ42.js.map} +0 -0
  222. /package/dist/{test-Z5LVO724.js.map → test-SI4NSHQX.js.map} +0 -0
  223. /package/dist/{tokens-CE46OTMD.js.map → tokens-T6SIVUT5.js.map} +0 -0
@@ -1,486 +0,0 @@
1
- /**
2
- * JSX Parser
3
- *
4
- * Parses JSX strings into a structured component tree that can be
5
- * used for rendering multi-component compositions.
6
- */
7
-
8
- import { parse } from "@babel/parser";
9
- import type {
10
- JSXElement,
11
- JSXFragment,
12
- JSXText,
13
- JSXExpressionContainer,
14
- JSXAttribute,
15
- JSXSpreadAttribute,
16
- Expression,
17
- Node,
18
- } from "@babel/types";
19
-
20
- /**
21
- * Represents a parsed JSX component node
22
- */
23
- export interface ComponentNode {
24
- /** Component name (e.g., "Button", "Card") or HTML element name (e.g., "div") */
25
- name: string;
26
- /** Whether this is an HTML element (lowercase) vs custom component (PascalCase) */
27
- isHtmlElement: boolean;
28
- /** Props/attributes passed to the component */
29
- props: Record<string, unknown>;
30
- /** Child nodes (can be ComponentNode, string, or expression) */
31
- children: Array<ComponentNode | string>;
32
- }
33
-
34
- /**
35
- * Result of parsing a JSX string
36
- */
37
- export interface ParseResult {
38
- /** The root component tree */
39
- tree: ComponentNode | ComponentNode[];
40
- /** List of all unique component names found (excluding HTML elements) */
41
- components: string[];
42
- /** Any parsing errors */
43
- errors: string[];
44
- }
45
-
46
- /**
47
- * Parse a JSX string into a component tree.
48
- *
49
- * @param jsx - The JSX string to parse
50
- * @returns ParseResult with the component tree and metadata
51
- *
52
- * @example
53
- * ```ts
54
- * const result = parseJSX('<Card><Button variant="primary">Click</Button></Card>');
55
- * // result.tree = { name: 'Card', props: {}, children: [{ name: 'Button', props: { variant: 'primary' }, children: ['Click'] }] }
56
- * // result.components = ['Card', 'Button']
57
- * ```
58
- */
59
- export function parseJSX(jsx: string): ParseResult {
60
- const errors: string[] = [];
61
- const components = new Set<string>();
62
-
63
- try {
64
- // Wrap in a fragment to handle multiple root elements
65
- const wrappedJsx = `<>${jsx}</>`;
66
-
67
- const ast = parse(wrappedJsx, {
68
- sourceType: "module",
69
- plugins: ["jsx"],
70
- });
71
-
72
- // Find the expression statement containing our JSX
73
- const program = ast.program;
74
- const firstStatement = program.body[0];
75
-
76
- if (firstStatement?.type !== "ExpressionStatement") {
77
- return {
78
- tree: [],
79
- components: [],
80
- errors: ["Invalid JSX: expected expression statement"],
81
- };
82
- }
83
-
84
- const expression = firstStatement.expression;
85
-
86
- if (expression.type !== "JSXFragment" && expression.type !== "JSXElement") {
87
- return {
88
- tree: [],
89
- components: [],
90
- errors: ["Invalid JSX: expected JSX element or fragment"],
91
- };
92
- }
93
-
94
- // Parse the JSX tree
95
- const parseNode = (
96
- node: JSXElement | JSXFragment | JSXText | JSXExpressionContainer
97
- ): ComponentNode | string | null => {
98
- if (node.type === "JSXText") {
99
- const text = node.value.trim();
100
- return text || null;
101
- }
102
-
103
- if (node.type === "JSXExpressionContainer") {
104
- // Handle expressions like {variable} or {condition && <Component />}
105
- const exprValue = evaluateExpression(node.expression);
106
- // Convert to string or return null if not renderable
107
- if (exprValue === null || exprValue === undefined) {
108
- return null;
109
- }
110
- if (typeof exprValue === "string") {
111
- return exprValue;
112
- }
113
- // For non-string primitives, convert to string
114
- return String(exprValue);
115
- }
116
-
117
- if (node.type === "JSXFragment") {
118
- // For fragments, return children directly
119
- const children = node.children
120
- .map((child) => parseNode(child as JSXElement | JSXFragment | JSXText | JSXExpressionContainer))
121
- .filter((c): c is ComponentNode | string => c !== null);
122
-
123
- // If only one child, return it directly
124
- if (children.length === 1 && typeof children[0] !== "string") {
125
- return children[0];
126
- }
127
-
128
- // Return a special fragment node
129
- return {
130
- name: "Fragment",
131
- isHtmlElement: false,
132
- props: {},
133
- children,
134
- };
135
- }
136
-
137
- if (node.type === "JSXElement") {
138
- const openingElement = node.openingElement;
139
- let name: string;
140
-
141
- if (openingElement.name.type === "JSXIdentifier") {
142
- name = openingElement.name.name;
143
- } else if (openingElement.name.type === "JSXMemberExpression") {
144
- // Handle things like MyLib.Button
145
- name = getMemberExpressionName(openingElement.name);
146
- } else {
147
- name = "Unknown";
148
- }
149
-
150
- // Check if it's an HTML element (lowercase first letter)
151
- const isHtmlElement = /^[a-z]/.test(name);
152
-
153
- // Track custom components
154
- if (!isHtmlElement && name !== "Fragment") {
155
- components.add(name);
156
- }
157
-
158
- // Parse props
159
- const props: Record<string, unknown> = {};
160
- for (const attr of openingElement.attributes) {
161
- if (attr.type === "JSXAttribute") {
162
- const propName = attr.name.type === "JSXIdentifier" ? attr.name.name : String(attr.name.name);
163
- const propValue = parseAttributeValue(attr);
164
- props[propName] = propValue;
165
- } else if (attr.type === "JSXSpreadAttribute") {
166
- // Handle spread attributes {...props}
167
- const spreadValue = evaluateExpression(attr.argument);
168
- if (typeof spreadValue === "object" && spreadValue !== null) {
169
- Object.assign(props, spreadValue);
170
- }
171
- }
172
- }
173
-
174
- // Parse children
175
- const children = node.children
176
- .map((child) => parseNode(child as JSXElement | JSXFragment | JSXText | JSXExpressionContainer))
177
- .filter((c): c is ComponentNode | string => c !== null);
178
-
179
- return {
180
- name,
181
- isHtmlElement,
182
- props,
183
- children,
184
- };
185
- }
186
-
187
- return null;
188
- };
189
-
190
- // Parse the root
191
- const rootNode = parseNode(expression);
192
-
193
- if (rootNode === null) {
194
- return {
195
- tree: [],
196
- components: [],
197
- errors: ["Failed to parse JSX"],
198
- };
199
- }
200
-
201
- // If we got a Fragment with multiple children from our wrapper, unwrap it
202
- if (
203
- typeof rootNode !== "string" &&
204
- rootNode.name === "Fragment" &&
205
- rootNode.children.length > 0
206
- ) {
207
- // Check if all children are component nodes
208
- const componentChildren = rootNode.children.filter(
209
- (c): c is ComponentNode => typeof c !== "string"
210
- );
211
-
212
- if (componentChildren.length === 1) {
213
- return {
214
- tree: componentChildren[0],
215
- components: Array.from(components).sort(),
216
- errors,
217
- };
218
- }
219
-
220
- if (componentChildren.length > 1) {
221
- return {
222
- tree: componentChildren,
223
- components: Array.from(components).sort(),
224
- errors,
225
- };
226
- }
227
- }
228
-
229
- return {
230
- tree: typeof rootNode === "string" ? [] : rootNode,
231
- components: Array.from(components).sort(),
232
- errors,
233
- };
234
- } catch (error) {
235
- const message = error instanceof Error ? error.message : "Unknown parse error";
236
- return {
237
- tree: [],
238
- components: [],
239
- errors: [message],
240
- };
241
- }
242
- }
243
-
244
- /**
245
- * Get the full name from a JSX member expression (e.g., MyLib.Button)
246
- */
247
- function getMemberExpressionName(node: any): string {
248
- if (node.type === "JSXIdentifier") {
249
- return node.name;
250
- }
251
- if (node.type === "JSXMemberExpression") {
252
- return `${getMemberExpressionName(node.object)}.${node.property.name}`;
253
- }
254
- return "Unknown";
255
- }
256
-
257
- /**
258
- * Parse a JSX attribute value
259
- */
260
- function parseAttributeValue(attr: JSXAttribute): unknown {
261
- const value = attr.value;
262
-
263
- // Boolean attribute (no value means true)
264
- if (value === null || value === undefined) {
265
- return true;
266
- }
267
-
268
- // String literal
269
- if (value.type === "StringLiteral") {
270
- return (value as { value: string }).value;
271
- }
272
-
273
- // Expression container {value}
274
- if (value.type === "JSXExpressionContainer") {
275
- return evaluateExpression((value as JSXExpressionContainer).expression);
276
- }
277
-
278
- // JSX element as attribute value
279
- if (value.type === "JSXElement" || value.type === "JSXFragment") {
280
- // This would be something like <Component prop={<Other />} />
281
- // We'll return a string representation for now
282
- return "[JSX Element]";
283
- }
284
-
285
- return null;
286
- }
287
-
288
- /**
289
- * Evaluate a JavaScript expression from JSX.
290
- * This handles common cases but is intentionally limited for safety.
291
- */
292
- function evaluateExpression(expr: Expression | Node): unknown {
293
- if (!expr || expr.type === "JSXEmptyExpression") {
294
- return null;
295
- }
296
-
297
- switch (expr.type) {
298
- case "StringLiteral":
299
- return expr.value;
300
-
301
- case "NumericLiteral":
302
- return expr.value;
303
-
304
- case "BooleanLiteral":
305
- return expr.value;
306
-
307
- case "NullLiteral":
308
- return null;
309
-
310
- case "Identifier":
311
- // Handle special identifiers
312
- if (expr.name === "undefined") return undefined;
313
- if (expr.name === "true") return true;
314
- if (expr.name === "false") return false;
315
- // Return the identifier name as a placeholder
316
- return `{${expr.name}}`;
317
-
318
- case "TemplateLiteral":
319
- // Handle template literals with no expressions
320
- if (expr.expressions.length === 0 && expr.quasis.length === 1) {
321
- return expr.quasis[0].value.cooked || expr.quasis[0].value.raw;
322
- }
323
- // For template literals with expressions, return a placeholder
324
- return "[Template Literal]";
325
-
326
- case "ObjectExpression": {
327
- // Parse object literals like {{ key: 'value' }}
328
- const obj: Record<string, unknown> = {};
329
- for (const prop of expr.properties) {
330
- if (prop.type === "ObjectProperty") {
331
- const key =
332
- prop.key.type === "Identifier"
333
- ? prop.key.name
334
- : prop.key.type === "StringLiteral"
335
- ? prop.key.value
336
- : String(prop.key);
337
- obj[key] = evaluateExpression(prop.value as Expression);
338
- }
339
- }
340
- return obj;
341
- }
342
-
343
- case "ArrayExpression":
344
- // Parse array literals like {[1, 2, 3]}
345
- return expr.elements.map((el) =>
346
- el ? evaluateExpression(el as Expression) : null
347
- );
348
-
349
- case "ArrowFunctionExpression":
350
- case "FunctionExpression":
351
- // Return a placeholder for functions
352
- return "[Function]";
353
-
354
- case "UnaryExpression":
355
- // Handle negation like {-5}
356
- if (expr.operator === "-" && expr.argument.type === "NumericLiteral") {
357
- return -expr.argument.value;
358
- }
359
- if (expr.operator === "!" && expr.argument.type === "BooleanLiteral") {
360
- return !expr.argument.value;
361
- }
362
- return null;
363
-
364
- case "ConditionalExpression":
365
- // Ternary operator - return a placeholder
366
- return "[Conditional]";
367
-
368
- case "LogicalExpression":
369
- // && or || - return a placeholder
370
- return "[Logical Expression]";
371
-
372
- case "MemberExpression":
373
- // Something like obj.prop - return placeholder
374
- return "[Member Expression]";
375
-
376
- case "CallExpression":
377
- // Function call - return placeholder
378
- return "[Function Call]";
379
-
380
- default:
381
- return null;
382
- }
383
- }
384
-
385
- /**
386
- * Extract all component names from a parsed tree
387
- */
388
- export function extractComponents(tree: ComponentNode | ComponentNode[]): string[] {
389
- const components = new Set<string>();
390
-
391
- const visit = (node: ComponentNode) => {
392
- if (!node.isHtmlElement && node.name !== "Fragment") {
393
- components.add(node.name);
394
- }
395
- for (const child of node.children) {
396
- if (typeof child !== "string") {
397
- visit(child);
398
- }
399
- }
400
- };
401
-
402
- if (Array.isArray(tree)) {
403
- for (const node of tree) {
404
- visit(node);
405
- }
406
- } else {
407
- visit(tree);
408
- }
409
-
410
- return Array.from(components).sort();
411
- }
412
-
413
- /**
414
- * Validate that all components in the tree exist in the available components
415
- */
416
- export function validateComponents(
417
- tree: ComponentNode | ComponentNode[],
418
- availableComponents: string[]
419
- ): { valid: boolean; missing: string[]; suggestions: Map<string, string[]> } {
420
- const used = extractComponents(tree);
421
- const available = new Set(availableComponents.map((c) => c.toLowerCase()));
422
- const availableOriginal = new Map(
423
- availableComponents.map((c) => [c.toLowerCase(), c])
424
- );
425
-
426
- const missing: string[] = [];
427
- const suggestions = new Map<string, string[]>();
428
-
429
- for (const component of used) {
430
- const lower = component.toLowerCase();
431
- if (!available.has(lower)) {
432
- missing.push(component);
433
-
434
- // Find similar components for suggestions
435
- const similar = availableComponents.filter((c) => {
436
- const clower = c.toLowerCase();
437
- // Check for partial matches
438
- return (
439
- clower.includes(lower) ||
440
- lower.includes(clower) ||
441
- levenshteinDistance(lower, clower) <= 2
442
- );
443
- });
444
-
445
- if (similar.length > 0) {
446
- suggestions.set(component, similar.slice(0, 3));
447
- }
448
- }
449
- }
450
-
451
- return {
452
- valid: missing.length === 0,
453
- missing,
454
- suggestions,
455
- };
456
- }
457
-
458
- /**
459
- * Simple Levenshtein distance for fuzzy matching
460
- */
461
- function levenshteinDistance(a: string, b: string): number {
462
- const matrix: number[][] = [];
463
-
464
- for (let i = 0; i <= b.length; i++) {
465
- matrix[i] = [i];
466
- }
467
- for (let j = 0; j <= a.length; j++) {
468
- matrix[0][j] = j;
469
- }
470
-
471
- for (let i = 1; i <= b.length; i++) {
472
- for (let j = 1; j <= a.length; j++) {
473
- if (b.charAt(i - 1) === a.charAt(j - 1)) {
474
- matrix[i][j] = matrix[i - 1][j - 1];
475
- } else {
476
- matrix[i][j] = Math.min(
477
- matrix[i - 1][j - 1] + 1,
478
- matrix[i][j - 1] + 1,
479
- matrix[i - 1][j] + 1
480
- );
481
- }
482
- }
483
- }
484
-
485
- return matrix[b.length][a.length];
486
- }
@@ -1,25 +0,0 @@
1
- /**
2
- * Preview Frame Entry Point
3
- *
4
- * This is the entry point for the isolated iframe that renders component previews.
5
- * It runs inside the iframe and receives render requests from the parent window
6
- * via postMessage.
7
- */
8
-
9
- import { createRoot } from 'react-dom/client';
10
- import { PreviewFrameHost } from './components/PreviewFrameHost.js';
11
-
12
- // Mount the preview host
13
- const rootElement = document.getElementById('preview-root');
14
-
15
- if (rootElement) {
16
- const root = createRoot(rootElement);
17
- root.render(<PreviewFrameHost />);
18
- }
19
-
20
- // HMR support
21
- // @ts-expect-error Vite HMR types
22
- if (import.meta.hot) {
23
- // @ts-expect-error Vite HMR types
24
- import.meta.hot.accept();
25
- }
@@ -1,125 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8" />
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
- <title>Fragment Preview</title>
7
- <style>
8
- /* Reset and base styles for isolated preview */
9
- *, *::before, *::after {
10
- box-sizing: border-box;
11
- }
12
-
13
- html, body {
14
- margin: 0;
15
- padding: 0;
16
- font-family: var(--fui-font-sans, Inter, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif);
17
- -webkit-font-smoothing: antialiased;
18
- -moz-osx-font-smoothing: grayscale;
19
- width: 100%;
20
- height: 100%;
21
- overflow: hidden;
22
- }
23
-
24
- body {
25
- background: var(--fui-bg-primary, #ffffff);
26
- min-height: 100%;
27
- }
28
-
29
- #preview-root {
30
- width: 100%;
31
- height: 100%;
32
- min-height: 400px;
33
- padding: 16px;
34
- display: flex;
35
- flex-direction: column;
36
- box-sizing: border-box;
37
- overflow: auto;
38
- }
39
-
40
- #preview-root > * {
41
- width: 100%;
42
- flex: 1;
43
- }
44
-
45
- /* Hide scrollbars but allow scrolling */
46
- html::-webkit-scrollbar {
47
- width: 0;
48
- height: 0;
49
- }
50
-
51
- html {
52
- scrollbar-width: none;
53
- }
54
-
55
- /* Error display styles */
56
- .preview-error {
57
- padding: 16px;
58
- background: #fef2f2;
59
- border: 1px solid #fecaca;
60
- border-radius: 8px;
61
- color: #dc2626;
62
- font-size: 14px;
63
- max-width: 400px;
64
- }
65
-
66
- .preview-error pre {
67
- margin: 8px 0 0;
68
- padding: 8px;
69
- background: #fff;
70
- border-radius: 4px;
71
- overflow-x: auto;
72
- font-size: 12px;
73
- font-family: 'JetBrains Mono', ui-monospace, monospace;
74
- }
75
-
76
- /* Visually hidden but accessible to screen readers */
77
- .sr-only {
78
- position: absolute;
79
- width: 1px;
80
- height: 1px;
81
- padding: 0;
82
- margin: -1px;
83
- overflow: hidden;
84
- clip: rect(0, 0, 0, 0);
85
- white-space: nowrap;
86
- border-width: 0;
87
- }
88
-
89
- /* Loading indicator */
90
- .preview-loading {
91
- display: flex;
92
- align-items: center;
93
- justify-content: center;
94
- gap: 8px;
95
- color: #6b7280;
96
- font-size: 14px;
97
- }
98
-
99
- .preview-loading .spinner {
100
- width: 20px;
101
- height: 20px;
102
- border: 2px solid #e5e7eb;
103
- border-top-color: #3b82f6;
104
- border-radius: 50%;
105
- animation: spin 0.8s linear infinite;
106
- }
107
-
108
- @keyframes spin {
109
- to { transform: rotate(360deg); }
110
- }
111
- </style>
112
- </head>
113
- <body>
114
- <main style="height: 100%">
115
- <h1 class="sr-only">Component Preview</h1>
116
- <div id="preview-root">
117
- <div class="preview-loading">
118
- <div class="spinner"></div>
119
- <span>Loading preview...</span>
120
- </div>
121
- </div>
122
- </main>
123
- <script type="module" src="/src/preview-frame-entry.tsx"></script>
124
- </body>
125
- </html>
Binary file
@@ -1,68 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8" />
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
- <title>Fragments Render</title>
7
- <link rel="preconnect" href="https://fonts.googleapis.com" />
8
- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
9
- <link
10
- href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap"
11
- rel="stylesheet"
12
- />
13
- <style>
14
- /* Reset and base styles for isolated rendering */
15
- *, *::before, *::after {
16
- box-sizing: border-box;
17
- }
18
-
19
- html, body {
20
- margin: 0;
21
- padding: 0;
22
- font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
23
- -webkit-font-smoothing: antialiased;
24
- -moz-osx-font-smoothing: grayscale;
25
- }
26
-
27
- body {
28
- background: #ffffff;
29
- min-height: 100vh;
30
- }
31
-
32
- #render-root {
33
- padding: 16px;
34
- display: inline-block;
35
- }
36
-
37
- /* Signal that rendering is complete */
38
- #render-root.ready {
39
- /* Used by Playwright to know when to capture */
40
- }
41
-
42
- /* Error display */
43
- .render-error {
44
- padding: 16px;
45
- background: #fef2f2;
46
- border: 1px solid #fecaca;
47
- border-radius: 8px;
48
- color: #dc2626;
49
- font-size: 14px;
50
- }
51
-
52
- .render-error pre {
53
- margin: 8px 0 0;
54
- padding: 8px;
55
- background: #fff;
56
- border-radius: 4px;
57
- overflow-x: auto;
58
- font-size: 12px;
59
- font-family: 'JetBrains Mono', monospace;
60
- }
61
- </style>
62
- <!-- PROJECT_STYLES_PLACEHOLDER -->
63
- </head>
64
- <body>
65
- <div id="render-root"></div>
66
- <!-- RENDER_SCRIPT_PLACEHOLDER -->
67
- </body>
68
- </html>