@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,523 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Documentation Extractor
|
|
3
|
+
*
|
|
4
|
+
* Extracts JSDoc, TSDoc, and TypeScript interface information from component files.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { parse } from "@babel/parser";
|
|
8
|
+
import _traverse from "@babel/traverse";
|
|
9
|
+
import * as t from "@babel/types";
|
|
10
|
+
|
|
11
|
+
// Handle CommonJS/ESM interop
|
|
12
|
+
const traverse = (_traverse as unknown as { default: typeof _traverse }).default || _traverse;
|
|
13
|
+
import { readFile } from "node:fs/promises";
|
|
14
|
+
import { existsSync } from "node:fs";
|
|
15
|
+
import { basename, dirname, join } from "node:path";
|
|
16
|
+
import type { ExtractedDocs } from "./types.js";
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Extract documentation from a component source file
|
|
20
|
+
*/
|
|
21
|
+
export async function extractComponentDocs(
|
|
22
|
+
filePath: string
|
|
23
|
+
): Promise<ExtractedDocs> {
|
|
24
|
+
const content = await readFile(filePath, "utf-8");
|
|
25
|
+
return extractDocsFromSource(content, filePath);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Extract documentation from source code
|
|
30
|
+
*/
|
|
31
|
+
export function extractDocsFromSource(
|
|
32
|
+
source: string,
|
|
33
|
+
filePath: string
|
|
34
|
+
): ExtractedDocs {
|
|
35
|
+
const isTypeScript = filePath.endsWith(".ts") || filePath.endsWith(".tsx");
|
|
36
|
+
const isJSX = filePath.endsWith(".tsx") || filePath.endsWith(".jsx");
|
|
37
|
+
|
|
38
|
+
const ast = parse(source, {
|
|
39
|
+
sourceType: "module",
|
|
40
|
+
plugins: [
|
|
41
|
+
...(isTypeScript ? (["typescript"] as const) : []),
|
|
42
|
+
...(isJSX ? (["jsx"] as const) : []),
|
|
43
|
+
"decorators-legacy",
|
|
44
|
+
],
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
const docs: ExtractedDocs = {
|
|
48
|
+
filePath,
|
|
49
|
+
componentName: inferComponentName(filePath),
|
|
50
|
+
description: "",
|
|
51
|
+
props: [],
|
|
52
|
+
examples: [],
|
|
53
|
+
tags: {},
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
// Track leading comments for the main export
|
|
57
|
+
let mainExportComment: string | undefined;
|
|
58
|
+
|
|
59
|
+
traverse(ast, {
|
|
60
|
+
// Extract JSDoc from function declarations
|
|
61
|
+
FunctionDeclaration(path) {
|
|
62
|
+
const leadingComments = path.node.leadingComments;
|
|
63
|
+
if (leadingComments) {
|
|
64
|
+
const jsdoc = extractJSDoc(leadingComments);
|
|
65
|
+
if (jsdoc && isExportedComponent(path)) {
|
|
66
|
+
mergeJSDoc(docs, jsdoc);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
|
|
71
|
+
// Extract JSDoc from arrow function variable declarations
|
|
72
|
+
VariableDeclaration(path) {
|
|
73
|
+
const leadingComments = path.node.leadingComments;
|
|
74
|
+
if (leadingComments && hasExportedArrowFunction(path)) {
|
|
75
|
+
const jsdoc = extractJSDoc(leadingComments);
|
|
76
|
+
if (jsdoc) {
|
|
77
|
+
mergeJSDoc(docs, jsdoc);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
|
|
82
|
+
// Extract JSDoc from export default
|
|
83
|
+
ExportDefaultDeclaration(path) {
|
|
84
|
+
const leadingComments = path.node.leadingComments;
|
|
85
|
+
if (leadingComments) {
|
|
86
|
+
const jsdoc = extractJSDoc(leadingComments);
|
|
87
|
+
if (jsdoc) {
|
|
88
|
+
mergeJSDoc(docs, jsdoc);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
|
|
93
|
+
// Extract props from TypeScript interface
|
|
94
|
+
TSInterfaceDeclaration(path) {
|
|
95
|
+
const name = path.node.id.name;
|
|
96
|
+
if (isPropsInterface(name)) {
|
|
97
|
+
const props = extractPropsFromInterface(path.node);
|
|
98
|
+
docs.props.push(...props);
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
|
|
102
|
+
// Extract props from TypeScript type alias
|
|
103
|
+
TSTypeAliasDeclaration(path) {
|
|
104
|
+
const name = path.node.id.name;
|
|
105
|
+
if (isPropsInterface(name)) {
|
|
106
|
+
const props = extractPropsFromTypeAlias(path.node);
|
|
107
|
+
docs.props.push(...props);
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
// Clean up duplicates
|
|
113
|
+
docs.props = deduplicateProps(docs.props);
|
|
114
|
+
|
|
115
|
+
return docs;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Extract JSDoc information from comments
|
|
120
|
+
*/
|
|
121
|
+
function extractJSDoc(
|
|
122
|
+
comments: readonly t.Comment[]
|
|
123
|
+
): Partial<ExtractedDocs> | null {
|
|
124
|
+
const blockComments = comments.filter(
|
|
125
|
+
(c) => c.type === "CommentBlock" && c.value.startsWith("*")
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
if (blockComments.length === 0) return null;
|
|
129
|
+
|
|
130
|
+
const comment = blockComments[blockComments.length - 1];
|
|
131
|
+
const content = comment.value;
|
|
132
|
+
|
|
133
|
+
const result: Partial<ExtractedDocs> = {
|
|
134
|
+
description: "",
|
|
135
|
+
props: [],
|
|
136
|
+
examples: [],
|
|
137
|
+
tags: {},
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
// Parse JSDoc content
|
|
141
|
+
const lines = content.split("\n");
|
|
142
|
+
let currentDescription: string[] = [];
|
|
143
|
+
let currentTag: string | null = null;
|
|
144
|
+
let currentTagContent: string[] = [];
|
|
145
|
+
|
|
146
|
+
for (const line of lines) {
|
|
147
|
+
const trimmed = line.replace(/^\s*\*\s?/, "").trim();
|
|
148
|
+
|
|
149
|
+
// Check for JSDoc tag
|
|
150
|
+
const tagMatch = trimmed.match(/^@(\w+)(?:\s+(.*))?$/);
|
|
151
|
+
|
|
152
|
+
if (tagMatch) {
|
|
153
|
+
// Save previous tag if exists
|
|
154
|
+
if (currentTag) {
|
|
155
|
+
processTag(result, currentTag, currentTagContent.join("\n").trim());
|
|
156
|
+
} else if (currentDescription.length > 0) {
|
|
157
|
+
result.description = currentDescription.join("\n").trim();
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
currentTag = tagMatch[1];
|
|
161
|
+
currentTagContent = tagMatch[2] ? [tagMatch[2]] : [];
|
|
162
|
+
} else if (currentTag) {
|
|
163
|
+
currentTagContent.push(trimmed);
|
|
164
|
+
} else if (trimmed) {
|
|
165
|
+
currentDescription.push(trimmed);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Process final tag
|
|
170
|
+
if (currentTag) {
|
|
171
|
+
processTag(result, currentTag, currentTagContent.join("\n").trim());
|
|
172
|
+
} else if (currentDescription.length > 0) {
|
|
173
|
+
result.description = currentDescription.join("\n").trim();
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return result;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Process a JSDoc tag
|
|
181
|
+
*/
|
|
182
|
+
function processTag(
|
|
183
|
+
docs: Partial<ExtractedDocs>,
|
|
184
|
+
tag: string,
|
|
185
|
+
content: string
|
|
186
|
+
): void {
|
|
187
|
+
switch (tag) {
|
|
188
|
+
case "description":
|
|
189
|
+
docs.description = content;
|
|
190
|
+
break;
|
|
191
|
+
|
|
192
|
+
case "param":
|
|
193
|
+
case "prop": {
|
|
194
|
+
const propMatch = content.match(
|
|
195
|
+
/^\{([^}]+)\}\s+(\[?\w+\]?)\s*-?\s*(.*)/
|
|
196
|
+
);
|
|
197
|
+
if (propMatch) {
|
|
198
|
+
const [, type, nameRaw, description] = propMatch;
|
|
199
|
+
const isOptional = nameRaw.startsWith("[");
|
|
200
|
+
const name = nameRaw.replace(/[\[\]]/g, "");
|
|
201
|
+
docs.props?.push({
|
|
202
|
+
name,
|
|
203
|
+
type,
|
|
204
|
+
description,
|
|
205
|
+
required: !isOptional,
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
break;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
case "example": {
|
|
212
|
+
docs.examples?.push(content);
|
|
213
|
+
break;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
case "deprecated":
|
|
217
|
+
case "since":
|
|
218
|
+
case "version":
|
|
219
|
+
case "see":
|
|
220
|
+
case "link":
|
|
221
|
+
case "category":
|
|
222
|
+
docs.tags = docs.tags ?? {};
|
|
223
|
+
docs.tags[tag] = content;
|
|
224
|
+
break;
|
|
225
|
+
|
|
226
|
+
default:
|
|
227
|
+
// Store other tags generically
|
|
228
|
+
docs.tags = docs.tags ?? {};
|
|
229
|
+
docs.tags[tag] = content;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Extract props from TypeScript interface
|
|
235
|
+
*/
|
|
236
|
+
function extractPropsFromInterface(
|
|
237
|
+
node: t.TSInterfaceDeclaration
|
|
238
|
+
): ExtractedDocs["props"] {
|
|
239
|
+
const props: ExtractedDocs["props"] = [];
|
|
240
|
+
|
|
241
|
+
for (const member of node.body.body) {
|
|
242
|
+
if (t.isTSPropertySignature(member) && t.isIdentifier(member.key)) {
|
|
243
|
+
const name = member.key.name;
|
|
244
|
+
const type = member.typeAnnotation
|
|
245
|
+
? typeAnnotationToString(member.typeAnnotation.typeAnnotation)
|
|
246
|
+
: "unknown";
|
|
247
|
+
const required = !member.optional;
|
|
248
|
+
|
|
249
|
+
// Extract description from leading comments
|
|
250
|
+
let description = "";
|
|
251
|
+
if (member.leadingComments) {
|
|
252
|
+
const comment = member.leadingComments[member.leadingComments.length - 1];
|
|
253
|
+
if (comment.type === "CommentBlock") {
|
|
254
|
+
description = comment.value
|
|
255
|
+
.replace(/^\s*\*\s?/gm, "")
|
|
256
|
+
.trim();
|
|
257
|
+
} else if (comment.type === "CommentLine") {
|
|
258
|
+
description = comment.value.trim();
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
props.push({ name, type, description, required });
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
return props;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Extract props from TypeScript type alias
|
|
271
|
+
*/
|
|
272
|
+
function extractPropsFromTypeAlias(
|
|
273
|
+
node: t.TSTypeAliasDeclaration
|
|
274
|
+
): ExtractedDocs["props"] {
|
|
275
|
+
const props: ExtractedDocs["props"] = [];
|
|
276
|
+
|
|
277
|
+
if (t.isTSTypeLiteral(node.typeAnnotation)) {
|
|
278
|
+
for (const member of node.typeAnnotation.members) {
|
|
279
|
+
if (t.isTSPropertySignature(member) && t.isIdentifier(member.key)) {
|
|
280
|
+
const name = member.key.name;
|
|
281
|
+
const type = member.typeAnnotation
|
|
282
|
+
? typeAnnotationToString(member.typeAnnotation.typeAnnotation)
|
|
283
|
+
: "unknown";
|
|
284
|
+
const required = !member.optional;
|
|
285
|
+
|
|
286
|
+
// Extract description from leading comments
|
|
287
|
+
let description = "";
|
|
288
|
+
if (member.leadingComments) {
|
|
289
|
+
const comment = member.leadingComments[member.leadingComments.length - 1];
|
|
290
|
+
description = comment.value.replace(/^\s*\*\s?/gm, "").trim();
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
props.push({ name, type, description, required });
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
return props;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Convert TypeScript type annotation to string representation
|
|
303
|
+
*/
|
|
304
|
+
function typeAnnotationToString(node: t.TSType): string {
|
|
305
|
+
if (t.isTSStringKeyword(node)) return "string";
|
|
306
|
+
if (t.isTSNumberKeyword(node)) return "number";
|
|
307
|
+
if (t.isTSBooleanKeyword(node)) return "boolean";
|
|
308
|
+
if (t.isTSNullKeyword(node)) return "null";
|
|
309
|
+
if (t.isTSUndefinedKeyword(node)) return "undefined";
|
|
310
|
+
if (t.isTSVoidKeyword(node)) return "void";
|
|
311
|
+
if (t.isTSAnyKeyword(node)) return "any";
|
|
312
|
+
if (t.isTSNeverKeyword(node)) return "never";
|
|
313
|
+
if (t.isTSUnknownKeyword(node)) return "unknown";
|
|
314
|
+
|
|
315
|
+
if (t.isTSLiteralType(node)) {
|
|
316
|
+
if (t.isStringLiteral(node.literal)) return `"${node.literal.value}"`;
|
|
317
|
+
if (t.isNumericLiteral(node.literal)) return String(node.literal.value);
|
|
318
|
+
if (t.isBooleanLiteral(node.literal)) return String(node.literal.value);
|
|
319
|
+
return "literal";
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
if (t.isTSTypeReference(node)) {
|
|
323
|
+
if (t.isIdentifier(node.typeName)) {
|
|
324
|
+
const name = node.typeName.name;
|
|
325
|
+
if (node.typeParameters) {
|
|
326
|
+
const params = node.typeParameters.params
|
|
327
|
+
.map(typeAnnotationToString)
|
|
328
|
+
.join(", ");
|
|
329
|
+
return `${name}<${params}>`;
|
|
330
|
+
}
|
|
331
|
+
return name;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
if (t.isTSUnionType(node)) {
|
|
336
|
+
return node.types.map(typeAnnotationToString).join(" | ");
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
if (t.isTSIntersectionType(node)) {
|
|
340
|
+
return node.types.map(typeAnnotationToString).join(" & ");
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
if (t.isTSArrayType(node)) {
|
|
344
|
+
return `${typeAnnotationToString(node.elementType)}[]`;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
if (t.isTSFunctionType(node)) {
|
|
348
|
+
return "(...args: any[]) => any";
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
if (t.isTSTupleType(node)) {
|
|
352
|
+
const elements = node.elementTypes
|
|
353
|
+
.map((el) => {
|
|
354
|
+
// Handle named tuple members: [name: string]
|
|
355
|
+
if (t.isTSNamedTupleMember(el)) {
|
|
356
|
+
return typeAnnotationToString(el.elementType);
|
|
357
|
+
}
|
|
358
|
+
return typeAnnotationToString(el);
|
|
359
|
+
})
|
|
360
|
+
.join(", ");
|
|
361
|
+
return `[${elements}]`;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
if (t.isTSTypeLiteral(node)) {
|
|
365
|
+
return "object";
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
return "unknown";
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* Check if interface name looks like props
|
|
373
|
+
*/
|
|
374
|
+
function isPropsInterface(name: string): boolean {
|
|
375
|
+
return (
|
|
376
|
+
name.endsWith("Props") ||
|
|
377
|
+
name.endsWith("Properties") ||
|
|
378
|
+
name === "Props"
|
|
379
|
+
);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Check if a function declaration is exported
|
|
384
|
+
*/
|
|
385
|
+
function isExportedComponent(
|
|
386
|
+
path: babel.NodePath<t.FunctionDeclaration>
|
|
387
|
+
): boolean {
|
|
388
|
+
const parent = path.parent;
|
|
389
|
+
if (t.isExportDefaultDeclaration(parent)) return true;
|
|
390
|
+
if (t.isExportNamedDeclaration(parent)) return true;
|
|
391
|
+
if (t.isProgram(parent) && path.node.id && /^[A-Z]/.test(path.node.id.name)) {
|
|
392
|
+
return true;
|
|
393
|
+
}
|
|
394
|
+
return false;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
/**
|
|
398
|
+
* Check if variable declaration has an exported arrow function component
|
|
399
|
+
*/
|
|
400
|
+
function hasExportedArrowFunction(
|
|
401
|
+
path: babel.NodePath<t.VariableDeclaration>
|
|
402
|
+
): boolean {
|
|
403
|
+
const parent = path.parent;
|
|
404
|
+
if (!t.isExportNamedDeclaration(parent)) return false;
|
|
405
|
+
|
|
406
|
+
for (const declarator of path.node.declarations) {
|
|
407
|
+
if (
|
|
408
|
+
t.isIdentifier(declarator.id) &&
|
|
409
|
+
/^[A-Z]/.test(declarator.id.name) &&
|
|
410
|
+
(t.isArrowFunctionExpression(declarator.init) ||
|
|
411
|
+
t.isFunctionExpression(declarator.init))
|
|
412
|
+
) {
|
|
413
|
+
return true;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
return false;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
/**
|
|
421
|
+
* Infer component name from file path
|
|
422
|
+
*/
|
|
423
|
+
function inferComponentName(filePath: string): string {
|
|
424
|
+
const fileName = basename(filePath);
|
|
425
|
+
// Remove extension
|
|
426
|
+
let name = fileName.replace(/\.(tsx?|jsx?)$/, "");
|
|
427
|
+
// Handle index files
|
|
428
|
+
if (name === "index") {
|
|
429
|
+
name = basename(dirname(filePath));
|
|
430
|
+
}
|
|
431
|
+
return name;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
/**
|
|
435
|
+
* Merge JSDoc results into docs
|
|
436
|
+
*/
|
|
437
|
+
function mergeJSDoc(docs: ExtractedDocs, jsdoc: Partial<ExtractedDocs>): void {
|
|
438
|
+
if (jsdoc.description && !docs.description) {
|
|
439
|
+
docs.description = jsdoc.description;
|
|
440
|
+
}
|
|
441
|
+
if (jsdoc.props) {
|
|
442
|
+
docs.props.push(...jsdoc.props);
|
|
443
|
+
}
|
|
444
|
+
if (jsdoc.examples) {
|
|
445
|
+
docs.examples.push(...jsdoc.examples);
|
|
446
|
+
}
|
|
447
|
+
if (jsdoc.tags) {
|
|
448
|
+
docs.tags = { ...docs.tags, ...jsdoc.tags };
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
/**
|
|
453
|
+
* Remove duplicate props
|
|
454
|
+
*/
|
|
455
|
+
function deduplicateProps(
|
|
456
|
+
props: ExtractedDocs["props"]
|
|
457
|
+
): ExtractedDocs["props"] {
|
|
458
|
+
const seen = new Map<string, ExtractedDocs["props"][0]>();
|
|
459
|
+
|
|
460
|
+
for (const prop of props) {
|
|
461
|
+
const existing = seen.get(prop.name);
|
|
462
|
+
if (!existing || (prop.description && !existing.description)) {
|
|
463
|
+
seen.set(prop.name, prop);
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
return Array.from(seen.values());
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
/**
|
|
471
|
+
* Find component source file from various patterns
|
|
472
|
+
*/
|
|
473
|
+
export async function findComponentSource(
|
|
474
|
+
componentName: string,
|
|
475
|
+
searchDirs: string[]
|
|
476
|
+
): Promise<string | null> {
|
|
477
|
+
const patterns = [
|
|
478
|
+
`${componentName}.tsx`,
|
|
479
|
+
`${componentName}.ts`,
|
|
480
|
+
`${componentName}/index.tsx`,
|
|
481
|
+
`${componentName}/index.ts`,
|
|
482
|
+
`${componentName}/${componentName}.tsx`,
|
|
483
|
+
`${componentName}/${componentName}.ts`,
|
|
484
|
+
];
|
|
485
|
+
|
|
486
|
+
for (const dir of searchDirs) {
|
|
487
|
+
for (const pattern of patterns) {
|
|
488
|
+
const fullPath = join(dir, pattern);
|
|
489
|
+
if (existsSync(fullPath)) {
|
|
490
|
+
return fullPath;
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
return null;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
/**
|
|
499
|
+
* Extract documentation from multiple component files
|
|
500
|
+
*/
|
|
501
|
+
export async function extractAllComponentDocs(
|
|
502
|
+
componentFiles: Map<string, string>
|
|
503
|
+
): Promise<Map<string, ExtractedDocs>> {
|
|
504
|
+
const results = new Map<string, ExtractedDocs>();
|
|
505
|
+
|
|
506
|
+
for (const [componentName, filePath] of componentFiles) {
|
|
507
|
+
try {
|
|
508
|
+
const docs = await extractComponentDocs(filePath);
|
|
509
|
+
docs.componentName = componentName;
|
|
510
|
+
results.set(componentName, docs);
|
|
511
|
+
} catch (error) {
|
|
512
|
+
console.warn(
|
|
513
|
+
`Failed to extract docs for ${componentName}:`,
|
|
514
|
+
(error as Error).message
|
|
515
|
+
);
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
return results;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
// Type import for babel NodePath
|
|
523
|
+
import type * as babel from "@babel/traverse";
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Enhancement Module
|
|
3
|
+
*
|
|
4
|
+
* AI-powered documentation generation through codebase analysis.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// Types
|
|
8
|
+
export type {
|
|
9
|
+
ComponentUsage,
|
|
10
|
+
UsageProps,
|
|
11
|
+
ComponentImport,
|
|
12
|
+
UsagePattern,
|
|
13
|
+
FileContext,
|
|
14
|
+
ComponentAnalysis,
|
|
15
|
+
UsageAnalysis,
|
|
16
|
+
FileCacheEntry,
|
|
17
|
+
AnalysisCache,
|
|
18
|
+
ScanProgress,
|
|
19
|
+
ScanOptions,
|
|
20
|
+
FileChanges,
|
|
21
|
+
ExtractedDocs,
|
|
22
|
+
ComponentEnhancement,
|
|
23
|
+
EnhancementResult,
|
|
24
|
+
} from "./types.js";
|
|
25
|
+
|
|
26
|
+
export { CACHE_VERSION } from "./types.js";
|
|
27
|
+
|
|
28
|
+
// Scanner
|
|
29
|
+
export {
|
|
30
|
+
scanFileForImports,
|
|
31
|
+
scanFileForUsages,
|
|
32
|
+
scanFile,
|
|
33
|
+
} from "./scanner.js";
|
|
34
|
+
|
|
35
|
+
// Aggregator
|
|
36
|
+
export {
|
|
37
|
+
aggregateComponentUsages,
|
|
38
|
+
aggregateAllUsages,
|
|
39
|
+
findCommonPropCombinations,
|
|
40
|
+
summarizePatternsForPrompt,
|
|
41
|
+
inferRelations,
|
|
42
|
+
inferAllRelations,
|
|
43
|
+
type ComponentRelation,
|
|
44
|
+
} from "./aggregator.js";
|
|
45
|
+
|
|
46
|
+
// Cache
|
|
47
|
+
export {
|
|
48
|
+
getCachePath,
|
|
49
|
+
loadCache,
|
|
50
|
+
saveCache,
|
|
51
|
+
createEmptyCache,
|
|
52
|
+
computeFileHash,
|
|
53
|
+
computeFileHashFromDisk,
|
|
54
|
+
isFileCached,
|
|
55
|
+
getCachedFile,
|
|
56
|
+
updateCacheFile,
|
|
57
|
+
removeCacheFile,
|
|
58
|
+
invalidateCache,
|
|
59
|
+
detectFileChanges,
|
|
60
|
+
getAllCachedImports,
|
|
61
|
+
getAllCachedUsages,
|
|
62
|
+
getCacheStats,
|
|
63
|
+
} from "./cache.js";
|
|
64
|
+
|
|
65
|
+
// Codebase Scanner
|
|
66
|
+
export {
|
|
67
|
+
scanCodebase,
|
|
68
|
+
incrementalScan,
|
|
69
|
+
getScanStats,
|
|
70
|
+
hasCachedAnalysis,
|
|
71
|
+
} from "./codebase-scanner.js";
|
|
72
|
+
|
|
73
|
+
// Documentation Extractor
|
|
74
|
+
export {
|
|
75
|
+
extractComponentDocs,
|
|
76
|
+
extractDocsFromSource,
|
|
77
|
+
findComponentSource,
|
|
78
|
+
extractAllComponentDocs,
|
|
79
|
+
} from "./doc-extractor.js";
|
|
80
|
+
|
|
81
|
+
// Storybook Parser
|
|
82
|
+
export {
|
|
83
|
+
parseStoryFile,
|
|
84
|
+
parseStorySource,
|
|
85
|
+
findStoryFiles,
|
|
86
|
+
parseAllStories,
|
|
87
|
+
mergeStorybookIntoDoc,
|
|
88
|
+
type StoryExample,
|
|
89
|
+
type StorybookMeta,
|
|
90
|
+
type ArgType,
|
|
91
|
+
type ParsedStoryFile,
|
|
92
|
+
} from "./storybook-parser.js";
|
|
93
|
+
|
|
94
|
+
// AI Context Generator
|
|
95
|
+
export {
|
|
96
|
+
generateComponentContext,
|
|
97
|
+
generateAIContextPackage,
|
|
98
|
+
generatePromptContext,
|
|
99
|
+
generateEnhancementSuggestions,
|
|
100
|
+
generateSystemPrompt,
|
|
101
|
+
generateUserPrompt,
|
|
102
|
+
isBoilerplate,
|
|
103
|
+
filterBoilerplate,
|
|
104
|
+
type ComponentContext,
|
|
105
|
+
type AIContextPackage,
|
|
106
|
+
type EnhancedProp,
|
|
107
|
+
} from "./context-generator.js";
|
|
108
|
+
|
|
109
|
+
// TypeScript Props Extractor
|
|
110
|
+
export {
|
|
111
|
+
extractPropsFromFile,
|
|
112
|
+
extractPropsFromSource,
|
|
113
|
+
extractPropsForComponent,
|
|
114
|
+
extractAllComponentProps,
|
|
115
|
+
convertToSegmentProps,
|
|
116
|
+
type ExtractedProp,
|
|
117
|
+
type PropsExtractionResult,
|
|
118
|
+
type PropsExtractionOptions,
|
|
119
|
+
} from "./props-extractor.js";
|
|
120
|
+
|
|
121
|
+
// Variant Renderer (Playwright-based)
|
|
122
|
+
export {
|
|
123
|
+
renderVariants,
|
|
124
|
+
renderAllComponentVariants,
|
|
125
|
+
checkStorybookRunning,
|
|
126
|
+
getStorybookStoryIds,
|
|
127
|
+
shutdownSharedPool,
|
|
128
|
+
type RenderedVariant,
|
|
129
|
+
type VariantRenderResult,
|
|
130
|
+
type VariantRenderOptions,
|
|
131
|
+
} from "./variant-renderer.js";
|