@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,1489 @@
|
|
|
1
|
+
import { createRequire } from 'module'; const require = createRequire(import.meta.url);
|
|
2
|
+
import {
|
|
3
|
+
BRAND,
|
|
4
|
+
segmentsConfigSchema
|
|
5
|
+
} from "./chunk-XHNKNI6J.js";
|
|
6
|
+
|
|
7
|
+
// src/core/config.ts
|
|
8
|
+
import { existsSync } from "fs";
|
|
9
|
+
import { resolve, dirname } from "path";
|
|
10
|
+
import { createJiti } from "jiti";
|
|
11
|
+
var DEFAULT_CONFIG = {
|
|
12
|
+
include: [
|
|
13
|
+
`src/**/*${BRAND.fileExtension}`,
|
|
14
|
+
// *.segment.tsx files
|
|
15
|
+
"src/**/*.stories.tsx"
|
|
16
|
+
// Storybook stories (auto-converted)
|
|
17
|
+
],
|
|
18
|
+
exclude: ["**/node_modules/**"],
|
|
19
|
+
components: ["src/**/index.tsx", "src/**/*.tsx"],
|
|
20
|
+
framework: "react"
|
|
21
|
+
};
|
|
22
|
+
function findConfigFile(startDir = process.cwd()) {
|
|
23
|
+
let currentDir = startDir;
|
|
24
|
+
while (currentDir !== dirname(currentDir)) {
|
|
25
|
+
const configPath = resolve(currentDir, BRAND.configFile);
|
|
26
|
+
if (existsSync(configPath)) {
|
|
27
|
+
return configPath;
|
|
28
|
+
}
|
|
29
|
+
const legacyConfigPath = resolve(currentDir, BRAND.legacyConfigFile);
|
|
30
|
+
if (existsSync(legacyConfigPath)) {
|
|
31
|
+
return legacyConfigPath;
|
|
32
|
+
}
|
|
33
|
+
currentDir = dirname(currentDir);
|
|
34
|
+
}
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
async function loadConfig(configPath) {
|
|
38
|
+
const resolvedPath = configPath ?? findConfigFile();
|
|
39
|
+
if (!resolvedPath) {
|
|
40
|
+
return {
|
|
41
|
+
config: DEFAULT_CONFIG,
|
|
42
|
+
configDir: process.cwd()
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
try {
|
|
46
|
+
const jiti = createJiti(import.meta.url, {
|
|
47
|
+
interopDefault: true
|
|
48
|
+
});
|
|
49
|
+
const rawConfig = await jiti.import(resolvedPath);
|
|
50
|
+
const result = segmentsConfigSchema.safeParse(rawConfig);
|
|
51
|
+
if (!result.success) {
|
|
52
|
+
const errors = result.error.errors.map((e) => ` - ${e.path.join(".")}: ${e.message}`).join("\n");
|
|
53
|
+
throw new Error(`Invalid config in ${resolvedPath}:
|
|
54
|
+
${errors}`);
|
|
55
|
+
}
|
|
56
|
+
return {
|
|
57
|
+
config: { ...DEFAULT_CONFIG, ...result.data },
|
|
58
|
+
configDir: dirname(resolvedPath)
|
|
59
|
+
};
|
|
60
|
+
} catch (error) {
|
|
61
|
+
if (error instanceof Error && error.message.includes("Invalid config")) {
|
|
62
|
+
throw error;
|
|
63
|
+
}
|
|
64
|
+
throw new Error(`Failed to load config from ${resolvedPath}: ${error}`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// src/core/discovery.ts
|
|
69
|
+
import { resolve as resolve2, dirname as dirname2 } from "path";
|
|
70
|
+
import { readFile } from "fs/promises";
|
|
71
|
+
import { existsSync as existsSync2 } from "fs";
|
|
72
|
+
import fg from "fast-glob";
|
|
73
|
+
async function discoverRecipeFiles(configDir, exclude) {
|
|
74
|
+
const pattern = `**/*${BRAND.recipeFileExtension}`;
|
|
75
|
+
const files = await fg(pattern, {
|
|
76
|
+
cwd: configDir,
|
|
77
|
+
ignore: exclude ?? ["**/node_modules/**", "**/dist/**"],
|
|
78
|
+
absolute: false
|
|
79
|
+
});
|
|
80
|
+
return files.map((relativePath) => ({
|
|
81
|
+
relativePath,
|
|
82
|
+
absolutePath: resolve2(configDir, relativePath)
|
|
83
|
+
}));
|
|
84
|
+
}
|
|
85
|
+
async function discoverSegmentFiles(config, configDir) {
|
|
86
|
+
const files = await fg(config.include, {
|
|
87
|
+
cwd: configDir,
|
|
88
|
+
ignore: config.exclude ?? [],
|
|
89
|
+
absolute: false
|
|
90
|
+
});
|
|
91
|
+
return files.map((relativePath) => ({
|
|
92
|
+
relativePath,
|
|
93
|
+
absolutePath: resolve2(configDir, relativePath)
|
|
94
|
+
}));
|
|
95
|
+
}
|
|
96
|
+
async function discoverComponentFiles(config, configDir) {
|
|
97
|
+
if (!config.components || config.components.length === 0) {
|
|
98
|
+
return [];
|
|
99
|
+
}
|
|
100
|
+
const files = await fg(config.components, {
|
|
101
|
+
cwd: configDir,
|
|
102
|
+
ignore: [
|
|
103
|
+
...config.exclude ?? [],
|
|
104
|
+
// Exclude segment files themselves
|
|
105
|
+
...config.include,
|
|
106
|
+
// Exclude test files
|
|
107
|
+
"**/*.test.*",
|
|
108
|
+
"**/*.spec.*",
|
|
109
|
+
"**/__tests__/**"
|
|
110
|
+
],
|
|
111
|
+
absolute: false
|
|
112
|
+
});
|
|
113
|
+
return files.map((relativePath) => ({
|
|
114
|
+
relativePath,
|
|
115
|
+
absolutePath: resolve2(configDir, relativePath)
|
|
116
|
+
}));
|
|
117
|
+
}
|
|
118
|
+
function extractComponentName(filePath) {
|
|
119
|
+
const parts = filePath.replace(/\\/g, "/").split("/");
|
|
120
|
+
const fileName = parts[parts.length - 1];
|
|
121
|
+
if (fileName === "index.tsx" || fileName === "index.ts") {
|
|
122
|
+
return parts[parts.length - 2] ?? "Unknown";
|
|
123
|
+
}
|
|
124
|
+
return fileName.replace(/\.(tsx?|jsx?)$/, "");
|
|
125
|
+
}
|
|
126
|
+
var DEFAULT_COMPONENT_PATTERNS = [
|
|
127
|
+
"src/components/**/*.tsx",
|
|
128
|
+
"src/components/**/index.tsx",
|
|
129
|
+
"components/**/*.tsx",
|
|
130
|
+
"lib/components/**/*.tsx",
|
|
131
|
+
"packages/*/src/components/**/*.tsx"
|
|
132
|
+
];
|
|
133
|
+
var DEFAULT_EXCLUDE_PATTERNS = [
|
|
134
|
+
"**/*.test.*",
|
|
135
|
+
"**/*.spec.*",
|
|
136
|
+
"**/*.stories.*",
|
|
137
|
+
"**/*.story.*",
|
|
138
|
+
"**/__tests__/**",
|
|
139
|
+
"**/__mocks__/**",
|
|
140
|
+
"**/node_modules/**",
|
|
141
|
+
"**/dist/**"
|
|
142
|
+
];
|
|
143
|
+
async function discoverComponentsFromSource(configDir, patterns, exclude) {
|
|
144
|
+
const searchPatterns = patterns && patterns.length > 0 ? patterns : DEFAULT_COMPONENT_PATTERNS;
|
|
145
|
+
const excludePatterns = [
|
|
146
|
+
...DEFAULT_EXCLUDE_PATTERNS,
|
|
147
|
+
...exclude ?? []
|
|
148
|
+
];
|
|
149
|
+
const files = await fg(searchPatterns, {
|
|
150
|
+
cwd: configDir,
|
|
151
|
+
ignore: excludePatterns,
|
|
152
|
+
absolute: false
|
|
153
|
+
});
|
|
154
|
+
const componentFiles = files.filter((file) => {
|
|
155
|
+
const name = extractComponentName(file);
|
|
156
|
+
return /^[A-Z]/.test(name);
|
|
157
|
+
});
|
|
158
|
+
const storyPatterns = [
|
|
159
|
+
"**/*.stories.tsx",
|
|
160
|
+
"**/*.stories.ts",
|
|
161
|
+
"**/*.story.tsx",
|
|
162
|
+
"**/*.story.ts"
|
|
163
|
+
];
|
|
164
|
+
const storyFiles = await fg(storyPatterns, {
|
|
165
|
+
cwd: configDir,
|
|
166
|
+
ignore: ["**/node_modules/**", "**/dist/**"],
|
|
167
|
+
absolute: false
|
|
168
|
+
});
|
|
169
|
+
const storyMap = /* @__PURE__ */ new Map();
|
|
170
|
+
for (const storyFile of storyFiles) {
|
|
171
|
+
const name = extractComponentName(storyFile.replace(/\.stories?\.(tsx?|jsx?)$/, ".tsx"));
|
|
172
|
+
storyMap.set(name, storyFile);
|
|
173
|
+
}
|
|
174
|
+
const components = [];
|
|
175
|
+
for (const file of componentFiles) {
|
|
176
|
+
const name = extractComponentName(file);
|
|
177
|
+
const absolutePath = resolve2(configDir, file);
|
|
178
|
+
const storyFile = storyMap.get(name);
|
|
179
|
+
components.push({
|
|
180
|
+
name,
|
|
181
|
+
sourcePath: absolutePath,
|
|
182
|
+
relativePath: file,
|
|
183
|
+
storyPath: storyFile ? resolve2(configDir, storyFile) : void 0
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
components.sort((a, b) => a.name.localeCompare(b.name));
|
|
187
|
+
return components;
|
|
188
|
+
}
|
|
189
|
+
async function discoverComponentsFromBarrel(barrelPath, configDir) {
|
|
190
|
+
const absoluteBarrelPath = resolve2(configDir, barrelPath);
|
|
191
|
+
if (!existsSync2(absoluteBarrelPath)) {
|
|
192
|
+
return [];
|
|
193
|
+
}
|
|
194
|
+
const content = await readFile(absoluteBarrelPath, "utf-8");
|
|
195
|
+
const components = [];
|
|
196
|
+
const exportRegex = /export\s+(?:\*|{([^}]+)})\s+from\s+['"]([^'"]+)['"]/g;
|
|
197
|
+
let match;
|
|
198
|
+
while ((match = exportRegex.exec(content)) !== null) {
|
|
199
|
+
const exportedNames = match[1];
|
|
200
|
+
const importPath = match[2];
|
|
201
|
+
const barrelDir = dirname2(absoluteBarrelPath);
|
|
202
|
+
let resolvedPath = resolve2(barrelDir, importPath);
|
|
203
|
+
if (!resolvedPath.endsWith(".tsx") && !resolvedPath.endsWith(".ts")) {
|
|
204
|
+
if (existsSync2(`${resolvedPath}.tsx`)) {
|
|
205
|
+
resolvedPath = `${resolvedPath}.tsx`;
|
|
206
|
+
} else if (existsSync2(`${resolvedPath}.ts`)) {
|
|
207
|
+
resolvedPath = `${resolvedPath}.ts`;
|
|
208
|
+
} else if (existsSync2(`${resolvedPath}/index.tsx`)) {
|
|
209
|
+
resolvedPath = `${resolvedPath}/index.tsx`;
|
|
210
|
+
} else if (existsSync2(`${resolvedPath}/index.ts`)) {
|
|
211
|
+
resolvedPath = `${resolvedPath}/index.ts`;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
if (!existsSync2(resolvedPath)) {
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
217
|
+
if (exportedNames) {
|
|
218
|
+
const names = exportedNames.split(",").map((n) => n.trim().split(/\s+as\s+/)[0].trim());
|
|
219
|
+
for (const name of names) {
|
|
220
|
+
if (/^[A-Z]/.test(name)) {
|
|
221
|
+
const relativePath = resolvedPath.replace(configDir + "/", "");
|
|
222
|
+
components.push({
|
|
223
|
+
name,
|
|
224
|
+
sourcePath: resolvedPath,
|
|
225
|
+
relativePath
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
} else {
|
|
230
|
+
const name = extractComponentName(importPath);
|
|
231
|
+
if (/^[A-Z]/.test(name)) {
|
|
232
|
+
const relativePath = resolvedPath.replace(configDir + "/", "");
|
|
233
|
+
components.push({
|
|
234
|
+
name,
|
|
235
|
+
sourcePath: resolvedPath,
|
|
236
|
+
relativePath
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
return components;
|
|
242
|
+
}
|
|
243
|
+
async function discoverAllComponents(configDir, options = {}) {
|
|
244
|
+
const componentsMap = /* @__PURE__ */ new Map();
|
|
245
|
+
const sourceComponents = await discoverComponentsFromSource(
|
|
246
|
+
configDir,
|
|
247
|
+
options.patterns,
|
|
248
|
+
options.exclude
|
|
249
|
+
);
|
|
250
|
+
for (const comp of sourceComponents) {
|
|
251
|
+
componentsMap.set(comp.name, comp);
|
|
252
|
+
}
|
|
253
|
+
if (options.barrelFiles && options.barrelFiles.length > 0) {
|
|
254
|
+
for (const barrelFile of options.barrelFiles) {
|
|
255
|
+
const barrelComponents = await discoverComponentsFromBarrel(barrelFile, configDir);
|
|
256
|
+
for (const comp of barrelComponents) {
|
|
257
|
+
if (!componentsMap.has(comp.name)) {
|
|
258
|
+
componentsMap.set(comp.name, comp);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
return Array.from(componentsMap.values()).sort((a, b) => a.name.localeCompare(b.name));
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// src/core/generators/typescript-extractor.ts
|
|
267
|
+
import ts from "typescript";
|
|
268
|
+
import { readFileSync } from "fs";
|
|
269
|
+
function extractPropsFromFile(filePath) {
|
|
270
|
+
const sourceText = readFileSync(filePath, "utf-8");
|
|
271
|
+
return extractPropsFromSource(sourceText, filePath);
|
|
272
|
+
}
|
|
273
|
+
function extractPropsFromSource(sourceText, fileName = "component.tsx") {
|
|
274
|
+
const sourceFile = ts.createSourceFile(
|
|
275
|
+
fileName,
|
|
276
|
+
sourceText,
|
|
277
|
+
ts.ScriptTarget.Latest,
|
|
278
|
+
true,
|
|
279
|
+
fileName.endsWith(".tsx") ? ts.ScriptKind.TSX : ts.ScriptKind.TS
|
|
280
|
+
);
|
|
281
|
+
const result = {
|
|
282
|
+
componentName: "",
|
|
283
|
+
props: {},
|
|
284
|
+
exports: [],
|
|
285
|
+
imports: []
|
|
286
|
+
};
|
|
287
|
+
const propsInterfaces = /* @__PURE__ */ new Map();
|
|
288
|
+
const componentExports = [];
|
|
289
|
+
const importedModules = [];
|
|
290
|
+
ts.forEachChild(sourceFile, (node) => {
|
|
291
|
+
if (ts.isImportDeclaration(node)) {
|
|
292
|
+
const moduleSpecifier = node.moduleSpecifier;
|
|
293
|
+
if (ts.isStringLiteral(moduleSpecifier)) {
|
|
294
|
+
importedModules.push(moduleSpecifier.text);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
if (ts.isInterfaceDeclaration(node)) {
|
|
298
|
+
const name = node.name.text;
|
|
299
|
+
if (name.endsWith("Props")) {
|
|
300
|
+
propsInterfaces.set(name, node);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
if (ts.isTypeAliasDeclaration(node)) {
|
|
304
|
+
const name = node.name.text;
|
|
305
|
+
if (name.endsWith("Props")) {
|
|
306
|
+
propsInterfaces.set(name, node);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
if (ts.isFunctionDeclaration(node) && node.name) {
|
|
310
|
+
const hasExport = node.modifiers?.some(
|
|
311
|
+
(m) => m.kind === ts.SyntaxKind.ExportKeyword
|
|
312
|
+
);
|
|
313
|
+
if (hasExport) {
|
|
314
|
+
componentExports.push(node.name.text);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
if (ts.isVariableStatement(node)) {
|
|
318
|
+
const hasExport = node.modifiers?.some(
|
|
319
|
+
(m) => m.kind === ts.SyntaxKind.ExportKeyword
|
|
320
|
+
);
|
|
321
|
+
if (hasExport) {
|
|
322
|
+
for (const decl of node.declarationList.declarations) {
|
|
323
|
+
if (ts.isIdentifier(decl.name)) {
|
|
324
|
+
componentExports.push(decl.name.text);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
if (ts.isExportDeclaration(node) && node.exportClause) {
|
|
330
|
+
if (ts.isNamedExports(node.exportClause)) {
|
|
331
|
+
for (const element of node.exportClause.elements) {
|
|
332
|
+
componentExports.push(element.name.text);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
});
|
|
337
|
+
result.exports = componentExports;
|
|
338
|
+
result.imports = importedModules;
|
|
339
|
+
const mainComponent = componentExports.find(
|
|
340
|
+
(name) => /^[A-Z]/.test(name) && !name.endsWith("Props")
|
|
341
|
+
);
|
|
342
|
+
if (!mainComponent) {
|
|
343
|
+
return null;
|
|
344
|
+
}
|
|
345
|
+
result.componentName = mainComponent;
|
|
346
|
+
const propsInterfaceName = `${mainComponent}Props`;
|
|
347
|
+
const propsInterface = propsInterfaces.get(propsInterfaceName);
|
|
348
|
+
if (propsInterface) {
|
|
349
|
+
result.propsInterfaceName = propsInterfaceName;
|
|
350
|
+
result.props = extractPropsFromInterface(propsInterface, sourceFile);
|
|
351
|
+
}
|
|
352
|
+
return result;
|
|
353
|
+
}
|
|
354
|
+
function extractPropsFromInterface(node, sourceFile) {
|
|
355
|
+
const props = {};
|
|
356
|
+
if (ts.isInterfaceDeclaration(node)) {
|
|
357
|
+
for (const member of node.members) {
|
|
358
|
+
if (ts.isPropertySignature(member) && member.name) {
|
|
359
|
+
const propName = member.name.getText(sourceFile);
|
|
360
|
+
const prop = extractPropFromMember(member, sourceFile);
|
|
361
|
+
if (prop) {
|
|
362
|
+
props[propName] = prop;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
if (ts.isTypeAliasDeclaration(node)) {
|
|
368
|
+
const typeNode = node.type;
|
|
369
|
+
if (ts.isTypeLiteralNode(typeNode)) {
|
|
370
|
+
for (const member of typeNode.members) {
|
|
371
|
+
if (ts.isPropertySignature(member) && member.name) {
|
|
372
|
+
const propName = member.name.getText(sourceFile);
|
|
373
|
+
const prop = extractPropFromMember(member, sourceFile);
|
|
374
|
+
if (prop) {
|
|
375
|
+
props[propName] = prop;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
return props;
|
|
382
|
+
}
|
|
383
|
+
function extractPropFromMember(member, sourceFile) {
|
|
384
|
+
const entry = {};
|
|
385
|
+
entry.required = !member.questionToken;
|
|
386
|
+
if (member.type) {
|
|
387
|
+
const typeInfo = parseTypeNode(member.type, sourceFile);
|
|
388
|
+
entry.type = typeInfo.type;
|
|
389
|
+
entry.typeKind = typeInfo.typeKind;
|
|
390
|
+
if (typeInfo.options) {
|
|
391
|
+
entry.options = typeInfo.options;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
const jsDocComment = getJSDocComment(member);
|
|
395
|
+
if (jsDocComment) {
|
|
396
|
+
entry.description = jsDocComment;
|
|
397
|
+
}
|
|
398
|
+
const defaultValue = getJSDocDefault(member);
|
|
399
|
+
if (defaultValue !== void 0) {
|
|
400
|
+
entry.default = defaultValue;
|
|
401
|
+
}
|
|
402
|
+
return entry;
|
|
403
|
+
}
|
|
404
|
+
function parseTypeNode(typeNode, sourceFile) {
|
|
405
|
+
if (typeNode.kind === ts.SyntaxKind.StringKeyword) {
|
|
406
|
+
return { type: "string", typeKind: "string" };
|
|
407
|
+
}
|
|
408
|
+
if (typeNode.kind === ts.SyntaxKind.NumberKeyword) {
|
|
409
|
+
return { type: "number", typeKind: "number" };
|
|
410
|
+
}
|
|
411
|
+
if (typeNode.kind === ts.SyntaxKind.BooleanKeyword) {
|
|
412
|
+
return { type: "boolean", typeKind: "boolean" };
|
|
413
|
+
}
|
|
414
|
+
if (ts.isUnionTypeNode(typeNode)) {
|
|
415
|
+
const options = [];
|
|
416
|
+
let allLiterals = true;
|
|
417
|
+
for (const subType of typeNode.types) {
|
|
418
|
+
if (ts.isLiteralTypeNode(subType)) {
|
|
419
|
+
if (ts.isStringLiteral(subType.literal)) {
|
|
420
|
+
options.push(subType.literal.text);
|
|
421
|
+
} else if (subType.literal.kind === ts.SyntaxKind.TrueKeyword) {
|
|
422
|
+
options.push("true");
|
|
423
|
+
} else if (subType.literal.kind === ts.SyntaxKind.FalseKeyword) {
|
|
424
|
+
options.push("false");
|
|
425
|
+
} else {
|
|
426
|
+
allLiterals = false;
|
|
427
|
+
}
|
|
428
|
+
} else {
|
|
429
|
+
allLiterals = false;
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
if (allLiterals && options.length > 0) {
|
|
433
|
+
return {
|
|
434
|
+
type: options.map((o) => `"${o}"`).join(" | "),
|
|
435
|
+
typeKind: "enum",
|
|
436
|
+
options
|
|
437
|
+
};
|
|
438
|
+
}
|
|
439
|
+
return {
|
|
440
|
+
type: typeNode.getText(sourceFile),
|
|
441
|
+
typeKind: "union"
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
if (ts.isFunctionTypeNode(typeNode)) {
|
|
445
|
+
return {
|
|
446
|
+
type: typeNode.getText(sourceFile),
|
|
447
|
+
typeKind: "function"
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
if (ts.isArrayTypeNode(typeNode)) {
|
|
451
|
+
return {
|
|
452
|
+
type: typeNode.getText(sourceFile),
|
|
453
|
+
typeKind: "array"
|
|
454
|
+
};
|
|
455
|
+
}
|
|
456
|
+
if (ts.isTypeReferenceNode(typeNode)) {
|
|
457
|
+
const typeName = typeNode.typeName.getText(sourceFile);
|
|
458
|
+
if (typeName === "ReactNode" || typeName === "React.ReactNode") {
|
|
459
|
+
return { type: "ReactNode", typeKind: "node" };
|
|
460
|
+
}
|
|
461
|
+
if (typeName === "ReactElement" || typeName === "React.ReactElement") {
|
|
462
|
+
return { type: "ReactElement", typeKind: "element" };
|
|
463
|
+
}
|
|
464
|
+
if (typeName === "JSX.Element") {
|
|
465
|
+
return { type: "JSX.Element", typeKind: "element" };
|
|
466
|
+
}
|
|
467
|
+
return {
|
|
468
|
+
type: typeNode.getText(sourceFile),
|
|
469
|
+
typeKind: "object"
|
|
470
|
+
};
|
|
471
|
+
}
|
|
472
|
+
if (ts.isTypeLiteralNode(typeNode)) {
|
|
473
|
+
return {
|
|
474
|
+
type: typeNode.getText(sourceFile),
|
|
475
|
+
typeKind: "object"
|
|
476
|
+
};
|
|
477
|
+
}
|
|
478
|
+
return {
|
|
479
|
+
type: typeNode.getText(sourceFile),
|
|
480
|
+
typeKind: "unknown"
|
|
481
|
+
};
|
|
482
|
+
}
|
|
483
|
+
function getJSDocComment(node) {
|
|
484
|
+
const jsDocTags = ts.getJSDocTags(node);
|
|
485
|
+
const jsDoc = node.jsDoc;
|
|
486
|
+
if (jsDoc && jsDoc.length > 0) {
|
|
487
|
+
const comment = jsDoc[0].comment;
|
|
488
|
+
if (typeof comment === "string") {
|
|
489
|
+
return comment;
|
|
490
|
+
}
|
|
491
|
+
if (Array.isArray(comment)) {
|
|
492
|
+
return comment.map((c) => typeof c === "string" ? c : c.text).join("");
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
return void 0;
|
|
496
|
+
}
|
|
497
|
+
function getJSDocDefault(node) {
|
|
498
|
+
const jsDocTags = ts.getJSDocTags(node);
|
|
499
|
+
for (const tag of jsDocTags) {
|
|
500
|
+
if (tag.tagName.text === "default") {
|
|
501
|
+
const comment = tag.comment;
|
|
502
|
+
if (typeof comment === "string") {
|
|
503
|
+
try {
|
|
504
|
+
return JSON.parse(comment);
|
|
505
|
+
} catch {
|
|
506
|
+
return comment;
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
return void 0;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
// src/core/generators/registry.ts
|
|
515
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
516
|
+
import { relative, dirname as dirname3, basename as basename2, join } from "path";
|
|
517
|
+
import fg2 from "fast-glob";
|
|
518
|
+
async function generateRegistry(options) {
|
|
519
|
+
const {
|
|
520
|
+
projectRoot,
|
|
521
|
+
componentPatterns = ["src/**/*.tsx", "src/**/*.ts"],
|
|
522
|
+
storyPatterns = ["src/**/*.stories.tsx", "src/**/*.stories.ts"],
|
|
523
|
+
fragmentsDir = join(projectRoot, BRAND.dataDir),
|
|
524
|
+
registryOptions = {}
|
|
525
|
+
} = options;
|
|
526
|
+
const {
|
|
527
|
+
requireStory = false,
|
|
528
|
+
publicOnly = false,
|
|
529
|
+
categoryDepth = 1,
|
|
530
|
+
includeProps = false,
|
|
531
|
+
embedFragments = false
|
|
532
|
+
} = registryOptions;
|
|
533
|
+
const errors = [];
|
|
534
|
+
const warnings = [];
|
|
535
|
+
const storyFiles = await fg2(storyPatterns, {
|
|
536
|
+
cwd: projectRoot,
|
|
537
|
+
ignore: ["**/node_modules/**"],
|
|
538
|
+
absolute: true
|
|
539
|
+
});
|
|
540
|
+
const storyMap = /* @__PURE__ */ new Map();
|
|
541
|
+
for (const storyPath of storyFiles) {
|
|
542
|
+
const storyDir = dirname3(storyPath);
|
|
543
|
+
const storyBase = basename2(storyPath).replace(/\.stories\.(tsx?|jsx?)$/, "");
|
|
544
|
+
storyMap.set(`${storyDir}/${storyBase}`, storyPath);
|
|
545
|
+
}
|
|
546
|
+
const componentFiles = await fg2(componentPatterns, {
|
|
547
|
+
cwd: projectRoot,
|
|
548
|
+
ignore: [
|
|
549
|
+
"**/node_modules/**",
|
|
550
|
+
"**/*.stories.*",
|
|
551
|
+
"**/*.test.*",
|
|
552
|
+
"**/*.spec.*",
|
|
553
|
+
"**/*.d.ts"
|
|
554
|
+
],
|
|
555
|
+
absolute: true
|
|
556
|
+
});
|
|
557
|
+
const fragmentPattern = join(
|
|
558
|
+
BRAND.dataDir,
|
|
559
|
+
BRAND.componentsDir,
|
|
560
|
+
`*${BRAND.fileExtension}`
|
|
561
|
+
);
|
|
562
|
+
const fragmentFiles = await fg2(fragmentPattern, {
|
|
563
|
+
cwd: projectRoot,
|
|
564
|
+
absolute: true
|
|
565
|
+
});
|
|
566
|
+
const fragmentMap = /* @__PURE__ */ new Map();
|
|
567
|
+
for (const fragmentPath of fragmentFiles) {
|
|
568
|
+
const fragmentName = basename2(fragmentPath).replace(BRAND.fileExtension, "");
|
|
569
|
+
try {
|
|
570
|
+
const content = readFileSync2(fragmentPath, "utf-8");
|
|
571
|
+
const fragment = JSON.parse(content);
|
|
572
|
+
fragmentMap.set(fragmentName, {
|
|
573
|
+
path: relative(projectRoot, fragmentPath),
|
|
574
|
+
fragment
|
|
575
|
+
});
|
|
576
|
+
} catch (e) {
|
|
577
|
+
errors.push({
|
|
578
|
+
file: fragmentPath,
|
|
579
|
+
error: `Failed to parse fragment: ${e instanceof Error ? e.message : String(e)}`
|
|
580
|
+
});
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
const components = {};
|
|
584
|
+
const indexComponents = {};
|
|
585
|
+
const categories = {};
|
|
586
|
+
for (const filePath of componentFiles) {
|
|
587
|
+
try {
|
|
588
|
+
const extracted = extractPropsFromFile(filePath);
|
|
589
|
+
if (!extracted || !extracted.componentName) {
|
|
590
|
+
continue;
|
|
591
|
+
}
|
|
592
|
+
const componentName = extracted.componentName;
|
|
593
|
+
const relativePath = relative(projectRoot, filePath);
|
|
594
|
+
if (publicOnly && !extracted.exports.includes(componentName)) {
|
|
595
|
+
continue;
|
|
596
|
+
}
|
|
597
|
+
const componentDir = dirname3(filePath);
|
|
598
|
+
const baseNameWithoutExt = basename2(filePath).replace(/\.(tsx?|jsx?)$/, "");
|
|
599
|
+
const storyPath = storyMap.get(`${componentDir}/${baseNameWithoutExt}`);
|
|
600
|
+
if (requireStory && !storyPath) {
|
|
601
|
+
continue;
|
|
602
|
+
}
|
|
603
|
+
const fragmentData = fragmentMap.get(componentName);
|
|
604
|
+
const category = fragmentData?.fragment?.meta?.status ? void 0 : getCategoryFromPath(relativePath, categoryDepth);
|
|
605
|
+
const hasEnrichment = fragmentData ? hasRealEnrichment(fragmentData.fragment) : false;
|
|
606
|
+
const entry = {
|
|
607
|
+
path: relativePath
|
|
608
|
+
};
|
|
609
|
+
if (storyPath) {
|
|
610
|
+
entry.storyPath = relative(projectRoot, storyPath);
|
|
611
|
+
}
|
|
612
|
+
if (fragmentData) {
|
|
613
|
+
entry.fragmentPath = fragmentData.path;
|
|
614
|
+
if (hasEnrichment) {
|
|
615
|
+
entry.hasEnrichment = true;
|
|
616
|
+
}
|
|
617
|
+
if (fragmentData.fragment.description) {
|
|
618
|
+
entry.description = fragmentData.fragment.description;
|
|
619
|
+
}
|
|
620
|
+
if (fragmentData.fragment.meta?.status) {
|
|
621
|
+
entry.status = fragmentData.fragment.meta.status;
|
|
622
|
+
}
|
|
623
|
+
if (embedFragments) {
|
|
624
|
+
entry.fragment = fragmentData.fragment;
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
if (category) {
|
|
628
|
+
entry.category = category;
|
|
629
|
+
if (!categories[category]) {
|
|
630
|
+
categories[category] = [];
|
|
631
|
+
}
|
|
632
|
+
categories[category].push(componentName);
|
|
633
|
+
}
|
|
634
|
+
if (includeProps && extracted.props) {
|
|
635
|
+
entry.props = extracted.props;
|
|
636
|
+
}
|
|
637
|
+
if (extracted.exports.length > 1) {
|
|
638
|
+
entry.exports = extracted.exports;
|
|
639
|
+
}
|
|
640
|
+
components[componentName] = entry;
|
|
641
|
+
indexComponents[componentName] = relativePath;
|
|
642
|
+
} catch (e) {
|
|
643
|
+
errors.push({
|
|
644
|
+
file: filePath,
|
|
645
|
+
error: `Failed to extract component: ${e instanceof Error ? e.message : String(e)}`
|
|
646
|
+
});
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
const componentCount = Object.keys(components).length;
|
|
650
|
+
const registry = {
|
|
651
|
+
$schema: "https://fragments.dev/schema/registry-v1.json",
|
|
652
|
+
version: "1.0",
|
|
653
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
654
|
+
componentCount,
|
|
655
|
+
components,
|
|
656
|
+
categories
|
|
657
|
+
};
|
|
658
|
+
const index = {
|
|
659
|
+
version: "1.0",
|
|
660
|
+
generatedAt: registry.generatedAt,
|
|
661
|
+
components: indexComponents,
|
|
662
|
+
categories
|
|
663
|
+
};
|
|
664
|
+
return { registry, index, errors, warnings };
|
|
665
|
+
}
|
|
666
|
+
function getCategoryFromPath(relativePath, depth = 1) {
|
|
667
|
+
const parts = relativePath.split("/");
|
|
668
|
+
const componentsIndex = parts.findIndex((p) => p === "components");
|
|
669
|
+
if (componentsIndex === -1) {
|
|
670
|
+
return void 0;
|
|
671
|
+
}
|
|
672
|
+
const afterComponents = parts.slice(componentsIndex + 1);
|
|
673
|
+
if (afterComponents.length <= 1) {
|
|
674
|
+
return void 0;
|
|
675
|
+
}
|
|
676
|
+
const folderName = afterComponents[0];
|
|
677
|
+
const fileName = afterComponents[afterComponents.length - 1].replace(/\.(tsx?|jsx?)$/, "");
|
|
678
|
+
if (afterComponents.length === 2 && folderName === fileName) {
|
|
679
|
+
return void 0;
|
|
680
|
+
}
|
|
681
|
+
const categoryParts = afterComponents.slice(0, Math.min(depth, afterComponents.length - 1));
|
|
682
|
+
const lastCategoryPart = categoryParts[categoryParts.length - 1];
|
|
683
|
+
if (lastCategoryPart === fileName) {
|
|
684
|
+
categoryParts.pop();
|
|
685
|
+
}
|
|
686
|
+
if (categoryParts.length === 0) {
|
|
687
|
+
return void 0;
|
|
688
|
+
}
|
|
689
|
+
return categoryParts.join("/");
|
|
690
|
+
}
|
|
691
|
+
function hasRealEnrichment(fragment) {
|
|
692
|
+
if (fragment.description && fragment.description.length > 20) {
|
|
693
|
+
return true;
|
|
694
|
+
}
|
|
695
|
+
if (fragment.usage?.when && fragment.usage.when.length > 0) {
|
|
696
|
+
return true;
|
|
697
|
+
}
|
|
698
|
+
if (fragment.usage?.doNot && fragment.usage.doNot.length > 0) {
|
|
699
|
+
return true;
|
|
700
|
+
}
|
|
701
|
+
if (fragment.usage?.patterns && fragment.usage.patterns.length > 0) {
|
|
702
|
+
return true;
|
|
703
|
+
}
|
|
704
|
+
if (fragment.accessibility?.requirements && fragment.accessibility.requirements.length > 0) {
|
|
705
|
+
return true;
|
|
706
|
+
}
|
|
707
|
+
if (fragment.figma?.nodeId || fragment.figma?.variants) {
|
|
708
|
+
return true;
|
|
709
|
+
}
|
|
710
|
+
if (fragment.related?.similar && fragment.related.similar.length > 0) {
|
|
711
|
+
return true;
|
|
712
|
+
}
|
|
713
|
+
return false;
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
// src/core/generators/context.ts
|
|
717
|
+
function generateContextMd(registry, options = {}) {
|
|
718
|
+
const {
|
|
719
|
+
format = "markdown",
|
|
720
|
+
compact = false,
|
|
721
|
+
include = { props: true, relations: true, code: false }
|
|
722
|
+
} = options;
|
|
723
|
+
if (format === "json") {
|
|
724
|
+
return generateJsonContext(registry, include, compact);
|
|
725
|
+
}
|
|
726
|
+
return generateMarkdownContext(registry, include, compact);
|
|
727
|
+
}
|
|
728
|
+
function generateMarkdownContext(registry, include, compact) {
|
|
729
|
+
const lines = [];
|
|
730
|
+
const componentNames = Object.keys(registry.components).sort();
|
|
731
|
+
const componentCount = componentNames.length;
|
|
732
|
+
lines.push("# Component Library Context");
|
|
733
|
+
lines.push(`Generated: ${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]} | Components: ${componentCount}`);
|
|
734
|
+
lines.push("");
|
|
735
|
+
lines.push("## Quick Reference");
|
|
736
|
+
lines.push("");
|
|
737
|
+
lines.push("| Component | Path | Category | Status |");
|
|
738
|
+
lines.push("|-----------|------|----------|--------|");
|
|
739
|
+
for (const name of componentNames) {
|
|
740
|
+
const entry = registry.components[name];
|
|
741
|
+
const status = entry.status || "stable";
|
|
742
|
+
const category = entry.category || "-";
|
|
743
|
+
lines.push(`| ${name} | ${entry.path} | ${category} | ${status} |`);
|
|
744
|
+
}
|
|
745
|
+
lines.push("");
|
|
746
|
+
if (compact) {
|
|
747
|
+
const content2 = lines.join("\n");
|
|
748
|
+
return {
|
|
749
|
+
content: content2,
|
|
750
|
+
tokenEstimate: estimateTokens(content2),
|
|
751
|
+
componentCount
|
|
752
|
+
};
|
|
753
|
+
}
|
|
754
|
+
lines.push("---");
|
|
755
|
+
lines.push("");
|
|
756
|
+
for (const name of componentNames) {
|
|
757
|
+
const entry = registry.components[name];
|
|
758
|
+
const fragment = entry.fragment;
|
|
759
|
+
lines.push(`## ${name}`);
|
|
760
|
+
lines.push(`**Path:** \`${entry.path}\``);
|
|
761
|
+
if (entry.category) {
|
|
762
|
+
lines.push(`**Category:** ${entry.category} | **Status:** ${entry.status || "stable"}`);
|
|
763
|
+
}
|
|
764
|
+
lines.push("");
|
|
765
|
+
if (entry.description || fragment?.description) {
|
|
766
|
+
lines.push("### Description");
|
|
767
|
+
lines.push(entry.description || fragment?.description || "");
|
|
768
|
+
lines.push("");
|
|
769
|
+
}
|
|
770
|
+
if (fragment?.usage) {
|
|
771
|
+
if (fragment.usage.when && fragment.usage.when.length > 0) {
|
|
772
|
+
lines.push("### When to Use");
|
|
773
|
+
for (const when of fragment.usage.when) {
|
|
774
|
+
lines.push(`- ${when}`);
|
|
775
|
+
}
|
|
776
|
+
lines.push("");
|
|
777
|
+
}
|
|
778
|
+
if (fragment.usage.doNot && fragment.usage.doNot.length > 0) {
|
|
779
|
+
lines.push("### Do Not");
|
|
780
|
+
for (const doNotItem of fragment.usage.doNot) {
|
|
781
|
+
if (typeof doNotItem === "string") {
|
|
782
|
+
lines.push(`- ${doNotItem}`);
|
|
783
|
+
} else {
|
|
784
|
+
const item = doNotItem;
|
|
785
|
+
if (item.instead) {
|
|
786
|
+
const alternativePath = resolveComponentPath(item.instead, registry);
|
|
787
|
+
if (alternativePath) {
|
|
788
|
+
lines.push(`- ${item.text} \u2192 use **${item.instead}** (\`${alternativePath}\`)`);
|
|
789
|
+
} else {
|
|
790
|
+
lines.push(`- ${item.text} \u2192 use **${item.instead}**`);
|
|
791
|
+
}
|
|
792
|
+
} else {
|
|
793
|
+
lines.push(`- ${item.text}`);
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
lines.push("");
|
|
798
|
+
}
|
|
799
|
+
if (include.code && fragment.usage.patterns && fragment.usage.patterns.length > 0) {
|
|
800
|
+
lines.push("### Patterns");
|
|
801
|
+
for (const pattern of fragment.usage.patterns) {
|
|
802
|
+
lines.push(`**${pattern.name}**`);
|
|
803
|
+
if (pattern.description) {
|
|
804
|
+
lines.push(pattern.description);
|
|
805
|
+
}
|
|
806
|
+
lines.push("```tsx");
|
|
807
|
+
lines.push(pattern.code);
|
|
808
|
+
lines.push("```");
|
|
809
|
+
lines.push("");
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
if (include.props && entry.props && Object.keys(entry.props).length > 0) {
|
|
814
|
+
lines.push("### Props");
|
|
815
|
+
lines.push("| Prop | Type | Default | Description |");
|
|
816
|
+
lines.push("|------|------|---------|-------------|");
|
|
817
|
+
for (const [propName, prop] of Object.entries(entry.props)) {
|
|
818
|
+
const type = formatPropType(prop);
|
|
819
|
+
const defaultVal = prop.default !== void 0 ? `\`${JSON.stringify(prop.default)}\`` : "-";
|
|
820
|
+
const desc = prop.description || "-";
|
|
821
|
+
const required = prop.required ? " (required)" : "";
|
|
822
|
+
lines.push(`| ${propName}${required} | ${type} | ${defaultVal} | ${desc} |`);
|
|
823
|
+
}
|
|
824
|
+
lines.push("");
|
|
825
|
+
}
|
|
826
|
+
if (fragment?.accessibility) {
|
|
827
|
+
lines.push("### Accessibility");
|
|
828
|
+
if (fragment.accessibility.role) {
|
|
829
|
+
lines.push(`**Role:** ${fragment.accessibility.role}`);
|
|
830
|
+
}
|
|
831
|
+
if (fragment.accessibility.requirements && fragment.accessibility.requirements.length > 0) {
|
|
832
|
+
for (const req of fragment.accessibility.requirements) {
|
|
833
|
+
lines.push(`- ${req}`);
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
if (fragment.accessibility.keyboard) {
|
|
837
|
+
lines.push("");
|
|
838
|
+
lines.push("**Keyboard:**");
|
|
839
|
+
for (const [key, action] of Object.entries(fragment.accessibility.keyboard)) {
|
|
840
|
+
lines.push(`- \`${key}\`: ${action}`);
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
lines.push("");
|
|
844
|
+
}
|
|
845
|
+
if (include.relations && fragment?.related) {
|
|
846
|
+
lines.push("### Related");
|
|
847
|
+
if (fragment.related.similar && fragment.related.similar.length > 0) {
|
|
848
|
+
const resolved = fragment.related.similar.map((comp) => {
|
|
849
|
+
const path = resolveComponentPath(comp, registry);
|
|
850
|
+
return path ? `${comp} (\`${path}\`)` : comp;
|
|
851
|
+
});
|
|
852
|
+
lines.push(`- **Similar:** ${resolved.join(", ")}`);
|
|
853
|
+
}
|
|
854
|
+
if (fragment.related.composedWith && fragment.related.composedWith.length > 0) {
|
|
855
|
+
const resolved = fragment.related.composedWith.map((comp) => {
|
|
856
|
+
const path = resolveComponentPath(comp, registry);
|
|
857
|
+
return path ? `${comp} (\`${path}\`)` : comp;
|
|
858
|
+
});
|
|
859
|
+
lines.push(`- **Composed with:** ${resolved.join(", ")}`);
|
|
860
|
+
}
|
|
861
|
+
if (fragment.related.usedIn && fragment.related.usedIn.length > 0) {
|
|
862
|
+
const resolved = fragment.related.usedIn.map((comp) => {
|
|
863
|
+
const path = resolveComponentPath(comp, registry);
|
|
864
|
+
return path ? `${comp} (\`${path}\`)` : comp;
|
|
865
|
+
});
|
|
866
|
+
lines.push(`- **Used in:** ${resolved.join(", ")}`);
|
|
867
|
+
}
|
|
868
|
+
lines.push("");
|
|
869
|
+
}
|
|
870
|
+
lines.push("---");
|
|
871
|
+
lines.push("");
|
|
872
|
+
}
|
|
873
|
+
const content = lines.join("\n");
|
|
874
|
+
return {
|
|
875
|
+
content,
|
|
876
|
+
tokenEstimate: estimateTokens(content),
|
|
877
|
+
componentCount
|
|
878
|
+
};
|
|
879
|
+
}
|
|
880
|
+
function generateJsonContext(registry, include, compact) {
|
|
881
|
+
const componentNames = Object.keys(registry.components).sort();
|
|
882
|
+
const componentCount = componentNames.length;
|
|
883
|
+
const components = {};
|
|
884
|
+
for (const name of componentNames) {
|
|
885
|
+
const entry = registry.components[name];
|
|
886
|
+
const fragment = entry.fragment;
|
|
887
|
+
const component = {
|
|
888
|
+
path: entry.path
|
|
889
|
+
};
|
|
890
|
+
if (entry.category) component.category = entry.category;
|
|
891
|
+
if (entry.status) component.status = entry.status;
|
|
892
|
+
if (entry.description || fragment?.description) {
|
|
893
|
+
component.description = entry.description || fragment?.description;
|
|
894
|
+
}
|
|
895
|
+
if (!compact && fragment?.usage) {
|
|
896
|
+
if (fragment.usage.when && fragment.usage.when.length > 0) {
|
|
897
|
+
component.whenToUse = fragment.usage.when;
|
|
898
|
+
}
|
|
899
|
+
if (fragment.usage.doNot && fragment.usage.doNot.length > 0) {
|
|
900
|
+
component.doNot = fragment.usage.doNot.map((item) => {
|
|
901
|
+
if (typeof item === "string") {
|
|
902
|
+
return { text: item };
|
|
903
|
+
}
|
|
904
|
+
const doNotItem = item;
|
|
905
|
+
const result = {
|
|
906
|
+
text: doNotItem.text
|
|
907
|
+
};
|
|
908
|
+
if (doNotItem.instead) {
|
|
909
|
+
result.instead = doNotItem.instead;
|
|
910
|
+
result.insteadPath = resolveComponentPath(doNotItem.instead, registry);
|
|
911
|
+
}
|
|
912
|
+
return result;
|
|
913
|
+
});
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
if (!compact && include.props && entry.props) {
|
|
917
|
+
component.props = {};
|
|
918
|
+
for (const [propName, prop] of Object.entries(entry.props)) {
|
|
919
|
+
component.props[propName] = {
|
|
920
|
+
type: formatPropType(prop)
|
|
921
|
+
};
|
|
922
|
+
if (prop.required) component.props[propName].required = true;
|
|
923
|
+
if (prop.default !== void 0) component.props[propName].default = prop.default;
|
|
924
|
+
if (prop.description) component.props[propName].description = prop.description;
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
if (!compact && include.relations && fragment?.related) {
|
|
928
|
+
component.related = {};
|
|
929
|
+
if (fragment.related.similar) {
|
|
930
|
+
component.related.similar = fragment.related.similar.map((name2) => ({
|
|
931
|
+
name: name2,
|
|
932
|
+
path: resolveComponentPath(name2, registry)
|
|
933
|
+
}));
|
|
934
|
+
}
|
|
935
|
+
if (fragment.related.composedWith) {
|
|
936
|
+
component.related.composedWith = fragment.related.composedWith.map((name2) => ({
|
|
937
|
+
name: name2,
|
|
938
|
+
path: resolveComponentPath(name2, registry)
|
|
939
|
+
}));
|
|
940
|
+
}
|
|
941
|
+
if (fragment.related.usedIn) {
|
|
942
|
+
component.related.usedIn = fragment.related.usedIn.map((name2) => ({
|
|
943
|
+
name: name2,
|
|
944
|
+
path: resolveComponentPath(name2, registry)
|
|
945
|
+
}));
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
components[name] = component;
|
|
949
|
+
}
|
|
950
|
+
const output = {
|
|
951
|
+
version: "1.0",
|
|
952
|
+
generatedAt: registry.generatedAt,
|
|
953
|
+
componentCount,
|
|
954
|
+
categories: registry.categories,
|
|
955
|
+
components
|
|
956
|
+
};
|
|
957
|
+
const content = JSON.stringify(output, null, 2);
|
|
958
|
+
return {
|
|
959
|
+
content,
|
|
960
|
+
tokenEstimate: estimateTokens(content),
|
|
961
|
+
componentCount
|
|
962
|
+
};
|
|
963
|
+
}
|
|
964
|
+
function resolveComponentPath(componentName, registry) {
|
|
965
|
+
return registry.components[componentName]?.path;
|
|
966
|
+
}
|
|
967
|
+
function formatPropType(prop) {
|
|
968
|
+
if (prop.options && prop.options.length > 0) {
|
|
969
|
+
return prop.options.map((o) => `"${o}"`).join(" \\| ");
|
|
970
|
+
}
|
|
971
|
+
return prop.type || "unknown";
|
|
972
|
+
}
|
|
973
|
+
function estimateTokens(text) {
|
|
974
|
+
return Math.ceil(text.length / 4);
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
// src/core/loader.ts
|
|
978
|
+
import { unlink } from "fs/promises";
|
|
979
|
+
import { dirname as dirname4, basename as basename3, join as join2 } from "path";
|
|
980
|
+
import { pathToFileURL } from "url";
|
|
981
|
+
import { build } from "esbuild";
|
|
982
|
+
var DEFINE_SEGMENT_SHIM = `
|
|
983
|
+
export function defineSegment(def) {
|
|
984
|
+
return def;
|
|
985
|
+
}
|
|
986
|
+
export function defineFragment(def) {
|
|
987
|
+
return def;
|
|
988
|
+
}
|
|
989
|
+
export function defineRecipe(def) {
|
|
990
|
+
return def;
|
|
991
|
+
}
|
|
992
|
+
`;
|
|
993
|
+
function createSegmentsCoreShimPlugin() {
|
|
994
|
+
return {
|
|
995
|
+
name: BRAND.vitePluginNamespace,
|
|
996
|
+
setup(build2) {
|
|
997
|
+
build2.onResolve({ filter: /^@fragments\/core$/ }, (args) => {
|
|
998
|
+
return {
|
|
999
|
+
path: args.path,
|
|
1000
|
+
namespace: BRAND.vitePluginNamespace
|
|
1001
|
+
};
|
|
1002
|
+
});
|
|
1003
|
+
build2.onLoad({ filter: /.*/, namespace: BRAND.vitePluginNamespace }, () => {
|
|
1004
|
+
return {
|
|
1005
|
+
contents: DEFINE_SEGMENT_SHIM,
|
|
1006
|
+
loader: "js"
|
|
1007
|
+
};
|
|
1008
|
+
});
|
|
1009
|
+
}
|
|
1010
|
+
};
|
|
1011
|
+
}
|
|
1012
|
+
async function loadSegmentFile(absolutePath) {
|
|
1013
|
+
const ext = absolutePath.split(".").pop()?.toLowerCase();
|
|
1014
|
+
const needsTransform = ext === "tsx" || ext === "ts" || ext === "jsx";
|
|
1015
|
+
if (!needsTransform) {
|
|
1016
|
+
const fileUrl = pathToFileURL(absolutePath).href;
|
|
1017
|
+
const module = await import(fileUrl);
|
|
1018
|
+
return module.default ?? null;
|
|
1019
|
+
}
|
|
1020
|
+
const sourceDir = dirname4(absolutePath);
|
|
1021
|
+
const baseName = basename3(absolutePath, `.${ext}`);
|
|
1022
|
+
const tempFile = join2(sourceDir, `.${baseName}.segments-temp-${Date.now()}.cjs`);
|
|
1023
|
+
try {
|
|
1024
|
+
await build({
|
|
1025
|
+
entryPoints: [absolutePath],
|
|
1026
|
+
outfile: tempFile,
|
|
1027
|
+
bundle: true,
|
|
1028
|
+
format: "cjs",
|
|
1029
|
+
// Use CommonJS to avoid ESM issues with lodash, etc.
|
|
1030
|
+
target: "es2022",
|
|
1031
|
+
jsx: "automatic",
|
|
1032
|
+
platform: "node",
|
|
1033
|
+
plugins: [createSegmentsCoreShimPlugin()],
|
|
1034
|
+
// Externalize all node_modules - we only need segment metadata, not component code
|
|
1035
|
+
packages: "external",
|
|
1036
|
+
// Also explicitly list patterns for nested imports
|
|
1037
|
+
external: [
|
|
1038
|
+
// React and its subpaths
|
|
1039
|
+
"react",
|
|
1040
|
+
"react-dom",
|
|
1041
|
+
"react/*",
|
|
1042
|
+
"react-dom/*"
|
|
1043
|
+
],
|
|
1044
|
+
// Don't emit sourcemaps for temp files
|
|
1045
|
+
sourcemap: false,
|
|
1046
|
+
// Suppress warnings
|
|
1047
|
+
logLevel: "silent",
|
|
1048
|
+
// Use loader to ignore style imports
|
|
1049
|
+
loader: {
|
|
1050
|
+
".scss": "empty",
|
|
1051
|
+
".css": "empty",
|
|
1052
|
+
".svg": "empty",
|
|
1053
|
+
".png": "empty",
|
|
1054
|
+
".jpg": "empty",
|
|
1055
|
+
".jpeg": "empty",
|
|
1056
|
+
".gif": "empty",
|
|
1057
|
+
".woff": "empty",
|
|
1058
|
+
".woff2": "empty",
|
|
1059
|
+
".ttf": "empty",
|
|
1060
|
+
".eot": "empty"
|
|
1061
|
+
}
|
|
1062
|
+
});
|
|
1063
|
+
const fileUrl = pathToFileURL(tempFile).href;
|
|
1064
|
+
const module = await import(fileUrl);
|
|
1065
|
+
return module.default ?? null;
|
|
1066
|
+
} finally {
|
|
1067
|
+
try {
|
|
1068
|
+
await unlink(tempFile);
|
|
1069
|
+
} catch {
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
// src/core/parser.ts
|
|
1075
|
+
import ts2 from "typescript";
|
|
1076
|
+
function parseSegmentFile(fileContent, filePath) {
|
|
1077
|
+
const warnings = [];
|
|
1078
|
+
const sourceFile = ts2.createSourceFile(
|
|
1079
|
+
filePath ?? "segment.tsx",
|
|
1080
|
+
fileContent,
|
|
1081
|
+
ts2.ScriptTarget.Latest,
|
|
1082
|
+
true,
|
|
1083
|
+
ts2.ScriptKind.TSX
|
|
1084
|
+
);
|
|
1085
|
+
const imports = extractImports(sourceFile);
|
|
1086
|
+
const defineSegmentCall = findDefineSegmentCall(sourceFile);
|
|
1087
|
+
if (!defineSegmentCall) {
|
|
1088
|
+
warnings.push("No defineSegment() call found");
|
|
1089
|
+
return {
|
|
1090
|
+
componentImport: null,
|
|
1091
|
+
componentName: null,
|
|
1092
|
+
meta: {},
|
|
1093
|
+
usage: { when: [], whenNot: [] },
|
|
1094
|
+
props: {},
|
|
1095
|
+
variants: [],
|
|
1096
|
+
relations: [],
|
|
1097
|
+
warnings
|
|
1098
|
+
};
|
|
1099
|
+
}
|
|
1100
|
+
const arg = defineSegmentCall.arguments[0];
|
|
1101
|
+
if (!arg || !ts2.isObjectLiteralExpression(arg)) {
|
|
1102
|
+
warnings.push("defineSegment() argument is not an object literal");
|
|
1103
|
+
return {
|
|
1104
|
+
componentImport: null,
|
|
1105
|
+
componentName: null,
|
|
1106
|
+
meta: {},
|
|
1107
|
+
usage: { when: [], whenNot: [] },
|
|
1108
|
+
props: {},
|
|
1109
|
+
variants: [],
|
|
1110
|
+
relations: [],
|
|
1111
|
+
warnings
|
|
1112
|
+
};
|
|
1113
|
+
}
|
|
1114
|
+
const componentProp = findProperty(arg, "component");
|
|
1115
|
+
let componentName = null;
|
|
1116
|
+
let componentImport = null;
|
|
1117
|
+
if (componentProp && ts2.isIdentifier(componentProp)) {
|
|
1118
|
+
componentName = componentProp.text;
|
|
1119
|
+
componentImport = imports.get(componentName) ?? null;
|
|
1120
|
+
}
|
|
1121
|
+
const meta = extractMeta(arg, warnings);
|
|
1122
|
+
const usage = extractUsage(arg, warnings);
|
|
1123
|
+
const props = extractProps(arg, warnings);
|
|
1124
|
+
const variants = extractVariants(arg, sourceFile, warnings);
|
|
1125
|
+
const relations = extractRelations(arg, warnings);
|
|
1126
|
+
return {
|
|
1127
|
+
componentImport,
|
|
1128
|
+
componentName,
|
|
1129
|
+
meta,
|
|
1130
|
+
usage,
|
|
1131
|
+
props,
|
|
1132
|
+
variants,
|
|
1133
|
+
relations,
|
|
1134
|
+
warnings
|
|
1135
|
+
};
|
|
1136
|
+
}
|
|
1137
|
+
function extractImports(sourceFile) {
|
|
1138
|
+
const imports = /* @__PURE__ */ new Map();
|
|
1139
|
+
ts2.forEachChild(sourceFile, (node) => {
|
|
1140
|
+
if (ts2.isImportDeclaration(node)) {
|
|
1141
|
+
const moduleSpecifier = node.moduleSpecifier;
|
|
1142
|
+
if (ts2.isStringLiteral(moduleSpecifier)) {
|
|
1143
|
+
const modulePath = moduleSpecifier.text;
|
|
1144
|
+
const importClause = node.importClause;
|
|
1145
|
+
if (importClause) {
|
|
1146
|
+
if (importClause.name) {
|
|
1147
|
+
imports.set(importClause.name.text, modulePath);
|
|
1148
|
+
}
|
|
1149
|
+
const namedBindings = importClause.namedBindings;
|
|
1150
|
+
if (namedBindings && ts2.isNamedImports(namedBindings)) {
|
|
1151
|
+
for (const element of namedBindings.elements) {
|
|
1152
|
+
imports.set(element.name.text, modulePath);
|
|
1153
|
+
}
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
1158
|
+
});
|
|
1159
|
+
return imports;
|
|
1160
|
+
}
|
|
1161
|
+
function findDefineSegmentCall(sourceFile) {
|
|
1162
|
+
let result = null;
|
|
1163
|
+
function visit(node) {
|
|
1164
|
+
if (ts2.isCallExpression(node)) {
|
|
1165
|
+
const expression = node.expression;
|
|
1166
|
+
if (ts2.isIdentifier(expression) && expression.text === "defineSegment") {
|
|
1167
|
+
result = node;
|
|
1168
|
+
return;
|
|
1169
|
+
}
|
|
1170
|
+
}
|
|
1171
|
+
ts2.forEachChild(node, visit);
|
|
1172
|
+
}
|
|
1173
|
+
visit(sourceFile);
|
|
1174
|
+
return result;
|
|
1175
|
+
}
|
|
1176
|
+
function findProperty(obj, name) {
|
|
1177
|
+
for (const prop of obj.properties) {
|
|
1178
|
+
if (ts2.isPropertyAssignment(prop)) {
|
|
1179
|
+
const propName = prop.name;
|
|
1180
|
+
if (ts2.isIdentifier(propName) && propName.text === name) {
|
|
1181
|
+
return prop.initializer;
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
return null;
|
|
1186
|
+
}
|
|
1187
|
+
function extractMeta(arg, warnings) {
|
|
1188
|
+
const metaProp = findProperty(arg, "meta");
|
|
1189
|
+
if (!metaProp || !ts2.isObjectLiteralExpression(metaProp)) {
|
|
1190
|
+
warnings.push("No meta object found");
|
|
1191
|
+
return {};
|
|
1192
|
+
}
|
|
1193
|
+
const meta = {};
|
|
1194
|
+
const name = extractStringProperty(metaProp, "name");
|
|
1195
|
+
if (name) meta.name = name;
|
|
1196
|
+
const description = extractStringProperty(metaProp, "description");
|
|
1197
|
+
if (description) meta.description = description;
|
|
1198
|
+
const category = extractStringProperty(metaProp, "category");
|
|
1199
|
+
if (category) meta.category = category;
|
|
1200
|
+
const status = extractStringProperty(metaProp, "status");
|
|
1201
|
+
if (status) meta.status = status;
|
|
1202
|
+
const since = extractStringProperty(metaProp, "since");
|
|
1203
|
+
if (since) meta.since = since;
|
|
1204
|
+
const figma = extractStringProperty(metaProp, "figma");
|
|
1205
|
+
if (figma) meta.figma = figma;
|
|
1206
|
+
const tags = extractStringArray(metaProp, "tags");
|
|
1207
|
+
if (tags.length > 0) meta.tags = tags;
|
|
1208
|
+
return meta;
|
|
1209
|
+
}
|
|
1210
|
+
function extractUsage(arg, warnings) {
|
|
1211
|
+
const usageProp = findProperty(arg, "usage");
|
|
1212
|
+
if (!usageProp || !ts2.isObjectLiteralExpression(usageProp)) {
|
|
1213
|
+
return { when: [], whenNot: [] };
|
|
1214
|
+
}
|
|
1215
|
+
return {
|
|
1216
|
+
when: extractStringArray(usageProp, "when"),
|
|
1217
|
+
whenNot: extractStringArray(usageProp, "whenNot"),
|
|
1218
|
+
guidelines: extractStringArray(usageProp, "guidelines"),
|
|
1219
|
+
accessibility: extractStringArray(usageProp, "accessibility")
|
|
1220
|
+
};
|
|
1221
|
+
}
|
|
1222
|
+
function extractProps(arg, warnings) {
|
|
1223
|
+
const propsProp = findProperty(arg, "props");
|
|
1224
|
+
if (!propsProp || !ts2.isObjectLiteralExpression(propsProp)) {
|
|
1225
|
+
return {};
|
|
1226
|
+
}
|
|
1227
|
+
const props = {};
|
|
1228
|
+
for (const prop of propsProp.properties) {
|
|
1229
|
+
if (ts2.isPropertyAssignment(prop) && ts2.isIdentifier(prop.name)) {
|
|
1230
|
+
const propName = prop.name.text;
|
|
1231
|
+
const propValue = prop.initializer;
|
|
1232
|
+
if (ts2.isObjectLiteralExpression(propValue)) {
|
|
1233
|
+
props[propName] = extractPropDefinition(propValue);
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
return props;
|
|
1238
|
+
}
|
|
1239
|
+
function extractPropDefinition(obj) {
|
|
1240
|
+
const def = {};
|
|
1241
|
+
const type = extractStringProperty(obj, "type");
|
|
1242
|
+
if (type) def.type = type;
|
|
1243
|
+
const description = extractStringProperty(obj, "description");
|
|
1244
|
+
if (description) def.description = description;
|
|
1245
|
+
const values = extractStringArray(obj, "values");
|
|
1246
|
+
if (values.length > 0) def.values = values;
|
|
1247
|
+
const required = extractBooleanProperty(obj, "required");
|
|
1248
|
+
if (required !== null) def.required = required;
|
|
1249
|
+
const defaultProp = findProperty(obj, "default");
|
|
1250
|
+
if (defaultProp) {
|
|
1251
|
+
def.default = extractLiteralValue(defaultProp);
|
|
1252
|
+
}
|
|
1253
|
+
const constraints = extractStringArray(obj, "constraints");
|
|
1254
|
+
if (constraints.length > 0) def.constraints = constraints;
|
|
1255
|
+
return def;
|
|
1256
|
+
}
|
|
1257
|
+
function extractVariants(arg, sourceFile, warnings) {
|
|
1258
|
+
const variantsProp = findProperty(arg, "variants");
|
|
1259
|
+
if (!variantsProp || !ts2.isArrayLiteralExpression(variantsProp)) {
|
|
1260
|
+
return [];
|
|
1261
|
+
}
|
|
1262
|
+
const variants = [];
|
|
1263
|
+
for (const element of variantsProp.elements) {
|
|
1264
|
+
if (ts2.isObjectLiteralExpression(element)) {
|
|
1265
|
+
const name = extractStringProperty(element, "name");
|
|
1266
|
+
const description = extractStringProperty(element, "description");
|
|
1267
|
+
if (name) {
|
|
1268
|
+
const variant = {
|
|
1269
|
+
name,
|
|
1270
|
+
description: description ?? ""
|
|
1271
|
+
};
|
|
1272
|
+
const codeProp = findProperty(element, "code");
|
|
1273
|
+
if (codeProp && ts2.isStringLiteral(codeProp)) {
|
|
1274
|
+
variant.code = codeProp.text;
|
|
1275
|
+
}
|
|
1276
|
+
const renderProp = findProperty(element, "render");
|
|
1277
|
+
if (renderProp && !variant.code) {
|
|
1278
|
+
variant.code = extractRenderCode(renderProp, sourceFile);
|
|
1279
|
+
}
|
|
1280
|
+
const figma = extractStringProperty(element, "figma");
|
|
1281
|
+
if (figma) {
|
|
1282
|
+
variant.figma = figma;
|
|
1283
|
+
}
|
|
1284
|
+
const argsProp = findProperty(element, "args");
|
|
1285
|
+
if (argsProp && ts2.isObjectLiteralExpression(argsProp)) {
|
|
1286
|
+
const argsValue = extractLiteralValue(argsProp);
|
|
1287
|
+
if (argsValue && typeof argsValue === "object" && !Array.isArray(argsValue)) {
|
|
1288
|
+
variant.args = argsValue;
|
|
1289
|
+
}
|
|
1290
|
+
}
|
|
1291
|
+
variants.push(variant);
|
|
1292
|
+
}
|
|
1293
|
+
}
|
|
1294
|
+
}
|
|
1295
|
+
return variants;
|
|
1296
|
+
}
|
|
1297
|
+
function extractRenderCode(renderProp, sourceFile) {
|
|
1298
|
+
if (ts2.isArrowFunction(renderProp)) {
|
|
1299
|
+
const body = renderProp.body;
|
|
1300
|
+
const start = body.getStart(sourceFile);
|
|
1301
|
+
const end = body.getEnd();
|
|
1302
|
+
let code = sourceFile.text.substring(start, end).trim();
|
|
1303
|
+
if (code.startsWith("(") && code.endsWith(")")) {
|
|
1304
|
+
code = code.slice(1, -1).trim();
|
|
1305
|
+
}
|
|
1306
|
+
return code;
|
|
1307
|
+
}
|
|
1308
|
+
return void 0;
|
|
1309
|
+
}
|
|
1310
|
+
function extractRelations(arg, warnings) {
|
|
1311
|
+
const relationsProp = findProperty(arg, "relations");
|
|
1312
|
+
if (!relationsProp || !ts2.isArrayLiteralExpression(relationsProp)) {
|
|
1313
|
+
return [];
|
|
1314
|
+
}
|
|
1315
|
+
const relations = [];
|
|
1316
|
+
for (const element of relationsProp.elements) {
|
|
1317
|
+
if (ts2.isObjectLiteralExpression(element)) {
|
|
1318
|
+
const component = extractStringProperty(element, "component");
|
|
1319
|
+
const relationship = extractStringProperty(element, "relationship");
|
|
1320
|
+
const note = extractStringProperty(element, "note");
|
|
1321
|
+
if (component && relationship) {
|
|
1322
|
+
relations.push({
|
|
1323
|
+
component,
|
|
1324
|
+
relationship,
|
|
1325
|
+
note: note ?? ""
|
|
1326
|
+
});
|
|
1327
|
+
}
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
return relations;
|
|
1331
|
+
}
|
|
1332
|
+
function extractStringProperty(obj, name) {
|
|
1333
|
+
const prop = findProperty(obj, name);
|
|
1334
|
+
if (prop && ts2.isStringLiteral(prop)) {
|
|
1335
|
+
return prop.text;
|
|
1336
|
+
}
|
|
1337
|
+
if (prop && ts2.isNoSubstitutionTemplateLiteral(prop)) {
|
|
1338
|
+
return prop.text;
|
|
1339
|
+
}
|
|
1340
|
+
return null;
|
|
1341
|
+
}
|
|
1342
|
+
function extractBooleanProperty(obj, name) {
|
|
1343
|
+
const prop = findProperty(obj, name);
|
|
1344
|
+
if (prop) {
|
|
1345
|
+
if (prop.kind === ts2.SyntaxKind.TrueKeyword) return true;
|
|
1346
|
+
if (prop.kind === ts2.SyntaxKind.FalseKeyword) return false;
|
|
1347
|
+
}
|
|
1348
|
+
return null;
|
|
1349
|
+
}
|
|
1350
|
+
function extractStringArray(obj, name) {
|
|
1351
|
+
const prop = findProperty(obj, name);
|
|
1352
|
+
if (!prop || !ts2.isArrayLiteralExpression(prop)) {
|
|
1353
|
+
return [];
|
|
1354
|
+
}
|
|
1355
|
+
const result = [];
|
|
1356
|
+
for (const element of prop.elements) {
|
|
1357
|
+
if (ts2.isStringLiteral(element)) {
|
|
1358
|
+
result.push(element.text);
|
|
1359
|
+
} else if (ts2.isNoSubstitutionTemplateLiteral(element)) {
|
|
1360
|
+
result.push(element.text);
|
|
1361
|
+
}
|
|
1362
|
+
}
|
|
1363
|
+
return result;
|
|
1364
|
+
}
|
|
1365
|
+
function extractLiteralValue(expr) {
|
|
1366
|
+
if (ts2.isStringLiteral(expr)) {
|
|
1367
|
+
return expr.text;
|
|
1368
|
+
}
|
|
1369
|
+
if (ts2.isNumericLiteral(expr)) {
|
|
1370
|
+
return Number(expr.text);
|
|
1371
|
+
}
|
|
1372
|
+
if (expr.kind === ts2.SyntaxKind.TrueKeyword) {
|
|
1373
|
+
return true;
|
|
1374
|
+
}
|
|
1375
|
+
if (expr.kind === ts2.SyntaxKind.FalseKeyword) {
|
|
1376
|
+
return false;
|
|
1377
|
+
}
|
|
1378
|
+
if (expr.kind === ts2.SyntaxKind.NullKeyword) {
|
|
1379
|
+
return null;
|
|
1380
|
+
}
|
|
1381
|
+
if (ts2.isArrayLiteralExpression(expr)) {
|
|
1382
|
+
return expr.elements.map(extractLiteralValue);
|
|
1383
|
+
}
|
|
1384
|
+
if (ts2.isObjectLiteralExpression(expr)) {
|
|
1385
|
+
const obj = {};
|
|
1386
|
+
for (const prop of expr.properties) {
|
|
1387
|
+
if (ts2.isPropertyAssignment(prop) && ts2.isIdentifier(prop.name)) {
|
|
1388
|
+
obj[prop.name.text] = extractLiteralValue(prop.initializer);
|
|
1389
|
+
}
|
|
1390
|
+
}
|
|
1391
|
+
return obj;
|
|
1392
|
+
}
|
|
1393
|
+
return void 0;
|
|
1394
|
+
}
|
|
1395
|
+
|
|
1396
|
+
// src/core/previewLoader.ts
|
|
1397
|
+
import { existsSync as existsSync4 } from "fs";
|
|
1398
|
+
import { join as join3, resolve as resolve3 } from "path";
|
|
1399
|
+
var PREVIEW_FILES = [
|
|
1400
|
+
"preview.tsx",
|
|
1401
|
+
"preview.ts",
|
|
1402
|
+
"preview.jsx",
|
|
1403
|
+
"preview.js"
|
|
1404
|
+
];
|
|
1405
|
+
function findPreviewConfigPath(storybookDir) {
|
|
1406
|
+
for (const fileName of PREVIEW_FILES) {
|
|
1407
|
+
const filePath = join3(storybookDir, fileName);
|
|
1408
|
+
if (existsSync4(filePath)) {
|
|
1409
|
+
return filePath;
|
|
1410
|
+
}
|
|
1411
|
+
}
|
|
1412
|
+
return null;
|
|
1413
|
+
}
|
|
1414
|
+
function findStorybookDir(projectRoot) {
|
|
1415
|
+
const possiblePaths = [
|
|
1416
|
+
join3(projectRoot, ".storybook"),
|
|
1417
|
+
join3(projectRoot, "storybook")
|
|
1418
|
+
];
|
|
1419
|
+
for (const dir of possiblePaths) {
|
|
1420
|
+
if (existsSync4(dir)) {
|
|
1421
|
+
return dir;
|
|
1422
|
+
}
|
|
1423
|
+
}
|
|
1424
|
+
return null;
|
|
1425
|
+
}
|
|
1426
|
+
function generatePreviewModule(previewPath) {
|
|
1427
|
+
if (!previewPath) {
|
|
1428
|
+
return `
|
|
1429
|
+
export const decorators = [];
|
|
1430
|
+
export const parameters = {};
|
|
1431
|
+
export const globalTypes = {};
|
|
1432
|
+
export const args = {};
|
|
1433
|
+
export const argTypes = {};
|
|
1434
|
+
export const loaders = [];
|
|
1435
|
+
|
|
1436
|
+
export default {
|
|
1437
|
+
decorators,
|
|
1438
|
+
parameters,
|
|
1439
|
+
globalTypes,
|
|
1440
|
+
args,
|
|
1441
|
+
argTypes,
|
|
1442
|
+
loaders,
|
|
1443
|
+
};
|
|
1444
|
+
`;
|
|
1445
|
+
}
|
|
1446
|
+
return `
|
|
1447
|
+
import * as preview from "${previewPath}";
|
|
1448
|
+
|
|
1449
|
+
export const decorators = preview.decorators ?? preview.default?.decorators ?? [];
|
|
1450
|
+
export const parameters = preview.parameters ?? preview.default?.parameters ?? {};
|
|
1451
|
+
export const globalTypes = preview.globalTypes ?? preview.default?.globalTypes ?? {};
|
|
1452
|
+
export const args = preview.args ?? preview.default?.args ?? {};
|
|
1453
|
+
export const argTypes = preview.argTypes ?? preview.default?.argTypes ?? {};
|
|
1454
|
+
export const loaders = preview.loaders ?? preview.default?.loaders ?? [];
|
|
1455
|
+
|
|
1456
|
+
export default {
|
|
1457
|
+
decorators,
|
|
1458
|
+
parameters,
|
|
1459
|
+
globalTypes,
|
|
1460
|
+
args,
|
|
1461
|
+
argTypes,
|
|
1462
|
+
loaders,
|
|
1463
|
+
};
|
|
1464
|
+
`;
|
|
1465
|
+
}
|
|
1466
|
+
|
|
1467
|
+
// src/core/importAnalyzer.ts
|
|
1468
|
+
import ts3 from "typescript";
|
|
1469
|
+
import { readFileSync as readFileSync3 } from "fs";
|
|
1470
|
+
import { dirname as dirname5, basename as basename4 } from "path";
|
|
1471
|
+
|
|
1472
|
+
export {
|
|
1473
|
+
findConfigFile,
|
|
1474
|
+
loadConfig,
|
|
1475
|
+
discoverRecipeFiles,
|
|
1476
|
+
discoverSegmentFiles,
|
|
1477
|
+
discoverComponentFiles,
|
|
1478
|
+
extractComponentName,
|
|
1479
|
+
discoverAllComponents,
|
|
1480
|
+
loadSegmentFile,
|
|
1481
|
+
parseSegmentFile,
|
|
1482
|
+
findPreviewConfigPath,
|
|
1483
|
+
findStorybookDir,
|
|
1484
|
+
generatePreviewModule,
|
|
1485
|
+
extractPropsFromFile,
|
|
1486
|
+
generateRegistry,
|
|
1487
|
+
generateContextMd
|
|
1488
|
+
};
|
|
1489
|
+
//# sourceMappingURL=chunk-OAENNG3G.js.map
|