@fragments-sdk/cli 0.4.4 → 0.5.0
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/README.md +1 -1
- package/dist/bin.js +12 -12
- package/dist/{chunk-NOTYONHY.js → chunk-2DJH4F4P.js} +2 -2
- package/dist/{chunk-5CKYLCJH.js → chunk-2H2JAA3U.js} +35 -7
- package/dist/chunk-2H2JAA3U.js.map +1 -0
- package/dist/{chunk-G3M3MPQ6.js → chunk-B2TQKOLW.js} +157 -30
- package/dist/chunk-B2TQKOLW.js.map +1 -0
- package/dist/{chunk-AW7MWOUH.js → chunk-ICAIQ57V.js} +9 -5
- package/dist/chunk-ICAIQ57V.js.map +1 -0
- package/dist/{chunk-5ZYEOHYK.js → chunk-IOJE35DZ.js} +2 -2
- package/dist/{chunk-ZFKGX3QK.js → chunk-UXRGD3DM.js} +47 -14
- package/dist/chunk-UXRGD3DM.js.map +1 -0
- package/dist/{chunk-J4SI5RIH.js → chunk-XNWDI6UT.js} +4 -4
- package/dist/{core-LNXDLXDP.js → core-NJVKKLJ4.js} +11 -3
- package/dist/{generate-OIXXHOWR.js → generate-OVGMDKCJ.js} +4 -4
- package/dist/index.d.ts +30 -4
- package/dist/index.js +6 -6
- package/dist/{init-EVPXIDW4.js → init-EOA7TTOR.js} +4 -4
- package/dist/mcp-bin.js +266 -36
- package/dist/mcp-bin.js.map +1 -1
- package/dist/scan-YN4LUDKY.js +12 -0
- package/dist/{service-K52ORLCJ.js → service-2T26CBWE.js} +4 -4
- package/dist/{static-viewer-JNQIHA4B.js → static-viewer-CLJJRYHK.js} +4 -4
- package/dist/{test-USARUEFW.js → test-ECPEXFDN.js} +3 -3
- package/dist/{tokens-C6YHBOQE.js → tokens-FHA2DO22.js} +5 -5
- package/dist/{viewer-H7TVFT4E.js → viewer-XDPD52L7.js} +13 -13
- package/package.json +1 -1
- package/src/build.ts +53 -13
- package/src/core/constants.ts +4 -1
- package/src/core/context.ts +28 -28
- package/src/core/defineSegment.ts +21 -11
- package/src/core/discovery.ts +52 -4
- package/src/core/index.ts +14 -4
- package/src/core/loader.ts +3 -0
- package/src/core/node.ts +3 -1
- package/src/core/parser.ts +1 -1
- package/src/core/schema.ts +7 -2
- package/src/core/token-parser.ts +211 -0
- package/src/core/types.ts +46 -6
- package/src/mcp/server.ts +321 -39
- package/dist/chunk-5CKYLCJH.js.map +0 -1
- package/dist/chunk-AW7MWOUH.js.map +0 -1
- package/dist/chunk-G3M3MPQ6.js.map +0 -1
- package/dist/chunk-ZFKGX3QK.js.map +0 -1
- package/dist/scan-YVYD64GD.js +0 -12
- /package/dist/{chunk-NOTYONHY.js.map → chunk-2DJH4F4P.js.map} +0 -0
- /package/dist/{chunk-5ZYEOHYK.js.map → chunk-IOJE35DZ.js.map} +0 -0
- /package/dist/{chunk-J4SI5RIH.js.map → chunk-XNWDI6UT.js.map} +0 -0
- /package/dist/{core-LNXDLXDP.js.map → core-NJVKKLJ4.js.map} +0 -0
- /package/dist/{generate-OIXXHOWR.js.map → generate-OVGMDKCJ.js.map} +0 -0
- /package/dist/{init-EVPXIDW4.js.map → init-EOA7TTOR.js.map} +0 -0
- /package/dist/{scan-YVYD64GD.js.map → scan-YN4LUDKY.js.map} +0 -0
- /package/dist/{service-K52ORLCJ.js.map → service-2T26CBWE.js.map} +0 -0
- /package/dist/{static-viewer-JNQIHA4B.js.map → static-viewer-CLJJRYHK.js.map} +0 -0
- /package/dist/{test-USARUEFW.js.map → test-ECPEXFDN.js.map} +0 -0
- /package/dist/{tokens-C6YHBOQE.js.map → tokens-FHA2DO22.js.map} +0 -0
- /package/dist/{viewer-H7TVFT4E.js.map → viewer-XDPD52L7.js.map} +0 -0
|
@@ -6,13 +6,13 @@ import {
|
|
|
6
6
|
findStorybookDir,
|
|
7
7
|
generatePreviewModule,
|
|
8
8
|
loadConfig
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-2H2JAA3U.js";
|
|
10
10
|
import {
|
|
11
11
|
generateContext
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-B2TQKOLW.js";
|
|
13
13
|
import {
|
|
14
14
|
BRAND
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-ICAIQ57V.js";
|
|
16
16
|
|
|
17
17
|
// src/viewer/server.ts
|
|
18
18
|
import {
|
|
@@ -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-2T26CBWE.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-2T26CBWE.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-2T26CBWE.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-2T26CBWE.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-2T26CBWE.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-2T26CBWE.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-NJVKKLJ4.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-2T26CBWE.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-2T26CBWE.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-XDPD52L7.js.map
|
package/package.json
CHANGED
package/src/build.ts
CHANGED
|
@@ -5,13 +5,15 @@ import type {
|
|
|
5
5
|
SegmentsConfig,
|
|
6
6
|
CompiledSegmentsFile,
|
|
7
7
|
CompiledSegment,
|
|
8
|
-
|
|
8
|
+
CompiledBlock,
|
|
9
|
+
CompiledTokenData,
|
|
9
10
|
} from "./core/index.js";
|
|
10
|
-
import { BRAND,
|
|
11
|
-
import type {
|
|
11
|
+
import { BRAND, compileBlock, parseTokenFile } from "./core/index.js";
|
|
12
|
+
import type { BlockDefinition } from "./core/index.js";
|
|
12
13
|
import {
|
|
13
14
|
discoverSegmentFiles,
|
|
14
|
-
|
|
15
|
+
discoverBlockFiles,
|
|
16
|
+
discoverTokenFiles,
|
|
15
17
|
parseSegmentFile,
|
|
16
18
|
loadSegmentFile,
|
|
17
19
|
generateRegistry,
|
|
@@ -124,11 +126,11 @@ export async function buildSegments(
|
|
|
124
126
|
}
|
|
125
127
|
}
|
|
126
128
|
|
|
127
|
-
// Discover and compile
|
|
128
|
-
const
|
|
129
|
+
// Discover and compile block files
|
|
130
|
+
const blocks: Record<string, CompiledBlock> = {};
|
|
129
131
|
try {
|
|
130
|
-
const
|
|
131
|
-
for (const file of
|
|
132
|
+
const blockFiles = await discoverBlockFiles(configDir, config.exclude);
|
|
133
|
+
for (const file of blockFiles) {
|
|
132
134
|
try {
|
|
133
135
|
// loadSegmentFile uses esbuild to bundle+evaluate, returns default export
|
|
134
136
|
// CJS/ESM interop may double-wrap the default export
|
|
@@ -139,18 +141,55 @@ export async function buildSegments(
|
|
|
139
141
|
}
|
|
140
142
|
const def = raw;
|
|
141
143
|
if (def && typeof def === 'object' && 'name' in def && 'code' in def && 'components' in def) {
|
|
142
|
-
const compiled =
|
|
143
|
-
|
|
144
|
+
const compiled = compileBlock(def as unknown as BlockDefinition, file.relativePath);
|
|
145
|
+
blocks[compiled.name] = compiled;
|
|
144
146
|
}
|
|
145
147
|
} catch (error) {
|
|
146
148
|
warnings.push({
|
|
147
149
|
file: file.relativePath,
|
|
148
|
-
warning: `Failed to load
|
|
150
|
+
warning: `Failed to load block: ${error instanceof Error ? error.message : String(error)}`,
|
|
149
151
|
});
|
|
150
152
|
}
|
|
151
153
|
}
|
|
152
154
|
} catch {
|
|
153
|
-
//
|
|
155
|
+
// Block discovery failure is non-fatal
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Discover and extract design tokens from SCSS/CSS files
|
|
159
|
+
let tokens: CompiledTokenData | undefined;
|
|
160
|
+
try {
|
|
161
|
+
const tokenPatterns = config.tokens?.include;
|
|
162
|
+
const tokenFiles = await discoverTokenFiles(configDir, tokenPatterns, config.exclude);
|
|
163
|
+
if (tokenFiles.length > 0) {
|
|
164
|
+
// Merge tokens from all discovered files
|
|
165
|
+
const mergedCategories: Record<string, Array<{ name: string; description?: string }>> = {};
|
|
166
|
+
let prefix = '--';
|
|
167
|
+
let total = 0;
|
|
168
|
+
|
|
169
|
+
for (const file of tokenFiles) {
|
|
170
|
+
const content = await readFile(file.absolutePath, 'utf-8');
|
|
171
|
+
const parsed = parseTokenFile(content, file.relativePath);
|
|
172
|
+
prefix = parsed.prefix; // Use last file's prefix (usually consistent)
|
|
173
|
+
total += parsed.total;
|
|
174
|
+
for (const [cat, catTokens] of Object.entries(parsed.categories)) {
|
|
175
|
+
if (!mergedCategories[cat]) {
|
|
176
|
+
mergedCategories[cat] = [];
|
|
177
|
+
}
|
|
178
|
+
for (const t of catTokens) {
|
|
179
|
+
// Deduplicate by name
|
|
180
|
+
if (!mergedCategories[cat].some((e) => e.name === t.name)) {
|
|
181
|
+
mergedCategories[cat].push({ name: t.name, description: t.description });
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (total > 0) {
|
|
188
|
+
tokens = { prefix, total, categories: mergedCategories };
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
} catch {
|
|
192
|
+
// Token extraction failure is non-fatal
|
|
154
193
|
}
|
|
155
194
|
|
|
156
195
|
// Read package name for import statements
|
|
@@ -170,7 +209,8 @@ export async function buildSegments(
|
|
|
170
209
|
generatedAt: new Date().toISOString(),
|
|
171
210
|
...(packageName && { packageName }),
|
|
172
211
|
segments,
|
|
173
|
-
...(Object.keys(
|
|
212
|
+
...(Object.keys(blocks).length > 0 && { blocks }),
|
|
213
|
+
...(tokens && { tokens }),
|
|
174
214
|
};
|
|
175
215
|
|
|
176
216
|
const outputPath = resolve(configDir, config.outFile ?? BRAND.outFile);
|
package/src/core/constants.ts
CHANGED
|
@@ -66,7 +66,10 @@ export const BRAND = {
|
|
|
66
66
|
/** MCP tool name prefix (e.g., "fragments_") */
|
|
67
67
|
mcpToolPrefix: "fragments_",
|
|
68
68
|
|
|
69
|
-
/** File extension for
|
|
69
|
+
/** File extension for block definition files */
|
|
70
|
+
blockFileExtension: ".block.ts",
|
|
71
|
+
|
|
72
|
+
/** @deprecated Use blockFileExtension instead */
|
|
70
73
|
recipeFileExtension: ".recipe.ts",
|
|
71
74
|
|
|
72
75
|
/** Vite plugin namespace */
|
package/src/core/context.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { CompiledSegment,
|
|
1
|
+
import type { CompiledSegment, CompiledBlock, PropDefinition } from "./types.js";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Placeholder patterns to filter out from usage text.
|
|
@@ -55,12 +55,12 @@ export interface ContextResult {
|
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
/**
|
|
58
|
-
* Generate AI-ready context from compiled segments and optional
|
|
58
|
+
* Generate AI-ready context from compiled segments and optional blocks
|
|
59
59
|
*/
|
|
60
60
|
export function generateContext(
|
|
61
61
|
segments: CompiledSegment[],
|
|
62
62
|
options: ContextOptions = {},
|
|
63
|
-
|
|
63
|
+
blocks?: CompiledBlock[]
|
|
64
64
|
): ContextResult {
|
|
65
65
|
const format = options.format ?? "markdown";
|
|
66
66
|
const compact = options.compact ?? false;
|
|
@@ -81,10 +81,10 @@ export function generateContext(
|
|
|
81
81
|
});
|
|
82
82
|
|
|
83
83
|
if (format === "json") {
|
|
84
|
-
return generateJsonContext(sorted, include, compact,
|
|
84
|
+
return generateJsonContext(sorted, include, compact, blocks);
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
-
return generateMarkdownContext(sorted, include, compact,
|
|
87
|
+
return generateMarkdownContext(sorted, include, compact, blocks);
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
/**
|
|
@@ -94,7 +94,7 @@ function generateMarkdownContext(
|
|
|
94
94
|
segments: CompiledSegment[],
|
|
95
95
|
include: Required<NonNullable<ContextOptions["include"]>>,
|
|
96
96
|
compact: boolean,
|
|
97
|
-
|
|
97
|
+
blocks?: CompiledBlock[]
|
|
98
98
|
): ContextResult {
|
|
99
99
|
const lines: string[] = [];
|
|
100
100
|
|
|
@@ -209,26 +209,26 @@ function generateMarkdownContext(
|
|
|
209
209
|
lines.push("");
|
|
210
210
|
}
|
|
211
211
|
|
|
212
|
-
//
|
|
213
|
-
if (
|
|
214
|
-
lines.push("##
|
|
212
|
+
// Blocks section
|
|
213
|
+
if (blocks && blocks.length > 0) {
|
|
214
|
+
lines.push("## Blocks");
|
|
215
215
|
lines.push("");
|
|
216
216
|
lines.push("Composition patterns showing how components wire together.");
|
|
217
217
|
lines.push("");
|
|
218
218
|
|
|
219
|
-
for (const
|
|
220
|
-
lines.push(`### ${
|
|
219
|
+
for (const block of blocks) {
|
|
220
|
+
lines.push(`### ${block.name}`);
|
|
221
221
|
lines.push("");
|
|
222
|
-
lines.push(
|
|
222
|
+
lines.push(block.description);
|
|
223
223
|
lines.push("");
|
|
224
|
-
lines.push(`**Category:** ${
|
|
225
|
-
lines.push(`**Components:** ${
|
|
226
|
-
if (
|
|
227
|
-
lines.push(`**Tags:** ${
|
|
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
228
|
}
|
|
229
229
|
lines.push("");
|
|
230
230
|
lines.push("```tsx");
|
|
231
|
-
lines.push(
|
|
231
|
+
lines.push(block.code);
|
|
232
232
|
lines.push("```");
|
|
233
233
|
lines.push("");
|
|
234
234
|
lines.push("---");
|
|
@@ -250,7 +250,7 @@ function generateJsonContext(
|
|
|
250
250
|
segments: CompiledSegment[],
|
|
251
251
|
include: Required<NonNullable<ContextOptions["include"]>>,
|
|
252
252
|
compact: boolean,
|
|
253
|
-
|
|
253
|
+
blocks?: CompiledBlock[]
|
|
254
254
|
): ContextResult {
|
|
255
255
|
const categories = [...new Set(segments.map((s) => s.meta.category))].sort();
|
|
256
256
|
|
|
@@ -321,14 +321,14 @@ function generateJsonContext(
|
|
|
321
321
|
components[segment.meta.name] = component;
|
|
322
322
|
}
|
|
323
323
|
|
|
324
|
-
// Build
|
|
325
|
-
const
|
|
326
|
-
? Object.fromEntries(
|
|
327
|
-
description:
|
|
328
|
-
category:
|
|
329
|
-
components:
|
|
330
|
-
code:
|
|
331
|
-
tags:
|
|
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
332
|
}]))
|
|
333
333
|
: undefined;
|
|
334
334
|
|
|
@@ -338,10 +338,10 @@ function generateJsonContext(
|
|
|
338
338
|
summary: {
|
|
339
339
|
totalComponents: segments.length,
|
|
340
340
|
categories,
|
|
341
|
-
...(
|
|
341
|
+
...(blocksMap && { totalBlocks: blocks!.length }),
|
|
342
342
|
},
|
|
343
343
|
components,
|
|
344
|
-
...(
|
|
344
|
+
...(blocksMap && { blocks: blocksMap }),
|
|
345
345
|
};
|
|
346
346
|
|
|
347
347
|
const content = JSON.stringify(output, null, 2);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { SegmentDefinition, CompiledSegment, SegmentComponent,
|
|
2
|
-
import { segmentDefinitionSchema,
|
|
1
|
+
import type { SegmentDefinition, CompiledSegment, SegmentComponent, BlockDefinition, CompiledBlock } from './types.js';
|
|
2
|
+
import { segmentDefinitionSchema, blockDefinitionSchema } from './schema.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Define a segment for a component.
|
|
@@ -92,20 +92,20 @@ export function compileSegment(
|
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
/**
|
|
95
|
-
* Define a composition
|
|
95
|
+
* Define a composition block.
|
|
96
96
|
*
|
|
97
|
-
*
|
|
97
|
+
* Blocks are pure data describing how design system components
|
|
98
98
|
* wire together for common use cases.
|
|
99
99
|
*/
|
|
100
|
-
export function
|
|
100
|
+
export function defineBlock(definition: BlockDefinition): BlockDefinition {
|
|
101
101
|
if (process.env.NODE_ENV !== 'production') {
|
|
102
|
-
const result =
|
|
102
|
+
const result = blockDefinitionSchema.safeParse(definition);
|
|
103
103
|
if (!result.success) {
|
|
104
104
|
const errors = result.error.errors
|
|
105
105
|
.map((e) => ` - ${e.path.join('.')}: ${e.message}`)
|
|
106
106
|
.join('\n');
|
|
107
107
|
throw new Error(
|
|
108
|
-
`Invalid
|
|
108
|
+
`Invalid block definition for "${definition.name || 'unknown'}":\n${errors}`
|
|
109
109
|
);
|
|
110
110
|
}
|
|
111
111
|
}
|
|
@@ -114,12 +114,17 @@ export function defineRecipe(definition: RecipeDefinition): RecipeDefinition {
|
|
|
114
114
|
}
|
|
115
115
|
|
|
116
116
|
/**
|
|
117
|
-
*
|
|
117
|
+
* @deprecated Use defineBlock instead
|
|
118
118
|
*/
|
|
119
|
-
export
|
|
120
|
-
|
|
119
|
+
export const defineRecipe = defineBlock;
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Compile a block definition to JSON-serializable format.
|
|
123
|
+
*/
|
|
124
|
+
export function compileBlock(
|
|
125
|
+
definition: BlockDefinition,
|
|
121
126
|
filePath: string
|
|
122
|
-
):
|
|
127
|
+
): CompiledBlock {
|
|
123
128
|
return {
|
|
124
129
|
filePath,
|
|
125
130
|
name: definition.name,
|
|
@@ -131,6 +136,11 @@ export function compileRecipe(
|
|
|
131
136
|
};
|
|
132
137
|
}
|
|
133
138
|
|
|
139
|
+
/**
|
|
140
|
+
* @deprecated Use compileBlock instead
|
|
141
|
+
*/
|
|
142
|
+
export const compileRecipe = compileBlock;
|
|
143
|
+
|
|
134
144
|
/**
|
|
135
145
|
* Type helper for extracting props type from a component
|
|
136
146
|
*/
|
package/src/core/discovery.ts
CHANGED
|
@@ -27,14 +27,18 @@ export interface DiscoveredComponent {
|
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
/**
|
|
30
|
-
* Discover
|
|
30
|
+
* Discover block files (*.block.ts) under the config directory.
|
|
31
|
+
* Also discovers legacy *.recipe.ts files for backward compatibility.
|
|
31
32
|
*/
|
|
32
|
-
export async function
|
|
33
|
+
export async function discoverBlockFiles(
|
|
33
34
|
configDir: string,
|
|
34
35
|
exclude?: string[]
|
|
35
36
|
): Promise<DiscoveredFile[]> {
|
|
36
|
-
const
|
|
37
|
-
|
|
37
|
+
const patterns = [
|
|
38
|
+
`**/*${BRAND.blockFileExtension}`,
|
|
39
|
+
`**/*${BRAND.recipeFileExtension}`,
|
|
40
|
+
];
|
|
41
|
+
const files = await fg(patterns, {
|
|
38
42
|
cwd: configDir,
|
|
39
43
|
ignore: exclude ?? ['**/node_modules/**', '**/dist/**'],
|
|
40
44
|
absolute: false,
|
|
@@ -46,6 +50,11 @@ export async function discoverRecipeFiles(
|
|
|
46
50
|
}));
|
|
47
51
|
}
|
|
48
52
|
|
|
53
|
+
/**
|
|
54
|
+
* @deprecated Use discoverBlockFiles instead
|
|
55
|
+
*/
|
|
56
|
+
export const discoverRecipeFiles = discoverBlockFiles;
|
|
57
|
+
|
|
49
58
|
/**
|
|
50
59
|
* Discover segment files matching the config patterns
|
|
51
60
|
*/
|
|
@@ -296,6 +305,45 @@ export async function discoverComponentsFromBarrel(
|
|
|
296
305
|
return components;
|
|
297
306
|
}
|
|
298
307
|
|
|
308
|
+
/**
|
|
309
|
+
* Default glob patterns for discovering token files (SCSS/CSS with custom properties)
|
|
310
|
+
*/
|
|
311
|
+
const DEFAULT_TOKEN_PATTERNS = [
|
|
312
|
+
'src/**/tokens/**/_variables.scss',
|
|
313
|
+
'src/**/tokens/**/variables.scss',
|
|
314
|
+
'src/**/styles/**/variables.scss',
|
|
315
|
+
'src/**/styles/**/tokens.scss',
|
|
316
|
+
'src/**/styles/**/variables.css',
|
|
317
|
+
'src/**/theme/**/_variables.scss',
|
|
318
|
+
'src/**/theme/**/tokens.css',
|
|
319
|
+
];
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* Discover token files (SCSS/CSS files containing CSS custom properties).
|
|
323
|
+
* Uses config.tokens.include patterns if provided, otherwise falls back
|
|
324
|
+
* to default patterns that match common project structures.
|
|
325
|
+
*/
|
|
326
|
+
export async function discoverTokenFiles(
|
|
327
|
+
configDir: string,
|
|
328
|
+
patterns?: string[],
|
|
329
|
+
exclude?: string[]
|
|
330
|
+
): Promise<DiscoveredFile[]> {
|
|
331
|
+
const searchPatterns = patterns && patterns.length > 0
|
|
332
|
+
? patterns
|
|
333
|
+
: DEFAULT_TOKEN_PATTERNS;
|
|
334
|
+
|
|
335
|
+
const files = await fg(searchPatterns, {
|
|
336
|
+
cwd: configDir,
|
|
337
|
+
ignore: exclude ?? ['**/node_modules/**', '**/dist/**'],
|
|
338
|
+
absolute: false,
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
return files.map((relativePath) => ({
|
|
342
|
+
relativePath,
|
|
343
|
+
absolutePath: resolve(configDir, relativePath),
|
|
344
|
+
}));
|
|
345
|
+
}
|
|
346
|
+
|
|
299
347
|
/**
|
|
300
348
|
* Discover fragment files from installed packages that declare a "fragments" field
|
|
301
349
|
* in their package.json. This allows consumer projects to see components from
|
package/src/core/index.ts
CHANGED
|
@@ -27,8 +27,10 @@ export type {
|
|
|
27
27
|
RegistryOptions,
|
|
28
28
|
CompiledSegment,
|
|
29
29
|
CompiledSegmentsFile,
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
BlockDefinition,
|
|
31
|
+
CompiledBlock,
|
|
32
|
+
RecipeDefinition, // @deprecated - use BlockDefinition
|
|
33
|
+
CompiledRecipe, // @deprecated - use CompiledBlock
|
|
32
34
|
// Contract and provenance types
|
|
33
35
|
SegmentContract,
|
|
34
36
|
SegmentGenerated,
|
|
@@ -57,6 +59,9 @@ export type {
|
|
|
57
59
|
FigmaTextContentMapping,
|
|
58
60
|
// Token configuration
|
|
59
61
|
TokenConfig,
|
|
62
|
+
// Compiled token types
|
|
63
|
+
CompiledTokenEntry,
|
|
64
|
+
CompiledTokenData,
|
|
60
65
|
} from "./types.js";
|
|
61
66
|
|
|
62
67
|
// Token types
|
|
@@ -88,13 +93,14 @@ export {
|
|
|
88
93
|
segmentContractSchema,
|
|
89
94
|
segmentGeneratedSchema,
|
|
90
95
|
segmentBanSchema,
|
|
91
|
-
|
|
96
|
+
blockDefinitionSchema,
|
|
97
|
+
recipeDefinitionSchema, // @deprecated - use blockDefinitionSchema
|
|
92
98
|
// AI metadata schema
|
|
93
99
|
aiMetadataSchema,
|
|
94
100
|
} from "./schema.js";
|
|
95
101
|
|
|
96
102
|
// Main API
|
|
97
|
-
export { defineSegment, defineFragment, compileSegment, defineRecipe, compileRecipe } from "./defineSegment.js";
|
|
103
|
+
export { defineSegment, defineFragment, compileSegment, defineBlock, compileBlock, defineRecipe, compileRecipe } from "./defineSegment.js";
|
|
98
104
|
export type { InferProps } from "./defineSegment.js";
|
|
99
105
|
|
|
100
106
|
// Story adapter (runtime conversion of Storybook modules)
|
|
@@ -143,6 +149,10 @@ export type {
|
|
|
143
149
|
FragmentContextOptions,
|
|
144
150
|
} from "./fragment-types.js";
|
|
145
151
|
|
|
152
|
+
// Token parsing
|
|
153
|
+
export { parseTokenFile } from "./token-parser.js";
|
|
154
|
+
export type { ParsedToken, TokenParseOutput } from "./token-parser.js";
|
|
155
|
+
|
|
146
156
|
// Composition analysis
|
|
147
157
|
export { analyzeComposition } from "./composition.js";
|
|
148
158
|
export type {
|
package/src/core/loader.ts
CHANGED
package/src/core/node.ts
CHANGED
|
@@ -9,7 +9,8 @@ export { loadConfig, findConfigFile } from './config.js';
|
|
|
9
9
|
// Discovery
|
|
10
10
|
export {
|
|
11
11
|
discoverSegmentFiles,
|
|
12
|
-
|
|
12
|
+
discoverBlockFiles,
|
|
13
|
+
discoverRecipeFiles, // @deprecated - use discoverBlockFiles
|
|
13
14
|
discoverComponentFiles,
|
|
14
15
|
discoverInstalledFragments,
|
|
15
16
|
extractComponentName,
|
|
@@ -17,6 +18,7 @@ export {
|
|
|
17
18
|
discoverComponentsFromSource,
|
|
18
19
|
discoverComponentsFromBarrel,
|
|
19
20
|
discoverAllComponents,
|
|
21
|
+
discoverTokenFiles,
|
|
20
22
|
} from './discovery.js';
|
|
21
23
|
export type { DiscoveredFile, DiscoveredComponent } from './discovery.js';
|
|
22
24
|
|
package/src/core/parser.ts
CHANGED
|
@@ -375,7 +375,7 @@ function extractVariants(
|
|
|
375
375
|
|
|
376
376
|
// Try to extract code property if present
|
|
377
377
|
const codeProp = findProperty(element, "code");
|
|
378
|
-
if (codeProp && ts.isStringLiteral(codeProp)) {
|
|
378
|
+
if (codeProp && (ts.isStringLiteral(codeProp) || ts.isNoSubstitutionTemplateLiteral(codeProp))) {
|
|
379
379
|
variant.code = codeProp.text;
|
|
380
380
|
}
|
|
381
381
|
|
package/src/core/schema.ts
CHANGED
|
@@ -149,9 +149,9 @@ export const aiMetadataSchema = z.object({
|
|
|
149
149
|
});
|
|
150
150
|
|
|
151
151
|
/**
|
|
152
|
-
* Schema for
|
|
152
|
+
* Schema for block definitions
|
|
153
153
|
*/
|
|
154
|
-
export const
|
|
154
|
+
export const blockDefinitionSchema = z.object({
|
|
155
155
|
name: z.string().min(1),
|
|
156
156
|
description: z.string().min(1),
|
|
157
157
|
category: z.string().min(1),
|
|
@@ -191,3 +191,8 @@ export const segmentsConfigSchema = z.object({
|
|
|
191
191
|
include: z.array(z.string()).min(1),
|
|
192
192
|
}).passthrough().optional(),
|
|
193
193
|
});
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* @deprecated Use blockDefinitionSchema instead
|
|
197
|
+
*/
|
|
198
|
+
export const recipeDefinitionSchema = blockDefinitionSchema;
|