@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.
- package/LICENSE +21 -0
- package/README.md +106 -0
- package/dist/bin.d.ts +1 -0
- package/dist/bin.js +4783 -0
- package/dist/bin.js.map +1 -0
- package/dist/chunk-4FDQSGKX.js +786 -0
- package/dist/chunk-4FDQSGKX.js.map +1 -0
- package/dist/chunk-7H2MMGYG.js +369 -0
- package/dist/chunk-7H2MMGYG.js.map +1 -0
- package/dist/chunk-BSCG3IP7.js +619 -0
- package/dist/chunk-BSCG3IP7.js.map +1 -0
- package/dist/chunk-LY2CFFPY.js +898 -0
- package/dist/chunk-LY2CFFPY.js.map +1 -0
- package/dist/chunk-MUZ6CM66.js +6636 -0
- package/dist/chunk-MUZ6CM66.js.map +1 -0
- package/dist/chunk-OAENNG3G.js +1489 -0
- package/dist/chunk-OAENNG3G.js.map +1 -0
- package/dist/chunk-XHNKNI6J.js +235 -0
- package/dist/chunk-XHNKNI6J.js.map +1 -0
- package/dist/core-DWKLGY4N.js +68 -0
- package/dist/core-DWKLGY4N.js.map +1 -0
- package/dist/generate-4LQNJ7SX.js +249 -0
- package/dist/generate-4LQNJ7SX.js.map +1 -0
- package/dist/index.d.ts +775 -0
- package/dist/index.js +41 -0
- package/dist/index.js.map +1 -0
- package/dist/init-EMVI47QG.js +416 -0
- package/dist/init-EMVI47QG.js.map +1 -0
- package/dist/mcp-bin.d.ts +1 -0
- package/dist/mcp-bin.js +1117 -0
- package/dist/mcp-bin.js.map +1 -0
- package/dist/scan-4YPRF7FV.js +12 -0
- package/dist/scan-4YPRF7FV.js.map +1 -0
- package/dist/service-QSZMZJBJ.js +208 -0
- package/dist/service-QSZMZJBJ.js.map +1 -0
- package/dist/static-viewer-MIPGZ4Z7.js +12 -0
- package/dist/static-viewer-MIPGZ4Z7.js.map +1 -0
- package/dist/test-SQ5ZHXWU.js +1067 -0
- package/dist/test-SQ5ZHXWU.js.map +1 -0
- package/dist/tokens-HSGMYK64.js +173 -0
- package/dist/tokens-HSGMYK64.js.map +1 -0
- package/dist/viewer-YRF4SQE4.js +11101 -0
- package/dist/viewer-YRF4SQE4.js.map +1 -0
- package/package.json +107 -0
- package/src/ai.ts +266 -0
- package/src/analyze.ts +265 -0
- package/src/bin.ts +916 -0
- package/src/build.ts +248 -0
- package/src/commands/a11y.ts +302 -0
- package/src/commands/add.ts +313 -0
- package/src/commands/audit.ts +195 -0
- package/src/commands/baseline.ts +221 -0
- package/src/commands/build.ts +144 -0
- package/src/commands/compare.ts +337 -0
- package/src/commands/context.ts +107 -0
- package/src/commands/dev.ts +107 -0
- package/src/commands/enhance.ts +858 -0
- package/src/commands/generate.ts +391 -0
- package/src/commands/init.ts +531 -0
- package/src/commands/link/figma.ts +645 -0
- package/src/commands/link/index.ts +10 -0
- package/src/commands/link/storybook.ts +267 -0
- package/src/commands/list.ts +49 -0
- package/src/commands/metrics.ts +114 -0
- package/src/commands/reset.ts +242 -0
- package/src/commands/scan.ts +537 -0
- package/src/commands/storygen.ts +207 -0
- package/src/commands/tokens.ts +251 -0
- package/src/commands/validate.ts +93 -0
- package/src/commands/verify.ts +215 -0
- package/src/core/composition.test.ts +262 -0
- package/src/core/composition.ts +255 -0
- package/src/core/config.ts +84 -0
- package/src/core/constants.ts +111 -0
- package/src/core/context.ts +380 -0
- package/src/core/defineSegment.ts +137 -0
- package/src/core/discovery.ts +337 -0
- package/src/core/figma.ts +263 -0
- package/src/core/fragment-types.ts +214 -0
- package/src/core/generators/context.ts +389 -0
- package/src/core/generators/index.ts +23 -0
- package/src/core/generators/registry.ts +364 -0
- package/src/core/generators/typescript-extractor.ts +374 -0
- package/src/core/importAnalyzer.ts +217 -0
- package/src/core/index.ts +149 -0
- package/src/core/loader.ts +155 -0
- package/src/core/node.ts +63 -0
- package/src/core/parser.ts +551 -0
- package/src/core/previewLoader.ts +172 -0
- package/src/core/schema/fragment.schema.json +189 -0
- package/src/core/schema/registry.schema.json +137 -0
- package/src/core/schema.ts +182 -0
- package/src/core/storyAdapter.test.ts +571 -0
- package/src/core/storyAdapter.ts +761 -0
- package/src/core/token-types.ts +287 -0
- package/src/core/types.ts +754 -0
- package/src/diff.ts +323 -0
- package/src/index.ts +43 -0
- package/src/mcp/__tests__/projectFields.test.ts +130 -0
- package/src/mcp/bin.ts +36 -0
- package/src/mcp/index.ts +8 -0
- package/src/mcp/server.ts +1310 -0
- package/src/mcp/utils.ts +54 -0
- package/src/mcp-bin.ts +36 -0
- package/src/migrate/__tests__/argTypes/argTypes.test.ts +189 -0
- package/src/migrate/__tests__/args/args.test.ts +452 -0
- package/src/migrate/__tests__/meta/meta.test.ts +198 -0
- package/src/migrate/__tests__/stories/stories.test.ts +278 -0
- package/src/migrate/__tests__/utils/utils.test.ts +371 -0
- package/src/migrate/__tests__/values/values.test.ts +303 -0
- package/src/migrate/bin.ts +108 -0
- package/src/migrate/converter.ts +658 -0
- package/src/migrate/detect.ts +196 -0
- package/src/migrate/index.ts +45 -0
- package/src/migrate/migrate.ts +163 -0
- package/src/migrate/parser.ts +1136 -0
- package/src/migrate/report.ts +624 -0
- package/src/migrate/types.ts +169 -0
- package/src/screenshot.ts +249 -0
- package/src/service/__tests__/ast-utils.test.ts +426 -0
- package/src/service/__tests__/enhance-scanner.test.ts +200 -0
- package/src/service/__tests__/figma/figma.test.ts +652 -0
- package/src/service/__tests__/metrics-store.test.ts +409 -0
- package/src/service/__tests__/patch-generator.test.ts +186 -0
- package/src/service/__tests__/props-extractor.test.ts +365 -0
- package/src/service/__tests__/token-registry.test.ts +267 -0
- package/src/service/analytics.ts +659 -0
- package/src/service/ast-utils.ts +444 -0
- package/src/service/browser-pool.ts +339 -0
- package/src/service/capture.ts +267 -0
- package/src/service/diff.ts +279 -0
- package/src/service/enhance/aggregator.ts +489 -0
- package/src/service/enhance/cache.ts +275 -0
- package/src/service/enhance/codebase-scanner.ts +357 -0
- package/src/service/enhance/context-generator.ts +529 -0
- package/src/service/enhance/doc-extractor.ts +523 -0
- package/src/service/enhance/index.ts +131 -0
- package/src/service/enhance/props-extractor.ts +665 -0
- package/src/service/enhance/scanner.ts +445 -0
- package/src/service/enhance/storybook-parser.ts +552 -0
- package/src/service/enhance/types.ts +346 -0
- package/src/service/enhance/variant-renderer.ts +479 -0
- package/src/service/figma.ts +1008 -0
- package/src/service/index.ts +249 -0
- package/src/service/metrics-store.ts +333 -0
- package/src/service/patch-generator.ts +349 -0
- package/src/service/report.ts +854 -0
- package/src/service/storage.ts +401 -0
- package/src/service/token-fixes.ts +281 -0
- package/src/service/token-parser.ts +504 -0
- package/src/service/token-registry.ts +721 -0
- package/src/service/utils.ts +172 -0
- package/src/setup.ts +241 -0
- package/src/shared/command-wrapper.ts +81 -0
- package/src/shared/dev-server-client.ts +199 -0
- package/src/shared/index.ts +8 -0
- package/src/shared/segment-loader.ts +59 -0
- package/src/shared/types.ts +147 -0
- package/src/static-viewer.ts +715 -0
- package/src/test/discovery.ts +172 -0
- package/src/test/index.ts +281 -0
- package/src/test/reporters/console.ts +194 -0
- package/src/test/reporters/json.ts +190 -0
- package/src/test/reporters/junit.ts +186 -0
- package/src/test/runner.ts +598 -0
- package/src/test/types.ts +245 -0
- package/src/test/watch.ts +200 -0
- package/src/validators.ts +152 -0
- package/src/viewer/__tests__/jsx-parser.test.ts +502 -0
- package/src/viewer/__tests__/render-utils.test.ts +232 -0
- package/src/viewer/__tests__/style-utils.test.ts +404 -0
- package/src/viewer/bin.ts +86 -0
- package/src/viewer/cli/health.ts +256 -0
- package/src/viewer/cli/index.ts +33 -0
- package/src/viewer/cli/scan.ts +124 -0
- package/src/viewer/cli/utils.ts +174 -0
- package/src/viewer/components/AccessibilityPanel.tsx +1404 -0
- package/src/viewer/components/ActionCapture.tsx +172 -0
- package/src/viewer/components/ActionsPanel.tsx +371 -0
- package/src/viewer/components/App.tsx +638 -0
- package/src/viewer/components/BottomPanel.tsx +224 -0
- package/src/viewer/components/CodePanel.tsx +589 -0
- package/src/viewer/components/CommandPalette.tsx +336 -0
- package/src/viewer/components/ComponentGraph.tsx +394 -0
- package/src/viewer/components/ComponentHeader.tsx +85 -0
- package/src/viewer/components/ContractPanel.tsx +234 -0
- package/src/viewer/components/ErrorBoundary.tsx +85 -0
- package/src/viewer/components/FigmaEmbed.tsx +231 -0
- package/src/viewer/components/FragmentEditor.tsx +485 -0
- package/src/viewer/components/HealthDashboard.tsx +452 -0
- package/src/viewer/components/HmrStatusIndicator.tsx +71 -0
- package/src/viewer/components/Icons.tsx +417 -0
- package/src/viewer/components/InteractionsPanel.tsx +720 -0
- package/src/viewer/components/IsolatedPreviewFrame.tsx +321 -0
- package/src/viewer/components/IsolatedRender.tsx +111 -0
- package/src/viewer/components/KeyboardShortcutsHelp.tsx +89 -0
- package/src/viewer/components/LandingPage.tsx +441 -0
- package/src/viewer/components/Layout.tsx +22 -0
- package/src/viewer/components/LeftSidebar.tsx +391 -0
- package/src/viewer/components/MultiViewportPreview.tsx +429 -0
- package/src/viewer/components/PreviewArea.tsx +404 -0
- package/src/viewer/components/PreviewFrameHost.tsx +310 -0
- package/src/viewer/components/PreviewPane.tsx +150 -0
- package/src/viewer/components/PreviewToolbar.tsx +176 -0
- package/src/viewer/components/PropsEditor.tsx +512 -0
- package/src/viewer/components/PropsTable.tsx +98 -0
- package/src/viewer/components/RelationsSection.tsx +57 -0
- package/src/viewer/components/ResizablePanel.tsx +328 -0
- package/src/viewer/components/RightSidebar.tsx +118 -0
- package/src/viewer/components/ScreenshotButton.tsx +90 -0
- package/src/viewer/components/Sidebar.tsx +169 -0
- package/src/viewer/components/SkeletonLoader.tsx +156 -0
- package/src/viewer/components/StoryRenderer.tsx +128 -0
- package/src/viewer/components/ThemeProvider.tsx +96 -0
- package/src/viewer/components/Toast.tsx +67 -0
- package/src/viewer/components/TokenStylePanel.tsx +708 -0
- package/src/viewer/components/UsageSection.tsx +95 -0
- package/src/viewer/components/VariantMatrix.tsx +350 -0
- package/src/viewer/components/VariantRenderer.tsx +131 -0
- package/src/viewer/components/VariantTabs.tsx +84 -0
- package/src/viewer/components/ViewportSelector.tsx +165 -0
- package/src/viewer/components/_future/CreatePage.tsx +836 -0
- package/src/viewer/composition-renderer.ts +381 -0
- package/src/viewer/constants/index.ts +1 -0
- package/src/viewer/constants/ui.ts +185 -0
- package/src/viewer/entry.tsx +299 -0
- package/src/viewer/hooks/index.ts +2 -0
- package/src/viewer/hooks/useA11yCache.ts +383 -0
- package/src/viewer/hooks/useA11yService.ts +498 -0
- package/src/viewer/hooks/useActions.ts +138 -0
- package/src/viewer/hooks/useAppState.ts +124 -0
- package/src/viewer/hooks/useFigmaIntegration.ts +132 -0
- package/src/viewer/hooks/useHmrStatus.ts +109 -0
- package/src/viewer/hooks/useKeyboardShortcuts.ts +222 -0
- package/src/viewer/hooks/usePreviewBridge.ts +347 -0
- package/src/viewer/hooks/useScrollSpy.ts +78 -0
- package/src/viewer/hooks/useUrlState.ts +330 -0
- package/src/viewer/hooks/useViewSettings.ts +125 -0
- package/src/viewer/index.html +28 -0
- package/src/viewer/index.ts +14 -0
- package/src/viewer/intelligence/healthReport.ts +505 -0
- package/src/viewer/intelligence/styleDrift.ts +340 -0
- package/src/viewer/intelligence/usageScanner.ts +309 -0
- package/src/viewer/jsx-parser.ts +485 -0
- package/src/viewer/postcss.config.js +6 -0
- package/src/viewer/preview-frame-entry.tsx +25 -0
- package/src/viewer/preview-frame.html +109 -0
- package/src/viewer/render-template.html +68 -0
- package/src/viewer/render-utils.ts +170 -0
- package/src/viewer/server.ts +276 -0
- package/src/viewer/style-utils.ts +414 -0
- package/src/viewer/styles/globals.css +355 -0
- package/src/viewer/tailwind.config.js +37 -0
- package/src/viewer/types/a11y.ts +197 -0
- package/src/viewer/utils/a11y-fixes.ts +471 -0
- package/src/viewer/utils/actionExport.ts +372 -0
- package/src/viewer/utils/colorSchemes.ts +201 -0
- package/src/viewer/utils/detectRelationships.ts +256 -0
- package/src/viewer/vite-plugin.ts +2143 -0
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
import type { CompiledSegment, CompiledRecipe, PropDefinition } from "./types.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Placeholder patterns to filter out from usage text.
|
|
5
|
+
* These are generated by the migrate tool and provide no value.
|
|
6
|
+
*/
|
|
7
|
+
const PLACEHOLDER_PATTERNS = [
|
|
8
|
+
/^\w+ component is needed$/i,
|
|
9
|
+
/^Alternative component is more appropriate$/i,
|
|
10
|
+
/^Use \w+ when you need/i,
|
|
11
|
+
];
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Filter out placeholder text from usage arrays
|
|
15
|
+
*/
|
|
16
|
+
function filterPlaceholders(items: string[]): string[] {
|
|
17
|
+
return items.filter(item =>
|
|
18
|
+
!PLACEHOLDER_PATTERNS.some(pattern => pattern.test(item.trim()))
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Options for context generation
|
|
24
|
+
*/
|
|
25
|
+
export interface ContextOptions {
|
|
26
|
+
/** Output format */
|
|
27
|
+
format?: "markdown" | "json";
|
|
28
|
+
|
|
29
|
+
/** What to include in the output */
|
|
30
|
+
include?: {
|
|
31
|
+
/** Include prop details (default: true) */
|
|
32
|
+
props?: boolean;
|
|
33
|
+
/** Include variant list (default: true) */
|
|
34
|
+
variants?: boolean;
|
|
35
|
+
/** Include usage guidelines (default: true) */
|
|
36
|
+
usage?: boolean;
|
|
37
|
+
/** Include related components (default: false) */
|
|
38
|
+
relations?: boolean;
|
|
39
|
+
/** Include code examples (default: false) */
|
|
40
|
+
code?: boolean;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
/** Compact mode - minimal output for token efficiency */
|
|
44
|
+
compact?: boolean;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Result of context generation
|
|
49
|
+
*/
|
|
50
|
+
export interface ContextResult {
|
|
51
|
+
/** The generated context content */
|
|
52
|
+
content: string;
|
|
53
|
+
/** Estimated token count */
|
|
54
|
+
tokenEstimate: number;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Generate AI-ready context from compiled segments and optional recipes
|
|
59
|
+
*/
|
|
60
|
+
export function generateContext(
|
|
61
|
+
segments: CompiledSegment[],
|
|
62
|
+
options: ContextOptions = {},
|
|
63
|
+
recipes?: CompiledRecipe[]
|
|
64
|
+
): ContextResult {
|
|
65
|
+
const format = options.format ?? "markdown";
|
|
66
|
+
const compact = options.compact ?? false;
|
|
67
|
+
|
|
68
|
+
const include = {
|
|
69
|
+
props: options.include?.props ?? true,
|
|
70
|
+
variants: options.include?.variants ?? true,
|
|
71
|
+
usage: options.include?.usage ?? true,
|
|
72
|
+
relations: options.include?.relations ?? false,
|
|
73
|
+
code: options.include?.code ?? false,
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
// Sort segments by category, then name
|
|
77
|
+
const sorted = [...segments].sort((a, b) => {
|
|
78
|
+
const catCompare = a.meta.category.localeCompare(b.meta.category);
|
|
79
|
+
if (catCompare !== 0) return catCompare;
|
|
80
|
+
return a.meta.name.localeCompare(b.meta.name);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
if (format === "json") {
|
|
84
|
+
return generateJsonContext(sorted, include, compact, recipes);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return generateMarkdownContext(sorted, include, compact, recipes);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Generate markdown context
|
|
92
|
+
*/
|
|
93
|
+
function generateMarkdownContext(
|
|
94
|
+
segments: CompiledSegment[],
|
|
95
|
+
include: Required<NonNullable<ContextOptions["include"]>>,
|
|
96
|
+
compact: boolean,
|
|
97
|
+
recipes?: CompiledRecipe[]
|
|
98
|
+
): ContextResult {
|
|
99
|
+
const lines: string[] = [];
|
|
100
|
+
|
|
101
|
+
lines.push("# Design System Reference");
|
|
102
|
+
lines.push("");
|
|
103
|
+
|
|
104
|
+
// Quick reference table
|
|
105
|
+
lines.push("## Quick Reference");
|
|
106
|
+
lines.push("");
|
|
107
|
+
lines.push("| Component | Category | Use For |");
|
|
108
|
+
lines.push("|-----------|----------|---------|");
|
|
109
|
+
|
|
110
|
+
for (const segment of segments) {
|
|
111
|
+
const filteredWhen = filterPlaceholders(segment.usage.when);
|
|
112
|
+
const useFor = filteredWhen.slice(0, 2).join(", ") || segment.meta.description;
|
|
113
|
+
lines.push(`| ${segment.meta.name} | ${segment.meta.category} | ${truncate(useFor, 50)} |`);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
lines.push("");
|
|
117
|
+
|
|
118
|
+
// If compact mode, stop here
|
|
119
|
+
if (compact) {
|
|
120
|
+
const content = lines.join("\n");
|
|
121
|
+
return {
|
|
122
|
+
content,
|
|
123
|
+
tokenEstimate: estimateTokens(content),
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Full component documentation
|
|
128
|
+
lines.push("## Components");
|
|
129
|
+
lines.push("");
|
|
130
|
+
|
|
131
|
+
for (const segment of segments) {
|
|
132
|
+
lines.push(`### ${segment.meta.name}`);
|
|
133
|
+
lines.push("");
|
|
134
|
+
|
|
135
|
+
// Status line
|
|
136
|
+
const statusParts = [`**Category:** ${segment.meta.category}`];
|
|
137
|
+
if (segment.meta.status) {
|
|
138
|
+
statusParts.push(`**Status:** ${segment.meta.status}`);
|
|
139
|
+
}
|
|
140
|
+
lines.push(statusParts.join(" | "));
|
|
141
|
+
lines.push("");
|
|
142
|
+
|
|
143
|
+
if (segment.meta.description) {
|
|
144
|
+
lines.push(segment.meta.description);
|
|
145
|
+
lines.push("");
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Usage guidelines (filter out placeholder text)
|
|
149
|
+
const whenFiltered = filterPlaceholders(segment.usage.when);
|
|
150
|
+
const whenNotFiltered = filterPlaceholders(segment.usage.whenNot);
|
|
151
|
+
|
|
152
|
+
if (include.usage && (whenFiltered.length > 0 || whenNotFiltered.length > 0)) {
|
|
153
|
+
if (whenFiltered.length > 0) {
|
|
154
|
+
lines.push("**When to use:**");
|
|
155
|
+
for (const when of whenFiltered) {
|
|
156
|
+
lines.push(`- ${when}`);
|
|
157
|
+
}
|
|
158
|
+
lines.push("");
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (whenNotFiltered.length > 0) {
|
|
162
|
+
lines.push("**When NOT to use:**");
|
|
163
|
+
for (const whenNot of whenNotFiltered) {
|
|
164
|
+
lines.push(`- ${whenNot}`);
|
|
165
|
+
}
|
|
166
|
+
lines.push("");
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Props
|
|
171
|
+
if (include.props && Object.keys(segment.props).length > 0) {
|
|
172
|
+
lines.push("**Props:**");
|
|
173
|
+
for (const [name, prop] of Object.entries(segment.props)) {
|
|
174
|
+
lines.push(`- \`${name}\`: ${formatPropType(prop)}${prop.required ? " (required)" : ""}`);
|
|
175
|
+
}
|
|
176
|
+
lines.push("");
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Variants
|
|
180
|
+
if (include.variants && segment.variants.length > 0) {
|
|
181
|
+
const variantNames = segment.variants.map((v) => v.name).join(", ");
|
|
182
|
+
lines.push(`**Variants:** ${variantNames}`);
|
|
183
|
+
lines.push("");
|
|
184
|
+
|
|
185
|
+
// Code examples
|
|
186
|
+
if (include.code) {
|
|
187
|
+
for (const variant of segment.variants) {
|
|
188
|
+
if (variant.code) {
|
|
189
|
+
lines.push(`*${variant.name}:*`);
|
|
190
|
+
lines.push("```tsx");
|
|
191
|
+
lines.push(variant.code);
|
|
192
|
+
lines.push("```");
|
|
193
|
+
lines.push("");
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Relations
|
|
200
|
+
if (include.relations && segment.relations && segment.relations.length > 0) {
|
|
201
|
+
lines.push("**Related:**");
|
|
202
|
+
for (const relation of segment.relations) {
|
|
203
|
+
lines.push(`- ${relation.component} (${relation.relationship}): ${relation.note}`);
|
|
204
|
+
}
|
|
205
|
+
lines.push("");
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
lines.push("---");
|
|
209
|
+
lines.push("");
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Recipes section
|
|
213
|
+
if (recipes && recipes.length > 0) {
|
|
214
|
+
lines.push("## Recipes");
|
|
215
|
+
lines.push("");
|
|
216
|
+
lines.push("Composition patterns showing how components wire together.");
|
|
217
|
+
lines.push("");
|
|
218
|
+
|
|
219
|
+
for (const recipe of recipes) {
|
|
220
|
+
lines.push(`### ${recipe.name}`);
|
|
221
|
+
lines.push("");
|
|
222
|
+
lines.push(recipe.description);
|
|
223
|
+
lines.push("");
|
|
224
|
+
lines.push(`**Category:** ${recipe.category}`);
|
|
225
|
+
lines.push(`**Components:** ${recipe.components.join(", ")}`);
|
|
226
|
+
if (recipe.tags && recipe.tags.length > 0) {
|
|
227
|
+
lines.push(`**Tags:** ${recipe.tags.join(", ")}`);
|
|
228
|
+
}
|
|
229
|
+
lines.push("");
|
|
230
|
+
lines.push("```tsx");
|
|
231
|
+
lines.push(recipe.code);
|
|
232
|
+
lines.push("```");
|
|
233
|
+
lines.push("");
|
|
234
|
+
lines.push("---");
|
|
235
|
+
lines.push("");
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const content = lines.join("\n");
|
|
240
|
+
return {
|
|
241
|
+
content,
|
|
242
|
+
tokenEstimate: estimateTokens(content),
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Generate JSON context
|
|
248
|
+
*/
|
|
249
|
+
function generateJsonContext(
|
|
250
|
+
segments: CompiledSegment[],
|
|
251
|
+
include: Required<NonNullable<ContextOptions["include"]>>,
|
|
252
|
+
compact: boolean,
|
|
253
|
+
recipes?: CompiledRecipe[]
|
|
254
|
+
): ContextResult {
|
|
255
|
+
const categories = [...new Set(segments.map((s) => s.meta.category))].sort();
|
|
256
|
+
|
|
257
|
+
interface JsonComponent {
|
|
258
|
+
category: string;
|
|
259
|
+
description: string;
|
|
260
|
+
status?: string;
|
|
261
|
+
whenToUse?: string[];
|
|
262
|
+
whenNotToUse?: string[];
|
|
263
|
+
props?: Record<string, { type: string; required?: boolean; default?: unknown; description: string }>;
|
|
264
|
+
variants?: string[];
|
|
265
|
+
relations?: Array<{ component: string; relationship: string; note: string }>;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
const components: Record<string, JsonComponent> = {};
|
|
269
|
+
|
|
270
|
+
for (const segment of segments) {
|
|
271
|
+
const component: JsonComponent = {
|
|
272
|
+
category: segment.meta.category,
|
|
273
|
+
description: segment.meta.description,
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
if (segment.meta.status) {
|
|
277
|
+
component.status = segment.meta.status;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
if (!compact) {
|
|
281
|
+
if (include.usage) {
|
|
282
|
+
const whenFiltered = filterPlaceholders(segment.usage.when);
|
|
283
|
+
const whenNotFiltered = filterPlaceholders(segment.usage.whenNot);
|
|
284
|
+
if (whenFiltered.length > 0) {
|
|
285
|
+
component.whenToUse = whenFiltered;
|
|
286
|
+
}
|
|
287
|
+
if (whenNotFiltered.length > 0) {
|
|
288
|
+
component.whenNotToUse = whenNotFiltered;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
if (include.props && Object.keys(segment.props).length > 0) {
|
|
293
|
+
component.props = {};
|
|
294
|
+
for (const [name, prop] of Object.entries(segment.props)) {
|
|
295
|
+
component.props[name] = {
|
|
296
|
+
type: formatPropType(prop),
|
|
297
|
+
description: prop.description,
|
|
298
|
+
};
|
|
299
|
+
if (prop.required) {
|
|
300
|
+
component.props[name].required = true;
|
|
301
|
+
}
|
|
302
|
+
if (prop.default !== undefined) {
|
|
303
|
+
component.props[name].default = prop.default;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
if (include.variants && segment.variants.length > 0) {
|
|
309
|
+
component.variants = segment.variants.map((v) => v.name);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
if (include.relations && segment.relations && segment.relations.length > 0) {
|
|
313
|
+
component.relations = segment.relations.map((r) => ({
|
|
314
|
+
component: r.component,
|
|
315
|
+
relationship: r.relationship,
|
|
316
|
+
note: r.note,
|
|
317
|
+
}));
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
components[segment.meta.name] = component;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// Build recipes map
|
|
325
|
+
const recipesMap = recipes && recipes.length > 0
|
|
326
|
+
? Object.fromEntries(recipes.map(r => [r.name, {
|
|
327
|
+
description: r.description,
|
|
328
|
+
category: r.category,
|
|
329
|
+
components: r.components,
|
|
330
|
+
code: r.code,
|
|
331
|
+
tags: r.tags,
|
|
332
|
+
}]))
|
|
333
|
+
: undefined;
|
|
334
|
+
|
|
335
|
+
const output = {
|
|
336
|
+
version: "1.0",
|
|
337
|
+
generatedAt: new Date().toISOString(),
|
|
338
|
+
summary: {
|
|
339
|
+
totalComponents: segments.length,
|
|
340
|
+
categories,
|
|
341
|
+
...(recipesMap && { totalRecipes: recipes!.length }),
|
|
342
|
+
},
|
|
343
|
+
components,
|
|
344
|
+
...(recipesMap && { recipes: recipesMap }),
|
|
345
|
+
};
|
|
346
|
+
|
|
347
|
+
const content = JSON.stringify(output, null, 2);
|
|
348
|
+
return {
|
|
349
|
+
content,
|
|
350
|
+
tokenEstimate: estimateTokens(content),
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* Format a prop type for display
|
|
356
|
+
*/
|
|
357
|
+
function formatPropType(prop: PropDefinition): string {
|
|
358
|
+
if (prop.type === "enum" && prop.values) {
|
|
359
|
+
return prop.values.map((v) => `"${v}"`).join(" | ");
|
|
360
|
+
}
|
|
361
|
+
if (prop.default !== undefined) {
|
|
362
|
+
return `${prop.type} (default: ${JSON.stringify(prop.default)})`;
|
|
363
|
+
}
|
|
364
|
+
return prop.type;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* Truncate string to max length
|
|
369
|
+
*/
|
|
370
|
+
function truncate(str: string, maxLength: number): string {
|
|
371
|
+
if (str.length <= maxLength) return str;
|
|
372
|
+
return str.slice(0, maxLength - 3) + "...";
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
/**
|
|
376
|
+
* Estimate token count (rough approximation: ~4 chars per token)
|
|
377
|
+
*/
|
|
378
|
+
function estimateTokens(text: string): number {
|
|
379
|
+
return Math.ceil(text.length / 4);
|
|
380
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import type { SegmentDefinition, CompiledSegment, SegmentComponent, RecipeDefinition, CompiledRecipe } from './types.js';
|
|
2
|
+
import { segmentDefinitionSchema, recipeDefinitionSchema } from './schema.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Define a segment for a component.
|
|
6
|
+
*
|
|
7
|
+
* This is the main API for creating segment documentation.
|
|
8
|
+
* It provides runtime validation and type safety.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```tsx
|
|
12
|
+
* import { defineSegment } from '@fragments/core';
|
|
13
|
+
* import { Button } from './Button';
|
|
14
|
+
*
|
|
15
|
+
* export default defineSegment({
|
|
16
|
+
* component: Button,
|
|
17
|
+
* meta: {
|
|
18
|
+
* name: 'Button',
|
|
19
|
+
* description: 'Primary action trigger',
|
|
20
|
+
* category: 'actions',
|
|
21
|
+
* },
|
|
22
|
+
* usage: {
|
|
23
|
+
* when: ['User needs to trigger an action'],
|
|
24
|
+
* whenNot: ['Navigation without side effects'],
|
|
25
|
+
* },
|
|
26
|
+
* props: {
|
|
27
|
+
* variant: {
|
|
28
|
+
* type: 'enum',
|
|
29
|
+
* values: ['primary', 'secondary'],
|
|
30
|
+
* default: 'primary',
|
|
31
|
+
* description: 'Visual style',
|
|
32
|
+
* },
|
|
33
|
+
* },
|
|
34
|
+
* variants: [
|
|
35
|
+
* {
|
|
36
|
+
* name: 'Default',
|
|
37
|
+
* description: 'Default button',
|
|
38
|
+
* render: () => <Button>Click me</Button>,
|
|
39
|
+
* },
|
|
40
|
+
* ],
|
|
41
|
+
* });
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
export function defineSegment<TProps>(
|
|
45
|
+
definition: SegmentDefinition<TProps>
|
|
46
|
+
): SegmentDefinition<TProps> {
|
|
47
|
+
// Validate at runtime in development
|
|
48
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
49
|
+
const result = segmentDefinitionSchema.safeParse(definition);
|
|
50
|
+
if (!result.success) {
|
|
51
|
+
const errors = result.error.errors
|
|
52
|
+
.map((e) => ` - ${e.path.join('.')}: ${e.message}`)
|
|
53
|
+
.join('\n');
|
|
54
|
+
throw new Error(
|
|
55
|
+
`Invalid segment definition for "${definition.meta?.name || 'unknown'}":\n${errors}`
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return definition;
|
|
61
|
+
}
|
|
62
|
+
|
|
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.
|
|
71
|
+
* Used for generating fragments.json for AI consumption.
|
|
72
|
+
*/
|
|
73
|
+
export function compileSegment(
|
|
74
|
+
definition: SegmentDefinition,
|
|
75
|
+
filePath: string
|
|
76
|
+
): CompiledSegment {
|
|
77
|
+
return {
|
|
78
|
+
filePath,
|
|
79
|
+
meta: definition.meta,
|
|
80
|
+
usage: definition.usage,
|
|
81
|
+
props: definition.props,
|
|
82
|
+
relations: definition.relations,
|
|
83
|
+
variants: definition.variants.map((v) => ({
|
|
84
|
+
name: v.name,
|
|
85
|
+
description: v.description,
|
|
86
|
+
code: v.code,
|
|
87
|
+
figma: v.figma,
|
|
88
|
+
})),
|
|
89
|
+
contract: definition.contract,
|
|
90
|
+
_generated: definition._generated,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Define a composition recipe.
|
|
96
|
+
*
|
|
97
|
+
* Recipes are pure data describing how design system components
|
|
98
|
+
* wire together for common use cases.
|
|
99
|
+
*/
|
|
100
|
+
export function defineRecipe(definition: RecipeDefinition): RecipeDefinition {
|
|
101
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
102
|
+
const result = recipeDefinitionSchema.safeParse(definition);
|
|
103
|
+
if (!result.success) {
|
|
104
|
+
const errors = result.error.errors
|
|
105
|
+
.map((e) => ` - ${e.path.join('.')}: ${e.message}`)
|
|
106
|
+
.join('\n');
|
|
107
|
+
throw new Error(
|
|
108
|
+
`Invalid recipe definition for "${definition.name || 'unknown'}":\n${errors}`
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return definition;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Compile a recipe definition to JSON-serializable format.
|
|
118
|
+
*/
|
|
119
|
+
export function compileRecipe(
|
|
120
|
+
definition: RecipeDefinition,
|
|
121
|
+
filePath: string
|
|
122
|
+
): CompiledRecipe {
|
|
123
|
+
return {
|
|
124
|
+
filePath,
|
|
125
|
+
name: definition.name,
|
|
126
|
+
description: definition.description,
|
|
127
|
+
category: definition.category,
|
|
128
|
+
components: definition.components,
|
|
129
|
+
code: definition.code,
|
|
130
|
+
tags: definition.tags,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Type helper for extracting props type from a component
|
|
136
|
+
*/
|
|
137
|
+
export type InferProps<T> = T extends SegmentComponent<infer P> ? P : never;
|