@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.
Files changed (57) hide show
  1. package/README.md +1 -1
  2. package/dist/bin.js +12 -12
  3. package/dist/{chunk-NOTYONHY.js → chunk-2DJH4F4P.js} +2 -2
  4. package/dist/{chunk-5CKYLCJH.js → chunk-2H2JAA3U.js} +35 -7
  5. package/dist/chunk-2H2JAA3U.js.map +1 -0
  6. package/dist/{chunk-G3M3MPQ6.js → chunk-B2TQKOLW.js} +157 -30
  7. package/dist/chunk-B2TQKOLW.js.map +1 -0
  8. package/dist/{chunk-AW7MWOUH.js → chunk-ICAIQ57V.js} +9 -5
  9. package/dist/chunk-ICAIQ57V.js.map +1 -0
  10. package/dist/{chunk-5ZYEOHYK.js → chunk-IOJE35DZ.js} +2 -2
  11. package/dist/{chunk-ZFKGX3QK.js → chunk-UXRGD3DM.js} +47 -14
  12. package/dist/chunk-UXRGD3DM.js.map +1 -0
  13. package/dist/{chunk-J4SI5RIH.js → chunk-XNWDI6UT.js} +4 -4
  14. package/dist/{core-LNXDLXDP.js → core-NJVKKLJ4.js} +11 -3
  15. package/dist/{generate-OIXXHOWR.js → generate-OVGMDKCJ.js} +4 -4
  16. package/dist/index.d.ts +30 -4
  17. package/dist/index.js +6 -6
  18. package/dist/{init-EVPXIDW4.js → init-EOA7TTOR.js} +4 -4
  19. package/dist/mcp-bin.js +266 -36
  20. package/dist/mcp-bin.js.map +1 -1
  21. package/dist/scan-YN4LUDKY.js +12 -0
  22. package/dist/{service-K52ORLCJ.js → service-2T26CBWE.js} +4 -4
  23. package/dist/{static-viewer-JNQIHA4B.js → static-viewer-CLJJRYHK.js} +4 -4
  24. package/dist/{test-USARUEFW.js → test-ECPEXFDN.js} +3 -3
  25. package/dist/{tokens-C6YHBOQE.js → tokens-FHA2DO22.js} +5 -5
  26. package/dist/{viewer-H7TVFT4E.js → viewer-XDPD52L7.js} +13 -13
  27. package/package.json +1 -1
  28. package/src/build.ts +53 -13
  29. package/src/core/constants.ts +4 -1
  30. package/src/core/context.ts +28 -28
  31. package/src/core/defineSegment.ts +21 -11
  32. package/src/core/discovery.ts +52 -4
  33. package/src/core/index.ts +14 -4
  34. package/src/core/loader.ts +3 -0
  35. package/src/core/node.ts +3 -1
  36. package/src/core/parser.ts +1 -1
  37. package/src/core/schema.ts +7 -2
  38. package/src/core/token-parser.ts +211 -0
  39. package/src/core/types.ts +46 -6
  40. package/src/mcp/server.ts +321 -39
  41. package/dist/chunk-5CKYLCJH.js.map +0 -1
  42. package/dist/chunk-AW7MWOUH.js.map +0 -1
  43. package/dist/chunk-G3M3MPQ6.js.map +0 -1
  44. package/dist/chunk-ZFKGX3QK.js.map +0 -1
  45. package/dist/scan-YVYD64GD.js +0 -12
  46. /package/dist/{chunk-NOTYONHY.js.map → chunk-2DJH4F4P.js.map} +0 -0
  47. /package/dist/{chunk-5ZYEOHYK.js.map → chunk-IOJE35DZ.js.map} +0 -0
  48. /package/dist/{chunk-J4SI5RIH.js.map → chunk-XNWDI6UT.js.map} +0 -0
  49. /package/dist/{core-LNXDLXDP.js.map → core-NJVKKLJ4.js.map} +0 -0
  50. /package/dist/{generate-OIXXHOWR.js.map → generate-OVGMDKCJ.js.map} +0 -0
  51. /package/dist/{init-EVPXIDW4.js.map → init-EOA7TTOR.js.map} +0 -0
  52. /package/dist/{scan-YVYD64GD.js.map → scan-YN4LUDKY.js.map} +0 -0
  53. /package/dist/{service-K52ORLCJ.js.map → service-2T26CBWE.js.map} +0 -0
  54. /package/dist/{static-viewer-JNQIHA4B.js.map → static-viewer-CLJJRYHK.js.map} +0 -0
  55. /package/dist/{test-USARUEFW.js.map → test-ECPEXFDN.js.map} +0 -0
  56. /package/dist/{tokens-C6YHBOQE.js.map → tokens-FHA2DO22.js.map} +0 -0
  57. /package/dist/{viewer-H7TVFT4E.js.map → viewer-XDPD52L7.js.map} +0 -0
@@ -0,0 +1,211 @@
1
+ /**
2
+ * Token Parser — extracts CSS custom property declarations from SCSS/CSS files.
3
+ *
4
+ * Parses files for `--prefix-*: value;` declarations and groups them
5
+ * by SCSS comment sections (e.g., `// Typography`, `// Colors`).
6
+ * Falls back to naming-convention-based categorization when comments
7
+ * are absent.
8
+ */
9
+
10
+ export interface ParsedToken {
11
+ /** Full CSS variable name (e.g., "--fui-color-accent") */
12
+ name: string;
13
+ /** Category inferred from SCSS comment or naming convention */
14
+ category: string;
15
+ /** Description from inline comment, if any */
16
+ description?: string;
17
+ }
18
+
19
+ export interface TokenParseOutput {
20
+ /** Detected prefix (e.g., "--fui-") */
21
+ prefix: string;
22
+ /** Tokens grouped by category */
23
+ categories: Record<string, ParsedToken[]>;
24
+ /** Total number of tokens found */
25
+ total: number;
26
+ }
27
+
28
+ /**
29
+ * Category inference from naming conventions.
30
+ * Order matters — first match wins.
31
+ */
32
+ const NAMING_RULES: Array<{ pattern: RegExp; category: string }> = [
33
+ { pattern: /--\w+-font-/, category: 'typography' },
34
+ { pattern: /--\w+-line-height-/, category: 'typography' },
35
+ { pattern: /--\w+-space-/, category: 'spacing' },
36
+ { pattern: /--\w+-padding-/, category: 'spacing' },
37
+ { pattern: /--\w+-radius-/, category: 'radius' },
38
+ { pattern: /--\w+-color-/, category: 'colors' },
39
+ { pattern: /--\w+-bg-/, category: 'surfaces' },
40
+ { pattern: /--\w+-text-/, category: 'text' },
41
+ { pattern: /--\w+-border/, category: 'borders' },
42
+ { pattern: /--\w+-shadow-/, category: 'shadows' },
43
+ { pattern: /--\w+-focus-/, category: 'focus' },
44
+ { pattern: /--\w+-transition-/, category: 'transitions' },
45
+ { pattern: /--\w+-scrollbar-/, category: 'scrollbar' },
46
+ { pattern: /--\w+-z-index/, category: 'z-index' },
47
+ { pattern: /--\w+-(button|input|touch)-/, category: 'component-sizing' },
48
+ { pattern: /--\w+-appshell-/, category: 'layout' },
49
+ { pattern: /--\w+-header-/, category: 'layout' },
50
+ { pattern: /--\w+-code-/, category: 'code' },
51
+ { pattern: /--\w+-tooltip-/, category: 'tooltip' },
52
+ { pattern: /--\w+-hero-/, category: 'marketing' },
53
+ ];
54
+
55
+ /**
56
+ * Infer category from a CSS variable name using naming conventions.
57
+ */
58
+ function inferCategory(name: string): string {
59
+ for (const rule of NAMING_RULES) {
60
+ if (rule.pattern.test(name)) {
61
+ return rule.category;
62
+ }
63
+ }
64
+ return 'other';
65
+ }
66
+
67
+ /**
68
+ * Detect the most common prefix from a list of CSS variable names.
69
+ * E.g., given ["--fui-color-accent", "--fui-bg-primary"] → "--fui-"
70
+ */
71
+ function detectPrefix(names: string[]): string {
72
+ if (names.length === 0) return '--';
73
+
74
+ // Find common prefix after "--"
75
+ const stripped = names.map((n) => n.slice(2)); // remove "--"
76
+ let prefix = '';
77
+ const first = stripped[0];
78
+
79
+ for (let i = 0; i < first.length; i++) {
80
+ const ch = first[i];
81
+ if (stripped.every((s) => s[i] === ch)) {
82
+ prefix += ch;
83
+ } else {
84
+ break;
85
+ }
86
+ }
87
+
88
+ // Trim to last hyphen to get clean prefix
89
+ const lastHyphen = prefix.lastIndexOf('-');
90
+ if (lastHyphen > 0) {
91
+ prefix = prefix.slice(0, lastHyphen + 1);
92
+ }
93
+
94
+ return `--${prefix}`;
95
+ }
96
+
97
+ /**
98
+ * Normalize a SCSS comment into a category name.
99
+ * "// Typography" → "typography"
100
+ * "// Component heights" → "component-sizing"
101
+ * "// Hero/Marketing gradient" → "marketing"
102
+ */
103
+ function normalizeCategory(comment: string): string {
104
+ const text = comment
105
+ .trim()
106
+ .replace(/^\/\/\s*/, '')
107
+ .replace(/^\/\*+\s*/, '')
108
+ .replace(/\s*\*+\/$/, '')
109
+ .trim()
110
+ .toLowerCase();
111
+
112
+ // Map common comment headings to clean category names
113
+ const mappings: Record<string, string> = {
114
+ 'base configuration': 'base',
115
+ 'typography': 'typography',
116
+ 'spacing (micro)': 'spacing',
117
+ 'spacing': 'spacing',
118
+ 'density padding': 'spacing',
119
+ 'border radius': 'radius',
120
+ 'transitions': 'transitions',
121
+ 'colors': 'colors',
122
+ 'surfaces': 'surfaces',
123
+ 'text': 'text',
124
+ 'borders': 'borders',
125
+ 'shadows': 'shadows',
126
+ 'focus': 'focus',
127
+ 'scrollbar': 'scrollbar',
128
+ 'component heights': 'component-sizing',
129
+ 'appshell layout': 'layout',
130
+ 'codeblock': 'code',
131
+ 'tooltip': 'tooltip',
132
+ 'hero/marketing gradient': 'marketing',
133
+ };
134
+
135
+ return mappings[text] ?? text.replace(/\s+/g, '-');
136
+ }
137
+
138
+ /**
139
+ * Parse a SCSS or CSS file and extract CSS custom property declarations.
140
+ *
141
+ * Handles two grouping strategies:
142
+ * 1. Comment-based: Uses `// Category` comments above groups of declarations
143
+ * 2. Naming-based: Falls back to inferring category from variable name patterns
144
+ */
145
+ export function parseTokenFile(content: string, filePath: string): TokenParseOutput {
146
+ const lines = content.split('\n');
147
+ const tokens: ParsedToken[] = [];
148
+ const seenNames = new Set<string>();
149
+ let currentCategory = 'other';
150
+ let hasCommentCategories = false;
151
+
152
+ // Regex for CSS custom property declarations
153
+ // Matches: --name: value; (with optional SCSS interpolation)
154
+ const varDeclRegex = /^\s*(--[\w-]+)\s*:/;
155
+ // Regex for section comments (// Category or /* Category */)
156
+ // Allow any characters after uppercase start (including / for "Hero/Marketing")
157
+ const sectionCommentRegex = /^\s*\/\/\s+([A-Z].+)$/;
158
+
159
+ for (const line of lines) {
160
+ // Check for section comment
161
+ const commentMatch = line.match(sectionCommentRegex);
162
+ if (commentMatch) {
163
+ const normalized = normalizeCategory(commentMatch[0]);
164
+ if (normalized) {
165
+ currentCategory = normalized;
166
+ hasCommentCategories = true;
167
+ }
168
+ continue;
169
+ }
170
+
171
+ // Check for CSS variable declaration
172
+ const varMatch = line.match(varDeclRegex);
173
+ if (varMatch) {
174
+ const name = varMatch[1];
175
+
176
+ // Deduplicate: keep only the first occurrence of each variable.
177
+ // Dark mode and high contrast blocks redefine the same variables
178
+ // with different values — we only want the canonical list.
179
+ if (seenNames.has(name)) continue;
180
+ seenNames.add(name);
181
+
182
+ // Extract inline comment if present
183
+ const inlineComment = line.match(/\/\/\s*(.+)$/);
184
+ const description = inlineComment ? inlineComment[1].trim() : undefined;
185
+
186
+ tokens.push({
187
+ name,
188
+ category: hasCommentCategories ? currentCategory : inferCategory(name),
189
+ description,
190
+ });
191
+ }
192
+ }
193
+
194
+ // Group by category
195
+ const categories: Record<string, ParsedToken[]> = {};
196
+ for (const token of tokens) {
197
+ if (!categories[token.category]) {
198
+ categories[token.category] = [];
199
+ }
200
+ categories[token.category].push(token);
201
+ }
202
+
203
+ // Detect prefix
204
+ const prefix = detectPrefix(tokens.map((t) => t.name));
205
+
206
+ return {
207
+ prefix,
208
+ categories,
209
+ total: tokens.length,
210
+ };
211
+ }
package/src/core/types.ts CHANGED
@@ -735,10 +735,10 @@ export interface CompiledSegment {
735
735
  }
736
736
 
737
737
  /**
738
- * Recipe definition — a named composition pattern showing how
738
+ * Block definition — a named composition pattern showing how
739
739
  * design system components wire together for a common use case.
740
740
  */
741
- export interface RecipeDefinition {
741
+ export interface BlockDefinition {
742
742
  name: string;
743
743
  description: string;
744
744
  category: string;
@@ -748,9 +748,14 @@ export interface RecipeDefinition {
748
748
  }
749
749
 
750
750
  /**
751
- * Compiled recipe data (JSON-serializable for AI consumption)
751
+ * @deprecated Use BlockDefinition instead
752
752
  */
753
- export interface CompiledRecipe {
753
+ export type RecipeDefinition = BlockDefinition;
754
+
755
+ /**
756
+ * Compiled block data (JSON-serializable for AI consumption)
757
+ */
758
+ export interface CompiledBlock {
754
759
  filePath: string;
755
760
  name: string;
756
761
  description: string;
@@ -760,6 +765,33 @@ export interface CompiledRecipe {
760
765
  tags?: string[];
761
766
  }
762
767
 
768
+ /**
769
+ * @deprecated Use CompiledBlock instead
770
+ */
771
+ export type CompiledRecipe = CompiledBlock;
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
+
763
795
  /**
764
796
  * The compiled segments.json structure
765
797
  */
@@ -776,6 +808,14 @@ export interface CompiledSegmentsFile {
776
808
  /** All compiled segments indexed by component name */
777
809
  segments: Record<string, CompiledSegment>;
778
810
 
779
- /** All compiled recipes indexed by recipe name */
780
- recipes?: Record<string, CompiledRecipe>;
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>;
781
821
  }