@fragments-sdk/cli 0.5.0 → 0.5.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/dist/bin.js +7 -7
- package/dist/{chunk-UXRGD3DM.js → chunk-U4GQ2JTD.js} +2 -2
- package/dist/{chunk-B2TQKOLW.js → chunk-V7YLRR4C.js} +2 -238
- package/dist/chunk-V7YLRR4C.js.map +1 -0
- package/dist/{core-NJVKKLJ4.js → core-DKHB7FYV.js} +2 -2
- package/dist/{generate-OVGMDKCJ.js → generate-KL24VZVD.js} +2 -2
- package/dist/index.d.ts +2 -285
- package/dist/index.js +2 -2
- package/dist/{init-EOA7TTOR.js → init-NION5S3M.js} +5 -5
- package/dist/init-NION5S3M.js.map +1 -0
- package/dist/mcp-bin.js +26 -3
- package/dist/mcp-bin.js.map +1 -1
- package/dist/{scan-YN4LUDKY.js → scan-ESEXV7LF.js} +2 -2
- package/dist/{service-2T26CBWE.js → service-RWUMZ3EW.js} +2 -2
- package/dist/{static-viewer-CLJJRYHK.js → static-viewer-O37MJ5B6.js} +2 -2
- package/dist/{tokens-FHA2DO22.js → tokens-ITADYVPF.js} +2 -2
- package/dist/{viewer-XDPD52L7.js → viewer-YDGFDTK5.js} +11 -11
- package/package.json +3 -2
- package/src/commands/init.ts +2 -2
- package/src/core/context.ts +2 -380
- package/src/core/types.ts +11 -101
- package/src/mcp/__tests__/findFragmentsJson.test.ts +30 -0
- package/src/mcp/server.ts +40 -1
- package/dist/chunk-B2TQKOLW.js.map +0 -1
- package/dist/init-EOA7TTOR.js.map +0 -1
- /package/dist/{chunk-UXRGD3DM.js.map → chunk-U4GQ2JTD.js.map} +0 -0
- /package/dist/{core-NJVKKLJ4.js.map → core-DKHB7FYV.js.map} +0 -0
- /package/dist/{generate-OVGMDKCJ.js.map → generate-KL24VZVD.js.map} +0 -0
- /package/dist/{scan-YN4LUDKY.js.map → scan-ESEXV7LF.js.map} +0 -0
- /package/dist/{service-2T26CBWE.js.map → service-RWUMZ3EW.js.map} +0 -0
- /package/dist/{static-viewer-CLJJRYHK.js.map → static-viewer-O37MJ5B6.js.map} +0 -0
- /package/dist/{tokens-FHA2DO22.js.map → tokens-ITADYVPF.js.map} +0 -0
- /package/dist/{viewer-XDPD52L7.js.map → viewer-YDGFDTK5.js.map} +0 -0
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
} from "./chunk-2H2JAA3U.js";
|
|
10
10
|
import {
|
|
11
11
|
generateContext
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-V7YLRR4C.js";
|
|
13
13
|
import {
|
|
14
14
|
BRAND
|
|
15
15
|
} from "./chunk-ICAIQ57V.js";
|
|
@@ -242,7 +242,7 @@ var sharedRenderPool = null;
|
|
|
242
242
|
var browserPoolModule = null;
|
|
243
243
|
async function getSharedRenderPool() {
|
|
244
244
|
if (!browserPoolModule) {
|
|
245
|
-
browserPoolModule = await import("./service-
|
|
245
|
+
browserPoolModule = await import("./service-RWUMZ3EW.js");
|
|
246
246
|
}
|
|
247
247
|
if (!sharedRenderPool) {
|
|
248
248
|
sharedRenderPool = new browserPoolModule.BrowserPool({
|
|
@@ -471,7 +471,7 @@ function segmentsPlugin(options) {
|
|
|
471
471
|
const address = _server.httpServer?.address();
|
|
472
472
|
const port = typeof address === "object" && address ? address.port : 6006;
|
|
473
473
|
const renderViewport = viewport || { width: 800, height: 600 };
|
|
474
|
-
const { FigmaClient, bufferToBase64Url } = await import("./service-
|
|
474
|
+
const { FigmaClient, bufferToBase64Url } = await import("./service-RWUMZ3EW.js");
|
|
475
475
|
const figmaClient = new FigmaClient({
|
|
476
476
|
accessToken: figmaToken
|
|
477
477
|
});
|
|
@@ -562,7 +562,7 @@ function segmentsPlugin(options) {
|
|
|
562
562
|
);
|
|
563
563
|
return;
|
|
564
564
|
}
|
|
565
|
-
const { FigmaClient } = await import("./service-
|
|
565
|
+
const { FigmaClient } = await import("./service-RWUMZ3EW.js");
|
|
566
566
|
const figmaClient = new FigmaClient({ accessToken: figmaToken });
|
|
567
567
|
const { fileKey, nodeId } = figmaClient.parseUrl(figmaUrl);
|
|
568
568
|
const figmaDesignProps = await figmaClient.getNodeProperties(
|
|
@@ -603,7 +603,7 @@ function segmentsPlugin(options) {
|
|
|
603
603
|
}));
|
|
604
604
|
return;
|
|
605
605
|
}
|
|
606
|
-
const { getSharedTokenRegistry } = await import("./service-
|
|
606
|
+
const { getSharedTokenRegistry } = await import("./service-RWUMZ3EW.js");
|
|
607
607
|
const registry = getSharedTokenRegistry();
|
|
608
608
|
if (!registry.isInitialized()) {
|
|
609
609
|
await registry.initialize(config.tokens, projectRoot);
|
|
@@ -663,7 +663,7 @@ function segmentsPlugin(options) {
|
|
|
663
663
|
}));
|
|
664
664
|
return;
|
|
665
665
|
}
|
|
666
|
-
const { getSharedTokenRegistry } = await import("./service-
|
|
666
|
+
const { getSharedTokenRegistry } = await import("./service-RWUMZ3EW.js");
|
|
667
667
|
const registry = getSharedTokenRegistry();
|
|
668
668
|
if (!registry.isInitialized()) {
|
|
669
669
|
await registry.initialize(config.tokens, projectRoot);
|
|
@@ -725,7 +725,7 @@ function segmentsPlugin(options) {
|
|
|
725
725
|
res.end(JSON.stringify({ error: "Could not resolve segment file path" }));
|
|
726
726
|
return;
|
|
727
727
|
}
|
|
728
|
-
const { getSharedTokenRegistry } = await import("./service-
|
|
728
|
+
const { getSharedTokenRegistry } = await import("./service-RWUMZ3EW.js");
|
|
729
729
|
const registry = getSharedTokenRegistry();
|
|
730
730
|
if (!registry.isInitialized()) {
|
|
731
731
|
await registry.initialize(config.tokens, projectRoot);
|
|
@@ -851,7 +851,7 @@ function segmentsPlugin(options) {
|
|
|
851
851
|
}
|
|
852
852
|
const { writeFile, mkdir } = await import("fs/promises");
|
|
853
853
|
const { join: join2 } = await import("path");
|
|
854
|
-
const { BRAND: BRAND2 } = await import("./core-
|
|
854
|
+
const { BRAND: BRAND2 } = await import("./core-DKHB7FYV.js");
|
|
855
855
|
const fragmentsDir = join2(projectRoot, BRAND2.dataDir, BRAND2.componentsDir);
|
|
856
856
|
await mkdir(fragmentsDir, { recursive: true });
|
|
857
857
|
const fragmentPath = join2(
|
|
@@ -906,7 +906,7 @@ function segmentsPlugin(options) {
|
|
|
906
906
|
const {
|
|
907
907
|
getSharedTokenRegistry,
|
|
908
908
|
generateTokenPatches
|
|
909
|
-
} = await import("./service-
|
|
909
|
+
} = await import("./service-RWUMZ3EW.js");
|
|
910
910
|
const registry = getSharedTokenRegistry();
|
|
911
911
|
if (!registry.isInitialized()) {
|
|
912
912
|
await registry.initialize(config.tokens, projectRoot);
|
|
@@ -1568,7 +1568,7 @@ async function loadFullSegmentForCompare(_server, _segmentFiles, componentName,
|
|
|
1568
1568
|
}
|
|
1569
1569
|
}
|
|
1570
1570
|
async function compareImages(image1Base64, image2Base64, threshold) {
|
|
1571
|
-
const { DiffEngine, base64UrlToBuffer, bufferToBase64Url } = await import("./service-
|
|
1571
|
+
const { DiffEngine, base64UrlToBuffer, bufferToBase64Url } = await import("./service-RWUMZ3EW.js");
|
|
1572
1572
|
const { PNG } = await import("pngjs");
|
|
1573
1573
|
const buffer1 = base64UrlToBuffer(image1Base64);
|
|
1574
1574
|
const buffer2 = base64UrlToBuffer(image2Base64);
|
|
@@ -11101,4 +11101,4 @@ export {
|
|
|
11101
11101
|
segmentsPlugin,
|
|
11102
11102
|
useTheme
|
|
11103
11103
|
};
|
|
11104
|
-
//# sourceMappingURL=viewer-
|
|
11104
|
+
//# sourceMappingURL=viewer-YDGFDTK5.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fragments-sdk/cli",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.2",
|
|
4
4
|
"description": "CLI, MCP server, and dev tools for Fragments design system",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -63,7 +63,8 @@
|
|
|
63
63
|
"tailwindcss": "^3.4.17",
|
|
64
64
|
"vite": "^6.0.0",
|
|
65
65
|
"vite-plugin-svgr": "^4.5.0",
|
|
66
|
-
"zod": "^3.24.1"
|
|
66
|
+
"zod": "^3.24.1",
|
|
67
|
+
"@fragments-sdk/context": "0.2.0"
|
|
67
68
|
},
|
|
68
69
|
"devDependencies": {
|
|
69
70
|
"@types/babel__generator": "^7.6.8",
|
package/src/commands/init.ts
CHANGED
|
@@ -133,7 +133,7 @@ function generateConfig(options: {
|
|
|
133
133
|
const includeStr = options.includePaths.map((p) => ` '${p}'`).join(",\n");
|
|
134
134
|
const componentStr = options.componentPaths.map((p) => ` '${p}'`).join(",\n");
|
|
135
135
|
|
|
136
|
-
return `import type { FragmentsConfig } from '@fragments/
|
|
136
|
+
return `import type { FragmentsConfig } from '@fragments-sdk/cli';
|
|
137
137
|
|
|
138
138
|
const config: FragmentsConfig = {
|
|
139
139
|
// Glob patterns for finding fragment/story files
|
|
@@ -215,7 +215,7 @@ export function Button({
|
|
|
215
215
|
*/
|
|
216
216
|
function generateExampleFragment(): string {
|
|
217
217
|
return `import React from 'react';
|
|
218
|
-
import { defineFragment } from '@fragments/
|
|
218
|
+
import { defineFragment } from '@fragments-sdk/cli';
|
|
219
219
|
import { Button } from './Button';
|
|
220
220
|
|
|
221
221
|
export default defineFragment({
|
package/src/core/context.ts
CHANGED
|
@@ -1,380 +1,2 @@
|
|
|
1
|
-
|
|
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 blocks
|
|
59
|
-
*/
|
|
60
|
-
export function generateContext(
|
|
61
|
-
segments: CompiledSegment[],
|
|
62
|
-
options: ContextOptions = {},
|
|
63
|
-
blocks?: CompiledBlock[]
|
|
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, blocks);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
return generateMarkdownContext(sorted, include, compact, blocks);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Generate markdown context
|
|
92
|
-
*/
|
|
93
|
-
function generateMarkdownContext(
|
|
94
|
-
segments: CompiledSegment[],
|
|
95
|
-
include: Required<NonNullable<ContextOptions["include"]>>,
|
|
96
|
-
compact: boolean,
|
|
97
|
-
blocks?: CompiledBlock[]
|
|
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
|
-
// Blocks section
|
|
213
|
-
if (blocks && blocks.length > 0) {
|
|
214
|
-
lines.push("## Blocks");
|
|
215
|
-
lines.push("");
|
|
216
|
-
lines.push("Composition patterns showing how components wire together.");
|
|
217
|
-
lines.push("");
|
|
218
|
-
|
|
219
|
-
for (const block of blocks) {
|
|
220
|
-
lines.push(`### ${block.name}`);
|
|
221
|
-
lines.push("");
|
|
222
|
-
lines.push(block.description);
|
|
223
|
-
lines.push("");
|
|
224
|
-
lines.push(`**Category:** ${block.category}`);
|
|
225
|
-
lines.push(`**Components:** ${block.components.join(", ")}`);
|
|
226
|
-
if (block.tags && block.tags.length > 0) {
|
|
227
|
-
lines.push(`**Tags:** ${block.tags.join(", ")}`);
|
|
228
|
-
}
|
|
229
|
-
lines.push("");
|
|
230
|
-
lines.push("```tsx");
|
|
231
|
-
lines.push(block.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
|
-
blocks?: CompiledBlock[]
|
|
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 blocks map
|
|
325
|
-
const blocksMap = blocks && blocks.length > 0
|
|
326
|
-
? Object.fromEntries(blocks.map(b => [b.name, {
|
|
327
|
-
description: b.description,
|
|
328
|
-
category: b.category,
|
|
329
|
-
components: b.components,
|
|
330
|
-
code: b.code,
|
|
331
|
-
tags: b.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
|
-
...(blocksMap && { totalBlocks: blocks!.length }),
|
|
342
|
-
},
|
|
343
|
-
components,
|
|
344
|
-
...(blocksMap && { blocks: blocksMap }),
|
|
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
|
-
}
|
|
1
|
+
export { generateContext, filterPlaceholders, PLACEHOLDER_PATTERNS } from '@fragments-sdk/context/generate';
|
|
2
|
+
export type { ContextOptions, ContextResult } from '@fragments-sdk/context/generate';
|
package/src/core/types.ts
CHANGED
|
@@ -695,44 +695,17 @@ export interface VerifyResult {
|
|
|
695
695
|
};
|
|
696
696
|
}
|
|
697
697
|
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
meta: SegmentMeta;
|
|
707
|
-
|
|
708
|
-
/** Usage guidelines */
|
|
709
|
-
usage: SegmentUsage;
|
|
710
|
-
|
|
711
|
-
/** Props documentation (without render functions) */
|
|
712
|
-
props: Record<string, PropDefinition>;
|
|
713
|
-
|
|
714
|
-
/** Component relationships */
|
|
715
|
-
relations?: ComponentRelation[];
|
|
716
|
-
|
|
717
|
-
/** Variant names and descriptions (without render functions) */
|
|
718
|
-
variants: Array<{
|
|
719
|
-
name: string;
|
|
720
|
-
description: string;
|
|
721
|
-
code?: string;
|
|
722
|
-
figma?: string;
|
|
723
|
-
/** Args/props used to render this variant (for code generation) */
|
|
724
|
-
args?: Record<string, unknown>;
|
|
725
|
-
}>;
|
|
698
|
+
// Compiled types — re-exported from @fragments-sdk/context
|
|
699
|
+
export type {
|
|
700
|
+
CompiledSegment,
|
|
701
|
+
CompiledBlock,
|
|
702
|
+
CompiledTokenEntry,
|
|
703
|
+
CompiledTokenData,
|
|
704
|
+
CompiledSegmentsFile,
|
|
705
|
+
} from '@fragments-sdk/context/types';
|
|
726
706
|
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
/** AI-specific metadata for playground context generation */
|
|
731
|
-
ai?: AIMetadata;
|
|
732
|
-
|
|
733
|
-
/** Provenance tracking (for generated segments) */
|
|
734
|
-
_generated?: SegmentGenerated;
|
|
735
|
-
}
|
|
707
|
+
// Re-export CompiledBlock under deprecated alias
|
|
708
|
+
import type { CompiledBlock as _CompiledBlock } from '@fragments-sdk/context/types';
|
|
736
709
|
|
|
737
710
|
/**
|
|
738
711
|
* Block definition — a named composition pattern showing how
|
|
@@ -752,70 +725,7 @@ export interface BlockDefinition {
|
|
|
752
725
|
*/
|
|
753
726
|
export type RecipeDefinition = BlockDefinition;
|
|
754
727
|
|
|
755
|
-
/**
|
|
756
|
-
* Compiled block data (JSON-serializable for AI consumption)
|
|
757
|
-
*/
|
|
758
|
-
export interface CompiledBlock {
|
|
759
|
-
filePath: string;
|
|
760
|
-
name: string;
|
|
761
|
-
description: string;
|
|
762
|
-
category: string;
|
|
763
|
-
components: string[];
|
|
764
|
-
code: string;
|
|
765
|
-
tags?: string[];
|
|
766
|
-
}
|
|
767
|
-
|
|
768
728
|
/**
|
|
769
729
|
* @deprecated Use CompiledBlock instead
|
|
770
730
|
*/
|
|
771
|
-
export type CompiledRecipe =
|
|
772
|
-
|
|
773
|
-
/**
|
|
774
|
-
* A single token entry in the compiled output
|
|
775
|
-
*/
|
|
776
|
-
export interface CompiledTokenEntry {
|
|
777
|
-
/** CSS variable name (e.g., "--fui-color-accent") */
|
|
778
|
-
name: string;
|
|
779
|
-
/** Description from inline comment */
|
|
780
|
-
description?: string;
|
|
781
|
-
}
|
|
782
|
-
|
|
783
|
-
/**
|
|
784
|
-
* Compiled token data stored in fragments.json
|
|
785
|
-
*/
|
|
786
|
-
export interface CompiledTokenData {
|
|
787
|
-
/** Detected variable prefix (e.g., "--fui-") */
|
|
788
|
-
prefix: string;
|
|
789
|
-
/** Total number of tokens */
|
|
790
|
-
total: number;
|
|
791
|
-
/** Tokens grouped by category */
|
|
792
|
-
categories: Record<string, CompiledTokenEntry[]>;
|
|
793
|
-
}
|
|
794
|
-
|
|
795
|
-
/**
|
|
796
|
-
* The compiled segments.json structure
|
|
797
|
-
*/
|
|
798
|
-
export interface CompiledSegmentsFile {
|
|
799
|
-
/** Version of the schema */
|
|
800
|
-
version: string;
|
|
801
|
-
|
|
802
|
-
/** When this file was generated */
|
|
803
|
-
generatedAt: string;
|
|
804
|
-
|
|
805
|
-
/** Package name for import statements (read from package.json at build time) */
|
|
806
|
-
packageName?: string;
|
|
807
|
-
|
|
808
|
-
/** All compiled segments indexed by component name */
|
|
809
|
-
segments: Record<string, CompiledSegment>;
|
|
810
|
-
|
|
811
|
-
/** All compiled blocks indexed by block name */
|
|
812
|
-
blocks?: Record<string, CompiledBlock>;
|
|
813
|
-
|
|
814
|
-
/** Design tokens (CSS custom properties) extracted from style files */
|
|
815
|
-
tokens?: CompiledTokenData;
|
|
816
|
-
|
|
817
|
-
/**
|
|
818
|
-
* @deprecated Use blocks instead
|
|
819
|
-
*/
|
|
820
|
-
recipes?: Record<string, CompiledBlock>;
|
|
821
|
-
}
|
|
731
|
+
export type CompiledRecipe = _CompiledBlock;
|
|
@@ -306,6 +306,36 @@ describe('findFragmentsJson', () => {
|
|
|
306
306
|
});
|
|
307
307
|
});
|
|
308
308
|
|
|
309
|
+
describe('exports map fallback', () => {
|
|
310
|
+
it('finds fragments.json when package exports block ./package.json access', () => {
|
|
311
|
+
// Simulate a package with an exports map that doesn't expose ./package.json
|
|
312
|
+
// Node's createRequire.resolve('pkg/package.json') will throw ERR_PACKAGE_PATH_NOT_EXPORTED
|
|
313
|
+
// The fallback resolves the main entry and walks up to find package.json
|
|
314
|
+
const depDir = join(root, 'node_modules', 'my-ui-lib');
|
|
315
|
+
mkdirSync(depDir, { recursive: true });
|
|
316
|
+
|
|
317
|
+
writeJson(join(root, 'package.json'), {
|
|
318
|
+
dependencies: { 'my-ui-lib': '^1.0.0' },
|
|
319
|
+
});
|
|
320
|
+
writeJson(join(depDir, 'package.json'), {
|
|
321
|
+
name: 'my-ui-lib',
|
|
322
|
+
main: './index.js',
|
|
323
|
+
// exports that block ./package.json:
|
|
324
|
+
exports: {
|
|
325
|
+
'.': './index.js',
|
|
326
|
+
'./fragments.json': './fragments.json',
|
|
327
|
+
},
|
|
328
|
+
fragments: 'fragments.json',
|
|
329
|
+
});
|
|
330
|
+
writeFileSync(join(depDir, 'index.js'), 'module.exports = {}');
|
|
331
|
+
writeFileSync(join(depDir, 'fragments.json'), '{}');
|
|
332
|
+
|
|
333
|
+
const result = findFragmentsJson(root);
|
|
334
|
+
expect(result.length).toBe(1);
|
|
335
|
+
expect(result[0]).toBe(join(depDir, 'fragments.json'));
|
|
336
|
+
});
|
|
337
|
+
});
|
|
338
|
+
|
|
309
339
|
describe('Phase 3: monorepo workspace discovery', () => {
|
|
310
340
|
it('finds fragments via npm/yarn workspaces field', () => {
|
|
311
341
|
// Root has workspaces but does NOT list @my-scope/ui as a dep
|