@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,364 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Registry Generator
|
|
3
|
+
* Generates .fragments/registry.json and .fragments/index.json
|
|
4
|
+
*
|
|
5
|
+
* Philosophy: Generate only what AI agents and humans can't easily get from source.
|
|
6
|
+
* - Props: AI can read TypeScript directly (skip by default)
|
|
7
|
+
* - Paths: Essential for file navigation (always include)
|
|
8
|
+
* - Enrichment: Human knowledge that doesn't exist elsewhere (include references)
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { readFileSync, existsSync } from "node:fs";
|
|
12
|
+
import { relative, dirname, basename, join } from "node:path";
|
|
13
|
+
import fg from "fast-glob";
|
|
14
|
+
import type {
|
|
15
|
+
Fragment,
|
|
16
|
+
FragmentRegistry,
|
|
17
|
+
FragmentIndex,
|
|
18
|
+
RegistryComponentEntry,
|
|
19
|
+
} from "../fragment-types.js";
|
|
20
|
+
import { extractPropsFromFile } from "./typescript-extractor.js";
|
|
21
|
+
import { BRAND } from "../constants.js";
|
|
22
|
+
import type { RegistryOptions } from "../types.js";
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Options for registry generation
|
|
26
|
+
*/
|
|
27
|
+
export interface RegistryGeneratorOptions {
|
|
28
|
+
/** Project root directory */
|
|
29
|
+
projectRoot: string;
|
|
30
|
+
/** Glob patterns for component files */
|
|
31
|
+
componentPatterns?: string[];
|
|
32
|
+
/** Glob patterns for story files */
|
|
33
|
+
storyPatterns?: string[];
|
|
34
|
+
/** Path to .fragments directory */
|
|
35
|
+
fragmentsDir?: string;
|
|
36
|
+
/** Registry options from config */
|
|
37
|
+
registryOptions?: RegistryOptions;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Result of registry generation
|
|
42
|
+
*/
|
|
43
|
+
export interface RegistryGeneratorResult {
|
|
44
|
+
/** Generated registry (full metadata) */
|
|
45
|
+
registry: FragmentRegistry;
|
|
46
|
+
/** Generated index (minimal name -> path) */
|
|
47
|
+
index: FragmentIndex;
|
|
48
|
+
/** Errors encountered during generation */
|
|
49
|
+
errors: Array<{ file: string; error: string }>;
|
|
50
|
+
/** Warnings */
|
|
51
|
+
warnings: Array<{ file: string; warning: string }>;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Generate a fragment registry and index by scanning the project
|
|
56
|
+
*/
|
|
57
|
+
export async function generateRegistry(
|
|
58
|
+
options: RegistryGeneratorOptions
|
|
59
|
+
): Promise<RegistryGeneratorResult> {
|
|
60
|
+
const {
|
|
61
|
+
projectRoot,
|
|
62
|
+
componentPatterns = ["src/**/*.tsx", "src/**/*.ts"],
|
|
63
|
+
storyPatterns = ["src/**/*.stories.tsx", "src/**/*.stories.ts"],
|
|
64
|
+
fragmentsDir = join(projectRoot, BRAND.dataDir),
|
|
65
|
+
registryOptions = {},
|
|
66
|
+
} = options;
|
|
67
|
+
|
|
68
|
+
const {
|
|
69
|
+
requireStory = false,
|
|
70
|
+
publicOnly = false,
|
|
71
|
+
categoryDepth = 1,
|
|
72
|
+
includeProps = false,
|
|
73
|
+
embedFragments = false,
|
|
74
|
+
} = registryOptions;
|
|
75
|
+
|
|
76
|
+
const errors: Array<{ file: string; error: string }> = [];
|
|
77
|
+
const warnings: Array<{ file: string; warning: string }> = [];
|
|
78
|
+
|
|
79
|
+
// Find all story files first (for requireStory filtering)
|
|
80
|
+
const storyFiles = await fg(storyPatterns, {
|
|
81
|
+
cwd: projectRoot,
|
|
82
|
+
ignore: ["**/node_modules/**"],
|
|
83
|
+
absolute: true,
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
// Build a map of story files by component directory and base name
|
|
87
|
+
const storyMap = new Map<string, string>();
|
|
88
|
+
for (const storyPath of storyFiles) {
|
|
89
|
+
const storyDir = dirname(storyPath);
|
|
90
|
+
const storyBase = basename(storyPath).replace(/\.stories\.(tsx?|jsx?)$/, "");
|
|
91
|
+
storyMap.set(`${storyDir}/${storyBase}`, storyPath);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Find all component files
|
|
95
|
+
const componentFiles = await fg(componentPatterns, {
|
|
96
|
+
cwd: projectRoot,
|
|
97
|
+
ignore: [
|
|
98
|
+
"**/node_modules/**",
|
|
99
|
+
"**/*.stories.*",
|
|
100
|
+
"**/*.test.*",
|
|
101
|
+
"**/*.spec.*",
|
|
102
|
+
"**/*.d.ts",
|
|
103
|
+
],
|
|
104
|
+
absolute: true,
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
// Find all fragment files
|
|
108
|
+
const fragmentPattern = join(
|
|
109
|
+
BRAND.dataDir,
|
|
110
|
+
BRAND.componentsDir,
|
|
111
|
+
`*${BRAND.fileExtension}`
|
|
112
|
+
);
|
|
113
|
+
const fragmentFiles = await fg(fragmentPattern, {
|
|
114
|
+
cwd: projectRoot,
|
|
115
|
+
absolute: true,
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
// Build fragment lookup map
|
|
119
|
+
const fragmentMap = new Map<string, { path: string; fragment: Fragment }>();
|
|
120
|
+
for (const fragmentPath of fragmentFiles) {
|
|
121
|
+
const fragmentName = basename(fragmentPath).replace(BRAND.fileExtension, "");
|
|
122
|
+
try {
|
|
123
|
+
const content = readFileSync(fragmentPath, "utf-8");
|
|
124
|
+
const fragment = JSON.parse(content) as Fragment;
|
|
125
|
+
fragmentMap.set(fragmentName, {
|
|
126
|
+
path: relative(projectRoot, fragmentPath),
|
|
127
|
+
fragment,
|
|
128
|
+
});
|
|
129
|
+
} catch (e) {
|
|
130
|
+
errors.push({
|
|
131
|
+
file: fragmentPath,
|
|
132
|
+
error: `Failed to parse fragment: ${e instanceof Error ? e.message : String(e)}`,
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Build component index
|
|
138
|
+
const components: Record<string, RegistryComponentEntry> = {};
|
|
139
|
+
const indexComponents: Record<string, string> = {};
|
|
140
|
+
const categories: Record<string, string[]> = {};
|
|
141
|
+
|
|
142
|
+
// Process component files
|
|
143
|
+
for (const filePath of componentFiles) {
|
|
144
|
+
try {
|
|
145
|
+
const extracted = extractPropsFromFile(filePath);
|
|
146
|
+
if (!extracted || !extracted.componentName) {
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const componentName = extracted.componentName;
|
|
151
|
+
const relativePath = relative(projectRoot, filePath);
|
|
152
|
+
|
|
153
|
+
// Check if component is exported (for publicOnly filter)
|
|
154
|
+
if (publicOnly && !extracted.exports.includes(componentName)) {
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Find matching story file
|
|
159
|
+
const componentDir = dirname(filePath);
|
|
160
|
+
const baseNameWithoutExt = basename(filePath).replace(/\.(tsx?|jsx?)$/, "");
|
|
161
|
+
const storyPath = storyMap.get(`${componentDir}/${baseNameWithoutExt}`);
|
|
162
|
+
|
|
163
|
+
// Apply requireStory filter
|
|
164
|
+
if (requireStory && !storyPath) {
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Get fragment data if exists
|
|
169
|
+
const fragmentData = fragmentMap.get(componentName);
|
|
170
|
+
|
|
171
|
+
// Determine category from directory structure
|
|
172
|
+
const category = fragmentData?.fragment?.meta?.status
|
|
173
|
+
? undefined // Don't use status as category
|
|
174
|
+
: getCategoryFromPath(relativePath, categoryDepth);
|
|
175
|
+
|
|
176
|
+
// Check if fragment has meaningful enrichment (not just a skeleton)
|
|
177
|
+
const hasEnrichment = fragmentData
|
|
178
|
+
? hasRealEnrichment(fragmentData.fragment)
|
|
179
|
+
: false;
|
|
180
|
+
|
|
181
|
+
// Build minimal component entry
|
|
182
|
+
const entry: RegistryComponentEntry = {
|
|
183
|
+
path: relativePath,
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
// Add optional fields only if present
|
|
187
|
+
if (storyPath) {
|
|
188
|
+
entry.storyPath = relative(projectRoot, storyPath);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (fragmentData) {
|
|
192
|
+
entry.fragmentPath = fragmentData.path;
|
|
193
|
+
if (hasEnrichment) {
|
|
194
|
+
entry.hasEnrichment = true;
|
|
195
|
+
}
|
|
196
|
+
if (fragmentData.fragment.description) {
|
|
197
|
+
entry.description = fragmentData.fragment.description;
|
|
198
|
+
}
|
|
199
|
+
if (fragmentData.fragment.meta?.status) {
|
|
200
|
+
entry.status = fragmentData.fragment.meta.status;
|
|
201
|
+
}
|
|
202
|
+
// Only embed full fragment if explicitly requested
|
|
203
|
+
if (embedFragments) {
|
|
204
|
+
entry.fragment = fragmentData.fragment;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (category) {
|
|
209
|
+
entry.category = category;
|
|
210
|
+
// Add to categories index
|
|
211
|
+
if (!categories[category]) {
|
|
212
|
+
categories[category] = [];
|
|
213
|
+
}
|
|
214
|
+
categories[category].push(componentName);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Only include props if explicitly requested
|
|
218
|
+
if (includeProps && extracted.props) {
|
|
219
|
+
entry.props = extracted.props;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Only include exports if multiple exports
|
|
223
|
+
if (extracted.exports.length > 1) {
|
|
224
|
+
entry.exports = extracted.exports;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
components[componentName] = entry;
|
|
228
|
+
indexComponents[componentName] = relativePath;
|
|
229
|
+
} catch (e) {
|
|
230
|
+
errors.push({
|
|
231
|
+
file: filePath,
|
|
232
|
+
error: `Failed to extract component: ${e instanceof Error ? e.message : String(e)}`,
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const componentCount = Object.keys(components).length;
|
|
238
|
+
|
|
239
|
+
// Build registry
|
|
240
|
+
const registry: FragmentRegistry = {
|
|
241
|
+
$schema: "https://fragments.dev/schema/registry-v1.json",
|
|
242
|
+
version: "1.0",
|
|
243
|
+
generatedAt: new Date().toISOString(),
|
|
244
|
+
componentCount,
|
|
245
|
+
components,
|
|
246
|
+
categories,
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
// Build minimal index
|
|
250
|
+
const index: FragmentIndex = {
|
|
251
|
+
version: "1.0",
|
|
252
|
+
generatedAt: registry.generatedAt,
|
|
253
|
+
components: indexComponents,
|
|
254
|
+
categories,
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
return { registry, index, errors, warnings };
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Get category from file path based on directory structure
|
|
262
|
+
*
|
|
263
|
+
* Examples:
|
|
264
|
+
* - src/components/buttons/Button.tsx -> "buttons"
|
|
265
|
+
* - src/components/forms/inputs/TextInput.tsx -> "forms" (depth 1) or "forms/inputs" (depth 2)
|
|
266
|
+
* - src/components/Button/Button.tsx -> "Button" (component folder)
|
|
267
|
+
*/
|
|
268
|
+
function getCategoryFromPath(relativePath: string, depth: number = 1): string | undefined {
|
|
269
|
+
const parts = relativePath.split("/");
|
|
270
|
+
const componentsIndex = parts.findIndex((p) => p === "components");
|
|
271
|
+
|
|
272
|
+
if (componentsIndex === -1) {
|
|
273
|
+
return undefined;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// Get the parts after "components/"
|
|
277
|
+
const afterComponents = parts.slice(componentsIndex + 1);
|
|
278
|
+
|
|
279
|
+
// If it's just components/Button.tsx, no category
|
|
280
|
+
if (afterComponents.length <= 1) {
|
|
281
|
+
return undefined;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// If it's components/Button/Button.tsx (component folder pattern), no category
|
|
285
|
+
const folderName = afterComponents[0];
|
|
286
|
+
const fileName = afterComponents[afterComponents.length - 1].replace(/\.(tsx?|jsx?)$/, "");
|
|
287
|
+
if (afterComponents.length === 2 && folderName === fileName) {
|
|
288
|
+
return undefined;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// Get category parts based on depth
|
|
292
|
+
const categoryParts = afterComponents.slice(0, Math.min(depth, afterComponents.length - 1));
|
|
293
|
+
|
|
294
|
+
// Filter out component folders (where folder name matches file name)
|
|
295
|
+
const lastCategoryPart = categoryParts[categoryParts.length - 1];
|
|
296
|
+
if (lastCategoryPart === fileName) {
|
|
297
|
+
categoryParts.pop();
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
if (categoryParts.length === 0) {
|
|
301
|
+
return undefined;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
return categoryParts.join("/");
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Check if a fragment has meaningful enrichment beyond a skeleton
|
|
309
|
+
*/
|
|
310
|
+
function hasRealEnrichment(fragment: Fragment): boolean {
|
|
311
|
+
// Has description beyond just the name
|
|
312
|
+
if (fragment.description && fragment.description.length > 20) {
|
|
313
|
+
return true;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// Has usage guidelines
|
|
317
|
+
if (fragment.usage?.when && fragment.usage.when.length > 0) {
|
|
318
|
+
return true;
|
|
319
|
+
}
|
|
320
|
+
if (fragment.usage?.doNot && fragment.usage.doNot.length > 0) {
|
|
321
|
+
return true;
|
|
322
|
+
}
|
|
323
|
+
if (fragment.usage?.patterns && fragment.usage.patterns.length > 0) {
|
|
324
|
+
return true;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// Has accessibility info
|
|
328
|
+
if (fragment.accessibility?.requirements && fragment.accessibility.requirements.length > 0) {
|
|
329
|
+
return true;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// Has figma links
|
|
333
|
+
if (fragment.figma?.nodeId || fragment.figma?.variants) {
|
|
334
|
+
return true;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// Has related components
|
|
338
|
+
if (fragment.related?.similar && fragment.related.similar.length > 0) {
|
|
339
|
+
return true;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
return false;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Resolve component name to path using registry
|
|
347
|
+
*/
|
|
348
|
+
export function resolveComponentPath(
|
|
349
|
+
componentName: string,
|
|
350
|
+
registry: FragmentRegistry
|
|
351
|
+
): string | undefined {
|
|
352
|
+
const entry = registry.components[componentName];
|
|
353
|
+
return entry?.path;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Get all components by category
|
|
358
|
+
*/
|
|
359
|
+
export function getComponentsByCategory(
|
|
360
|
+
category: string,
|
|
361
|
+
registry: FragmentRegistry
|
|
362
|
+
): string[] {
|
|
363
|
+
return registry.categories?.[category] || [];
|
|
364
|
+
}
|