@fragments-sdk/cli 0.7.0 → 0.7.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 (173) hide show
  1. package/LICENSE +77 -14
  2. package/dist/bin.js +247 -247
  3. package/dist/bin.js.map +1 -1
  4. package/dist/{chunk-CVXKXVOY.js → chunk-3T6QL7IY.js} +47 -29
  5. package/dist/chunk-3T6QL7IY.js.map +1 -0
  6. package/dist/{chunk-7OPWMLOE.js → chunk-7KUSBMI4.js} +114 -112
  7. package/dist/chunk-7KUSBMI4.js.map +1 -0
  8. package/dist/{chunk-XHUDJNN3.js → chunk-DH4ETVSM.js} +18 -18
  9. package/dist/chunk-DH4ETVSM.js.map +1 -0
  10. package/dist/{chunk-RVRTRESS.js → chunk-DQHWLAUV.js} +29 -29
  11. package/dist/chunk-DQHWLAUV.js.map +1 -0
  12. package/dist/{chunk-6JBGU74P.js → chunk-GHYYFAQN.js} +23 -23
  13. package/dist/chunk-GHYYFAQN.js.map +1 -0
  14. package/dist/{chunk-NWQ4CJOQ.js → chunk-GKX2HPZ6.js} +40 -40
  15. package/dist/chunk-GKX2HPZ6.js.map +1 -0
  16. package/dist/{chunk-TJ34N7C7.js → chunk-OOGTG5FM.js} +34 -33
  17. package/dist/chunk-OOGTG5FM.js.map +1 -0
  18. package/dist/{core-W2HYIQW6.js → core-UQXZTBFZ.js} +24 -26
  19. package/dist/{generate-LMTISDIJ.js → generate-GP6ZLAQB.js} +5 -5
  20. package/dist/generate-GP6ZLAQB.js.map +1 -0
  21. package/dist/index.d.ts +23 -27
  22. package/dist/index.js +10 -10
  23. package/dist/{init-7CHRKQ7P.js → init-W72WBSU2.js} +5 -5
  24. package/dist/{init-7CHRKQ7P.js.map → init-W72WBSU2.js.map} +1 -1
  25. package/dist/mcp-bin.js +73 -73
  26. package/dist/mcp-bin.js.map +1 -1
  27. package/dist/scan-V54HWRDY.js +12 -0
  28. package/dist/{service-T2L7VLTE.js → service-PVGTYUKX.js} +6 -6
  29. package/dist/{static-viewer-GBR7YNF3.js → static-viewer-KILKIVN7.js} +4 -4
  30. package/dist/{test-OJRXNDO2.js → test-3YRYQRGV.js} +19 -19
  31. package/dist/test-3YRYQRGV.js.map +1 -0
  32. package/dist/{tokens-3BWDESVM.js → tokens-IXSQHPQK.js} +5 -5
  33. package/dist/{viewer-SUFOISZM.js → viewer-K42REJU2.js} +199 -199
  34. package/dist/viewer-K42REJU2.js.map +1 -0
  35. package/package.json +13 -2
  36. package/src/ai.ts +5 -5
  37. package/src/analyze.ts +11 -11
  38. package/src/bin.ts +1 -1
  39. package/src/build.ts +37 -35
  40. package/src/commands/a11y.ts +6 -6
  41. package/src/commands/add.ts +11 -11
  42. package/src/commands/audit.ts +4 -4
  43. package/src/commands/baseline.ts +3 -3
  44. package/src/commands/build.ts +8 -8
  45. package/src/commands/compare.ts +20 -20
  46. package/src/commands/context.ts +16 -16
  47. package/src/commands/enhance.ts +36 -36
  48. package/src/commands/generate.ts +1 -1
  49. package/src/commands/graph.ts +5 -5
  50. package/src/commands/init.ts +1 -1
  51. package/src/commands/link/figma.ts +82 -82
  52. package/src/commands/link/index.ts +3 -3
  53. package/src/commands/link/storybook.ts +9 -9
  54. package/src/commands/list.ts +2 -2
  55. package/src/commands/reset.ts +15 -15
  56. package/src/commands/scan.ts +27 -27
  57. package/src/commands/storygen.ts +24 -24
  58. package/src/commands/validate.ts +2 -2
  59. package/src/commands/verify.ts +8 -8
  60. package/src/core/auto-props.ts +4 -4
  61. package/src/core/composition.test.ts +36 -36
  62. package/src/core/composition.ts +19 -19
  63. package/src/core/config.ts +6 -6
  64. package/src/core/{defineSegment.ts → defineFragment.ts} +16 -22
  65. package/src/core/discovery.ts +6 -6
  66. package/src/core/figma.ts +2 -2
  67. package/src/core/graph-extractor.test.ts +77 -77
  68. package/src/core/graph-extractor.ts +32 -32
  69. package/src/core/importAnalyzer.ts +1 -1
  70. package/src/core/index.ts +22 -23
  71. package/src/core/loader.ts +21 -24
  72. package/src/core/node.ts +5 -5
  73. package/src/core/parser.ts +71 -31
  74. package/src/core/previewLoader.ts +1 -1
  75. package/src/core/schema.ts +16 -16
  76. package/src/core/storyAdapter.test.ts +87 -87
  77. package/src/core/storyAdapter.ts +16 -16
  78. package/src/core/token-parser.ts +9 -1
  79. package/src/core/types.ts +21 -26
  80. package/src/diff.ts +22 -22
  81. package/src/index.ts +2 -2
  82. package/src/mcp/server.ts +80 -80
  83. package/src/migrate/__tests__/utils/utils.test.ts +3 -3
  84. package/src/migrate/bin.ts +4 -4
  85. package/src/migrate/converter.ts +16 -16
  86. package/src/migrate/index.ts +3 -3
  87. package/src/migrate/migrate.ts +3 -3
  88. package/src/migrate/parser.ts +8 -8
  89. package/src/migrate/report.ts +2 -2
  90. package/src/migrate/types.ts +4 -4
  91. package/src/screenshot.ts +22 -22
  92. package/src/service/__tests__/props-extractor.test.ts +15 -15
  93. package/src/service/analytics.ts +39 -39
  94. package/src/service/enhance/codebase-scanner.ts +1 -1
  95. package/src/service/enhance/index.ts +1 -1
  96. package/src/service/enhance/props-extractor.ts +2 -2
  97. package/src/service/enhance/types.ts +2 -2
  98. package/src/service/index.ts +2 -2
  99. package/src/service/metrics-store.ts +1 -1
  100. package/src/service/patch-generator.ts +1 -1
  101. package/src/setup.ts +52 -52
  102. package/src/shared/dev-server-client.ts +7 -7
  103. package/src/shared/fragment-loader.ts +59 -0
  104. package/src/shared/index.ts +1 -1
  105. package/src/shared/types.ts +4 -4
  106. package/src/static-viewer.ts +35 -35
  107. package/src/test/discovery.ts +6 -6
  108. package/src/test/index.ts +5 -5
  109. package/src/test/reporters/console.ts +1 -1
  110. package/src/test/reporters/junit.ts +1 -1
  111. package/src/test/runner.ts +7 -7
  112. package/src/test/types.ts +3 -3
  113. package/src/test/watch.ts +9 -9
  114. package/src/validators.ts +26 -26
  115. package/src/viewer/__tests__/render-utils.test.ts +28 -28
  116. package/src/viewer/__tests__/viewer-integration.test.ts +4 -4
  117. package/src/viewer/cli/health.ts +26 -26
  118. package/src/viewer/components/App.tsx +79 -79
  119. package/src/viewer/components/BottomPanel.tsx +17 -17
  120. package/src/viewer/components/CodePanel.tsx +3 -3
  121. package/src/viewer/components/CommandPalette.tsx +11 -11
  122. package/src/viewer/components/ComponentGraph.tsx +28 -28
  123. package/src/viewer/components/ComponentHeader.tsx +2 -2
  124. package/src/viewer/components/ContractPanel.tsx +6 -6
  125. package/src/viewer/components/FigmaEmbed.tsx +9 -9
  126. package/src/viewer/components/HealthDashboard.tsx +17 -17
  127. package/src/viewer/components/InteractionsPanel.tsx +2 -2
  128. package/src/viewer/components/IsolatedPreviewFrame.tsx +6 -6
  129. package/src/viewer/components/IsolatedRender.tsx +10 -10
  130. package/src/viewer/components/LeftSidebar.tsx +28 -28
  131. package/src/viewer/components/MultiViewportPreview.tsx +14 -14
  132. package/src/viewer/components/PreviewArea.tsx +11 -11
  133. package/src/viewer/components/PreviewFrameHost.tsx +51 -51
  134. package/src/viewer/components/RightSidebar.tsx +9 -9
  135. package/src/viewer/components/Sidebar.tsx +17 -17
  136. package/src/viewer/components/StoryRenderer.tsx +2 -2
  137. package/src/viewer/components/TokenStylePanel.tsx +1 -1
  138. package/src/viewer/components/UsageSection.tsx +2 -2
  139. package/src/viewer/components/VariantMatrix.tsx +11 -11
  140. package/src/viewer/components/VariantRenderer.tsx +3 -3
  141. package/src/viewer/components/VariantTabs.tsx +2 -2
  142. package/src/viewer/components/_future/CreatePage.tsx +6 -6
  143. package/src/viewer/composition-renderer.ts +11 -11
  144. package/src/viewer/entry.tsx +40 -40
  145. package/src/viewer/hooks/useFigmaIntegration.ts +1 -1
  146. package/src/viewer/hooks/usePreviewBridge.ts +5 -5
  147. package/src/viewer/hooks/useUrlState.ts +6 -6
  148. package/src/viewer/index.ts +2 -2
  149. package/src/viewer/intelligence/healthReport.ts +17 -17
  150. package/src/viewer/intelligence/styleDrift.ts +1 -1
  151. package/src/viewer/intelligence/usageScanner.ts +1 -1
  152. package/src/viewer/render-template.html +1 -1
  153. package/src/viewer/render-utils.ts +21 -21
  154. package/src/viewer/server.ts +18 -18
  155. package/src/viewer/utils/detectRelationships.ts +22 -22
  156. package/src/viewer/vite-plugin.ts +213 -213
  157. package/dist/chunk-6JBGU74P.js.map +0 -1
  158. package/dist/chunk-7OPWMLOE.js.map +0 -1
  159. package/dist/chunk-CVXKXVOY.js.map +0 -1
  160. package/dist/chunk-NWQ4CJOQ.js.map +0 -1
  161. package/dist/chunk-RVRTRESS.js.map +0 -1
  162. package/dist/chunk-TJ34N7C7.js.map +0 -1
  163. package/dist/chunk-XHUDJNN3.js.map +0 -1
  164. package/dist/generate-LMTISDIJ.js.map +0 -1
  165. package/dist/scan-WY23TJCP.js +0 -12
  166. package/dist/test-OJRXNDO2.js.map +0 -1
  167. package/dist/viewer-SUFOISZM.js.map +0 -1
  168. package/src/shared/segment-loader.ts +0 -59
  169. /package/dist/{core-W2HYIQW6.js.map → core-UQXZTBFZ.js.map} +0 -0
  170. /package/dist/{scan-WY23TJCP.js.map → scan-V54HWRDY.js.map} +0 -0
  171. /package/dist/{service-T2L7VLTE.js.map → service-PVGTYUKX.js.map} +0 -0
  172. /package/dist/{static-viewer-GBR7YNF3.js.map → static-viewer-KILKIVN7.js.map} +0 -0
  173. /package/dist/{tokens-3BWDESVM.js.map → tokens-IXSQHPQK.js.map} +0 -0
@@ -1,8 +1,8 @@
1
1
  import { describe, it, expect } from "vitest";
2
2
  import { analyzeComposition } from "./composition.js";
3
- import type { CompiledSegment } from "./types.js";
3
+ import type { CompiledFragment } from "./types.js";
4
4
 
5
- function makeSegment(overrides: Partial<CompiledSegment> & { meta: CompiledSegment["meta"] }): CompiledSegment {
5
+ function makeFragment(overrides: Partial<CompiledFragment> & { meta: CompiledFragment["meta"] }): CompiledFragment {
6
6
  return {
7
7
  filePath: "src/components/Test.tsx",
8
8
  usage: { when: [], whenNot: [] },
@@ -12,8 +12,8 @@ function makeSegment(overrides: Partial<CompiledSegment> & { meta: CompiledSegme
12
12
  };
13
13
  }
14
14
 
15
- const baseSegments: Record<string, CompiledSegment> = {
16
- Button: makeSegment({
15
+ const baseFragments: Record<string, CompiledFragment> = {
16
+ Button: makeFragment({
17
17
  meta: { name: "Button", description: "A button", category: "actions", status: "stable" },
18
18
  relations: [
19
19
  { component: "IconButton", relationship: "alternative", note: "Use for icon-only actions" },
@@ -24,19 +24,19 @@ const baseSegments: Record<string, CompiledSegment> = {
24
24
  whenNot: ["Use Link for navigation"],
25
25
  },
26
26
  }),
27
- IconButton: makeSegment({
27
+ IconButton: makeFragment({
28
28
  meta: { name: "IconButton", description: "Icon-only button", category: "actions", status: "stable" },
29
29
  relations: [
30
30
  { component: "Button", relationship: "alternative", note: "Use for labeled actions" },
31
31
  ],
32
32
  }),
33
- ButtonGroup: makeSegment({
33
+ ButtonGroup: makeFragment({
34
34
  meta: { name: "ButtonGroup", description: "Groups buttons", category: "actions", status: "stable" },
35
35
  relations: [
36
36
  { component: "Button", relationship: "child", note: "Contains buttons" },
37
37
  ],
38
38
  }),
39
- TextField: makeSegment({
39
+ TextField: makeFragment({
40
40
  meta: { name: "TextField", description: "Text input", category: "forms", status: "stable" },
41
41
  relations: [
42
42
  { component: "Form", relationship: "parent", note: "Should be inside a Form" },
@@ -47,22 +47,22 @@ const baseSegments: Record<string, CompiledSegment> = {
47
47
  whenNot: ["Use TextArea for multiline input"],
48
48
  },
49
49
  }),
50
- Form: makeSegment({
50
+ Form: makeFragment({
51
51
  meta: { name: "Form", description: "Form container", category: "forms", status: "stable" },
52
52
  }),
53
- Label: makeSegment({
53
+ Label: makeFragment({
54
54
  meta: { name: "Label", description: "Form label", category: "forms", status: "stable" },
55
55
  }),
56
- Alert: makeSegment({
56
+ Alert: makeFragment({
57
57
  meta: { name: "Alert", description: "Feedback alert", category: "feedback", status: "stable" },
58
58
  }),
59
- Toast: makeSegment({
59
+ Toast: makeFragment({
60
60
  meta: { name: "Toast", description: "Toast notification", category: "feedback", status: "experimental" },
61
61
  }),
62
- OldButton: makeSegment({
62
+ OldButton: makeFragment({
63
63
  meta: { name: "OldButton", description: "Use Button instead", category: "actions", status: "deprecated" },
64
64
  }),
65
- Link: makeSegment({
65
+ Link: makeFragment({
66
66
  meta: { name: "Link", description: "Navigation link", category: "navigation", status: "stable" },
67
67
  usage: {
68
68
  when: ["User needs to navigate"],
@@ -74,25 +74,25 @@ const baseSegments: Record<string, CompiledSegment> = {
74
74
  describe("analyzeComposition", () => {
75
75
  describe("name validation", () => {
76
76
  it("should split components into found and unknown", () => {
77
- const result = analyzeComposition(baseSegments, ["Button", "NonExistent", "TextField", "AlsoFake"]);
77
+ const result = analyzeComposition(baseFragments, ["Button", "NonExistent", "TextField", "AlsoFake"]);
78
78
  expect(result.components).toEqual(["Button", "TextField"]);
79
79
  expect(result.unknown).toEqual(["NonExistent", "AlsoFake"]);
80
80
  });
81
81
 
82
82
  it("should handle all valid names", () => {
83
- const result = analyzeComposition(baseSegments, ["Button", "TextField"]);
83
+ const result = analyzeComposition(baseFragments, ["Button", "TextField"]);
84
84
  expect(result.components).toEqual(["Button", "TextField"]);
85
85
  expect(result.unknown).toEqual([]);
86
86
  });
87
87
 
88
88
  it("should handle all unknown names", () => {
89
- const result = analyzeComposition(baseSegments, ["Foo", "Bar"]);
89
+ const result = analyzeComposition(baseFragments, ["Foo", "Bar"]);
90
90
  expect(result.components).toEqual([]);
91
91
  expect(result.unknown).toEqual(["Foo", "Bar"]);
92
92
  });
93
93
 
94
94
  it("should handle empty input", () => {
95
- const result = analyzeComposition(baseSegments, []);
95
+ const result = analyzeComposition(baseFragments, []);
96
96
  expect(result.components).toEqual([]);
97
97
  expect(result.unknown).toEqual([]);
98
98
  expect(result.warnings).toEqual([]);
@@ -103,7 +103,7 @@ describe("analyzeComposition", () => {
103
103
 
104
104
  describe("relation checks", () => {
105
105
  it("should warn about missing parent", () => {
106
- const result = analyzeComposition(baseSegments, ["TextField"]);
106
+ const result = analyzeComposition(baseFragments, ["TextField"]);
107
107
  const parentWarning = result.warnings.find((w) => w.type === "missing_parent");
108
108
  expect(parentWarning).toBeDefined();
109
109
  expect(parentWarning!.component).toBe("TextField");
@@ -111,13 +111,13 @@ describe("analyzeComposition", () => {
111
111
  });
112
112
 
113
113
  it("should not warn about parent when parent is selected", () => {
114
- const result = analyzeComposition(baseSegments, ["TextField", "Form"]);
114
+ const result = analyzeComposition(baseFragments, ["TextField", "Form"]);
115
115
  const parentWarning = result.warnings.find((w) => w.type === "missing_parent");
116
116
  expect(parentWarning).toBeUndefined();
117
117
  });
118
118
 
119
119
  it("should suggest missing children", () => {
120
- const result = analyzeComposition(baseSegments, ["ButtonGroup"]);
120
+ const result = analyzeComposition(baseFragments, ["ButtonGroup"]);
121
121
  const childSuggestion = result.suggestions.find(
122
122
  (s) => s.component === "Button" && s.relationship === "child"
123
123
  );
@@ -126,7 +126,7 @@ describe("analyzeComposition", () => {
126
126
  });
127
127
 
128
128
  it("should warn about missing composition peers", () => {
129
- const result = analyzeComposition(baseSegments, ["Button"]);
129
+ const result = analyzeComposition(baseFragments, ["Button"]);
130
130
  const compWarning = result.warnings.find((w) => w.type === "missing_composition");
131
131
  expect(compWarning).toBeDefined();
132
132
  expect(compWarning!.component).toBe("Button");
@@ -134,13 +134,13 @@ describe("analyzeComposition", () => {
134
134
  });
135
135
 
136
136
  it("should not warn when composition peer is selected", () => {
137
- const result = analyzeComposition(baseSegments, ["Button", "ButtonGroup"]);
137
+ const result = analyzeComposition(baseFragments, ["Button", "ButtonGroup"]);
138
138
  const compWarning = result.warnings.find((w) => w.type === "missing_composition");
139
139
  expect(compWarning).toBeUndefined();
140
140
  });
141
141
 
142
142
  it("should suggest missing siblings", () => {
143
- const result = analyzeComposition(baseSegments, ["TextField", "Form"]);
143
+ const result = analyzeComposition(baseFragments, ["TextField", "Form"]);
144
144
  const siblingSuggestion = result.suggestions.find(
145
145
  (s) => s.component === "Label" && s.relationship === "sibling"
146
146
  );
@@ -149,7 +149,7 @@ describe("analyzeComposition", () => {
149
149
  });
150
150
 
151
151
  it("should warn about redundant alternatives", () => {
152
- const result = analyzeComposition(baseSegments, ["Button", "IconButton"]);
152
+ const result = analyzeComposition(baseFragments, ["Button", "IconButton"]);
153
153
  const altWarning = result.warnings.find((w) => w.type === "redundant_alternative");
154
154
  expect(altWarning).toBeDefined();
155
155
  expect(altWarning!.component).toBe("Button");
@@ -157,7 +157,7 @@ describe("analyzeComposition", () => {
157
157
  });
158
158
 
159
159
  it("should not warn when only one alternative is selected", () => {
160
- const result = analyzeComposition(baseSegments, ["Button"]);
160
+ const result = analyzeComposition(baseFragments, ["Button"]);
161
161
  const altWarning = result.warnings.find((w) => w.type === "redundant_alternative");
162
162
  expect(altWarning).toBeUndefined();
163
163
  });
@@ -165,7 +165,7 @@ describe("analyzeComposition", () => {
165
165
 
166
166
  describe("usage conflict checks", () => {
167
167
  it("should emit guideline when whenNot mentions another selected component", () => {
168
- const result = analyzeComposition(baseSegments, ["Button", "Link"]);
168
+ const result = analyzeComposition(baseFragments, ["Button", "Link"]);
169
169
  const conflict = result.guidelines.find(
170
170
  (g) => g.component === "Link" && g.guideline.includes("Button")
171
171
  );
@@ -173,14 +173,14 @@ describe("analyzeComposition", () => {
173
173
  });
174
174
 
175
175
  it("should not emit guideline when no conflict exists", () => {
176
- const result = analyzeComposition(baseSegments, ["Button", "Alert"]);
176
+ const result = analyzeComposition(baseFragments, ["Button", "Alert"]);
177
177
  expect(result.guidelines).toEqual([]);
178
178
  });
179
179
  });
180
180
 
181
181
  describe("status warnings", () => {
182
182
  it("should warn about deprecated components", () => {
183
- const result = analyzeComposition(baseSegments, ["OldButton"]);
183
+ const result = analyzeComposition(baseFragments, ["OldButton"]);
184
184
  const depWarning = result.warnings.find((w) => w.type === "deprecated");
185
185
  expect(depWarning).toBeDefined();
186
186
  expect(depWarning!.component).toBe("OldButton");
@@ -188,7 +188,7 @@ describe("analyzeComposition", () => {
188
188
  });
189
189
 
190
190
  it("should warn about experimental components", () => {
191
- const result = analyzeComposition(baseSegments, ["Toast"]);
191
+ const result = analyzeComposition(baseFragments, ["Toast"]);
192
192
  const expWarning = result.warnings.find((w) => w.type === "experimental");
193
193
  expect(expWarning).toBeDefined();
194
194
  expect(expWarning!.component).toBe("Toast");
@@ -196,7 +196,7 @@ describe("analyzeComposition", () => {
196
196
  });
197
197
 
198
198
  it("should not warn about stable components", () => {
199
- const result = analyzeComposition(baseSegments, ["Button"]);
199
+ const result = analyzeComposition(baseFragments, ["Button"]);
200
200
  const statusWarnings = result.warnings.filter(
201
201
  (w) => w.type === "deprecated" || w.type === "experimental"
202
202
  );
@@ -206,7 +206,7 @@ describe("analyzeComposition", () => {
206
206
 
207
207
  describe("category gap analysis", () => {
208
208
  it("should suggest feedback component when forms are selected without feedback", () => {
209
- const result = analyzeComposition(baseSegments, ["TextField", "Form"]);
209
+ const result = analyzeComposition(baseFragments, ["TextField", "Form"]);
210
210
  const gapSuggestion = result.suggestions.find(
211
211
  (s) => s.relationship === "category_gap"
212
212
  );
@@ -216,7 +216,7 @@ describe("analyzeComposition", () => {
216
216
  });
217
217
 
218
218
  it("should suggest feedback component when actions are selected without feedback", () => {
219
- const result = analyzeComposition(baseSegments, ["Button", "ButtonGroup"]);
219
+ const result = analyzeComposition(baseFragments, ["Button", "ButtonGroup"]);
220
220
  const gapSuggestion = result.suggestions.find(
221
221
  (s) => s.relationship === "category_gap"
222
222
  );
@@ -225,7 +225,7 @@ describe("analyzeComposition", () => {
225
225
  });
226
226
 
227
227
  it("should not suggest category gap when feedback is already present", () => {
228
- const result = analyzeComposition(baseSegments, ["Button", "ButtonGroup", "Alert"]);
228
+ const result = analyzeComposition(baseFragments, ["Button", "ButtonGroup", "Alert"]);
229
229
  const gapSuggestion = result.suggestions.find(
230
230
  (s) => s.relationship === "category_gap"
231
231
  );
@@ -234,7 +234,7 @@ describe("analyzeComposition", () => {
234
234
 
235
235
  it("should prefer stable components over experimental in category gap suggestions", () => {
236
236
  // Alert is stable, Toast is experimental — should pick Alert
237
- const result = analyzeComposition(baseSegments, ["TextField", "Form"]);
237
+ const result = analyzeComposition(baseFragments, ["TextField", "Form"]);
238
238
  const gapSuggestion = result.suggestions.find(
239
239
  (s) => s.relationship === "category_gap"
240
240
  );
@@ -244,7 +244,7 @@ describe("analyzeComposition", () => {
244
244
 
245
245
  describe("deduplication", () => {
246
246
  it("should not suggest the same component twice", () => {
247
- const result = analyzeComposition(baseSegments, ["ButtonGroup", "Button"]);
247
+ const result = analyzeComposition(baseFragments, ["ButtonGroup", "Button"]);
248
248
  const buttonSuggestions = result.suggestions.filter(
249
249
  (s) => s.component === "Button"
250
250
  );
@@ -255,7 +255,7 @@ describe("analyzeComposition", () => {
255
255
 
256
256
  describe("context parameter", () => {
257
257
  it("should accept optional context without error", () => {
258
- const result = analyzeComposition(baseSegments, ["Button"], "building a form page");
258
+ const result = analyzeComposition(baseFragments, ["Button"], "building a form page");
259
259
  expect(result.components).toEqual(["Button"]);
260
260
  });
261
261
  });
@@ -1,4 +1,4 @@
1
- import type { CompiledSegment, RelationshipType } from "./types.js";
1
+ import type { CompiledFragment, RelationshipType } from "./types.js";
2
2
  import type { ComponentGraph } from "@fragments-sdk/context/graph";
3
3
  import { ComponentGraphEngine } from "@fragments-sdk/context/graph";
4
4
 
@@ -66,12 +66,12 @@ const CATEGORY_AFFINITIES: Record<string, string[]> = {
66
66
  * Browser-safe: no Node.js APIs used.
67
67
  */
68
68
  export function analyzeComposition(
69
- segments: Record<string, CompiledSegment>,
69
+ fragments: Record<string, CompiledFragment>,
70
70
  componentNames: string[],
71
71
  _context?: string,
72
72
  options?: { graph?: ComponentGraph },
73
73
  ): CompositionAnalysis {
74
- const allNames = new Set(Object.keys(segments));
74
+ const allNames = new Set(Object.keys(fragments));
75
75
 
76
76
  // 1. Validate names
77
77
  const components: string[] = [];
@@ -93,11 +93,11 @@ export function analyzeComposition(
93
93
  const suggestedSet = new Set<string>();
94
94
 
95
95
  for (const name of components) {
96
- const segment = segments[name];
96
+ const fragment = fragments[name];
97
97
 
98
98
  // 2. Relation checks
99
- if (segment.relations) {
100
- for (const rel of segment.relations) {
99
+ if (fragment.relations) {
100
+ for (const rel of fragment.relations) {
101
101
  switch (rel.relationship) {
102
102
  case "parent":
103
103
  if (!selectedSet.has(rel.component)) {
@@ -160,8 +160,8 @@ export function analyzeComposition(
160
160
  }
161
161
 
162
162
  // 3. Usage conflict checks (whenNot)
163
- if (segment.usage?.whenNot) {
164
- for (const whenNotEntry of segment.usage.whenNot) {
163
+ if (fragment.usage?.whenNot) {
164
+ for (const whenNotEntry of fragment.usage.whenNot) {
165
165
  const lower = whenNotEntry.toLowerCase();
166
166
  for (const other of components) {
167
167
  if (other !== name && lower.includes(other.toLowerCase())) {
@@ -175,15 +175,15 @@ export function analyzeComposition(
175
175
  }
176
176
 
177
177
  // 4. Status warnings
178
- if (segment.meta.status === "deprecated") {
178
+ if (fragment.meta.status === "deprecated") {
179
179
  warnings.push({
180
180
  type: "deprecated",
181
181
  component: name,
182
- message: segment.meta.description
183
- ? `"${name}" is deprecated: ${segment.meta.description}`
182
+ message: fragment.meta.description
183
+ ? `"${name}" is deprecated: ${fragment.meta.description}`
184
184
  : `"${name}" is deprecated`,
185
185
  });
186
- } else if (segment.meta.status === "experimental") {
186
+ } else if (fragment.meta.status === "experimental") {
187
187
  warnings.push({
188
188
  type: "experimental",
189
189
  component: name,
@@ -194,7 +194,7 @@ export function analyzeComposition(
194
194
 
195
195
  // 5. Category gap analysis
196
196
  const selectedCategories = new Set(
197
- components.map((name) => segments[name].meta.category)
197
+ components.map((name) => fragments[name].meta.category)
198
198
  );
199
199
 
200
200
  for (const [category, affinities] of Object.entries(CATEGORY_AFFINITIES)) {
@@ -205,7 +205,7 @@ export function analyzeComposition(
205
205
 
206
206
  // Find the best component from the needed category
207
207
  const candidate = findBestCategoryCandidate(
208
- segments,
208
+ fragments,
209
209
  neededCategory,
210
210
  selectedSet,
211
211
  suggestedSet
@@ -216,7 +216,7 @@ export function analyzeComposition(
216
216
  reason: `Compositions using "${category}" components often benefit from a "${neededCategory}" component`,
217
217
  relationship: "category_gap",
218
218
  sourceComponent: components.find(
219
- (n) => segments[n].meta.category === category
219
+ (n) => fragments[n].meta.category === category
220
220
  )!,
221
221
  });
222
222
  suggestedSet.add(candidate);
@@ -289,7 +289,7 @@ export function analyzeComposition(
289
289
  * Prefers stable components and avoids already-selected or already-suggested ones.
290
290
  */
291
291
  function findBestCategoryCandidate(
292
- segments: Record<string, CompiledSegment>,
292
+ fragments: Record<string, CompiledFragment>,
293
293
  category: string,
294
294
  selectedSet: Set<string>,
295
295
  suggestedSet: Set<string>
@@ -297,11 +297,11 @@ function findBestCategoryCandidate(
297
297
  let best: string | null = null;
298
298
  let bestScore = -1;
299
299
 
300
- for (const [name, segment] of Object.entries(segments)) {
301
- if (segment.meta.category !== category) continue;
300
+ for (const [name, fragment] of Object.entries(fragments)) {
301
+ if (fragment.meta.category !== category) continue;
302
302
  if (selectedSet.has(name) || suggestedSet.has(name)) continue;
303
303
 
304
- const status = segment.meta.status ?? "stable";
304
+ const status = fragment.meta.status ?? "stable";
305
305
  let score = 0;
306
306
  if (status === "stable") score = 3;
307
307
  else if (status === "beta") score = 2;
@@ -2,12 +2,12 @@ import { existsSync } from 'node:fs';
2
2
  import { resolve, dirname } from 'node:path';
3
3
  import { createJiti } from 'jiti';
4
4
  import { BRAND } from './constants.js';
5
- import type { SegmentsConfig } from './types.js';
6
- import { segmentsConfigSchema } from './schema.js';
5
+ import type { FragmentsConfig } from './types.js';
6
+ import { fragmentsConfigSchema } from './schema.js';
7
7
 
8
- const DEFAULT_CONFIG: SegmentsConfig = {
8
+ const DEFAULT_CONFIG: FragmentsConfig = {
9
9
  include: [
10
- `src/**/*${BRAND.fileExtension}`, // *.segment.tsx files
10
+ `src/**/*${BRAND.fileExtension}`, // *.fragment.tsx files
11
11
  'src/**/*.stories.tsx', // Storybook stories (auto-converted)
12
12
  ],
13
13
  exclude: ['**/node_modules/**'],
@@ -43,7 +43,7 @@ export function findConfigFile(startDir: string = process.cwd()): string | null
43
43
  * Load and validate the config file
44
44
  */
45
45
  export async function loadConfig(configPath?: string): Promise<{
46
- config: SegmentsConfig;
46
+ config: FragmentsConfig;
47
47
  configDir: string;
48
48
  }> {
49
49
  const resolvedPath = configPath ?? findConfigFile();
@@ -62,7 +62,7 @@ export async function loadConfig(configPath?: string): Promise<{
62
62
  });
63
63
  const rawConfig = await jiti.import(resolvedPath);
64
64
 
65
- const result = segmentsConfigSchema.safeParse(rawConfig);
65
+ const result = fragmentsConfigSchema.safeParse(rawConfig);
66
66
 
67
67
  if (!result.success) {
68
68
  const errors = result.error.errors
@@ -1,18 +1,18 @@
1
- import type { SegmentDefinition, CompiledSegment, SegmentComponent, BlockDefinition, CompiledBlock } from './types.js';
2
- import { segmentDefinitionSchema, blockDefinitionSchema } from './schema.js';
1
+ import type { FragmentDefinition, CompiledFragment, FragmentComponent, BlockDefinition, CompiledBlock } from './types.js';
2
+ import { fragmentDefinitionSchema, blockDefinitionSchema } from './schema.js';
3
3
 
4
4
  /**
5
- * Define a segment for a component.
5
+ * Define a fragment for a component.
6
6
  *
7
- * This is the main API for creating segment documentation.
7
+ * This is the main API for creating fragment documentation.
8
8
  * It provides runtime validation and type safety.
9
9
  *
10
10
  * @example
11
11
  * ```tsx
12
- * import { defineSegment } from '@fragments/core';
12
+ * import { defineFragment } from '@fragments/core';
13
13
  * import { Button } from './Button';
14
14
  *
15
- * export default defineSegment({
15
+ * export default defineFragment({
16
16
  * component: Button,
17
17
  * meta: {
18
18
  * name: 'Button',
@@ -41,18 +41,18 @@ import { segmentDefinitionSchema, blockDefinitionSchema } from './schema.js';
41
41
  * });
42
42
  * ```
43
43
  */
44
- export function defineSegment<TProps>(
45
- definition: SegmentDefinition<TProps>
46
- ): SegmentDefinition<TProps> {
44
+ export function defineFragment<TProps>(
45
+ definition: FragmentDefinition<TProps>
46
+ ): FragmentDefinition<TProps> {
47
47
  // Validate at runtime in development
48
48
  if (process.env.NODE_ENV !== 'production') {
49
- const result = segmentDefinitionSchema.safeParse(definition);
49
+ const result = fragmentDefinitionSchema.safeParse(definition);
50
50
  if (!result.success) {
51
51
  const errors = result.error.errors
52
52
  .map((e) => ` - ${e.path.join('.')}: ${e.message}`)
53
53
  .join('\n');
54
54
  throw new Error(
55
- `Invalid segment definition for "${definition.meta?.name || 'unknown'}":\n${errors}`
55
+ `Invalid fragment definition for "${definition.meta?.name || 'unknown'}":\n${errors}`
56
56
  );
57
57
  }
58
58
  }
@@ -61,19 +61,13 @@ export function defineSegment<TProps>(
61
61
  }
62
62
 
63
63
  /**
64
- * Alias for defineSegment - use this for new projects.
65
- * @see defineSegment
66
- */
67
- export const defineFragment = defineSegment;
68
-
69
- /**
70
- * Compile a segment definition to JSON-serializable format.
64
+ * Compile a fragment definition to JSON-serializable format.
71
65
  * Used for generating fragments.json for AI consumption.
72
66
  */
73
- export function compileSegment(
74
- definition: SegmentDefinition,
67
+ export function compileFragment(
68
+ definition: FragmentDefinition,
75
69
  filePath: string
76
- ): CompiledSegment {
70
+ ): CompiledFragment {
77
71
  return {
78
72
  filePath,
79
73
  meta: definition.meta,
@@ -144,4 +138,4 @@ export const compileRecipe = compileBlock;
144
138
  /**
145
139
  * Type helper for extracting props type from a component
146
140
  */
147
- export type InferProps<T> = T extends SegmentComponent<infer P> ? P : never;
141
+ export type InferProps<T> = T extends FragmentComponent<infer P> ? P : never;
@@ -2,7 +2,7 @@ import { resolve, dirname, basename } from 'node:path';
2
2
  import { readFile } from 'node:fs/promises';
3
3
  import { existsSync } from 'node:fs';
4
4
  import fg from 'fast-glob';
5
- import type { SegmentsConfig } from './types.js';
5
+ import type { FragmentsConfig } from './types.js';
6
6
  import { BRAND } from './constants.js';
7
7
 
8
8
  export interface DiscoveredFile {
@@ -56,10 +56,10 @@ export async function discoverBlockFiles(
56
56
  export const discoverRecipeFiles = discoverBlockFiles;
57
57
 
58
58
  /**
59
- * Discover segment files matching the config patterns
59
+ * Discover fragment files matching the config patterns
60
60
  */
61
- export async function discoverSegmentFiles(
62
- config: SegmentsConfig,
61
+ export async function discoverFragmentFiles(
62
+ config: FragmentsConfig,
63
63
  configDir: string
64
64
  ): Promise<DiscoveredFile[]> {
65
65
  const files = await fg(config.include, {
@@ -78,7 +78,7 @@ export async function discoverSegmentFiles(
78
78
  * Discover component files for coverage validation
79
79
  */
80
80
  export async function discoverComponentFiles(
81
- config: SegmentsConfig,
81
+ config: FragmentsConfig,
82
82
  configDir: string
83
83
  ): Promise<DiscoveredFile[]> {
84
84
  if (!config.components || config.components.length === 0) {
@@ -89,7 +89,7 @@ export async function discoverComponentFiles(
89
89
  cwd: configDir,
90
90
  ignore: [
91
91
  ...(config.exclude ?? []),
92
- // Exclude segment files themselves
92
+ // Exclude fragment files themselves
93
93
  ...config.include,
94
94
  // Exclude test files
95
95
  '**/*.test.*',
package/src/core/figma.ts CHANGED
@@ -6,9 +6,9 @@
6
6
  *
7
7
  * @example
8
8
  * ```tsx
9
- * import { defineSegment, figma } from '@fragments/core';
9
+ * import { defineFragment, figma } from '@fragments/core';
10
10
  *
11
- * export default defineSegment({
11
+ * export default defineFragment({
12
12
  * component: Button,
13
13
  * meta: {
14
14
  * name: 'Button',