@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,249 @@
|
|
|
1
|
+
import { createRequire } from 'module'; const require = createRequire(import.meta.url);
|
|
2
|
+
import {
|
|
3
|
+
extractPropsFromFile
|
|
4
|
+
} from "./chunk-OAENNG3G.js";
|
|
5
|
+
import "./chunk-LY2CFFPY.js";
|
|
6
|
+
import {
|
|
7
|
+
BRAND
|
|
8
|
+
} from "./chunk-XHNKNI6J.js";
|
|
9
|
+
|
|
10
|
+
// src/commands/generate.ts
|
|
11
|
+
import { readFile, writeFile, mkdir, access } from "fs/promises";
|
|
12
|
+
import { resolve, join, basename, relative } from "path";
|
|
13
|
+
import pc from "picocolors";
|
|
14
|
+
import fg from "fast-glob";
|
|
15
|
+
async function generate(options = {}) {
|
|
16
|
+
const projectRoot = resolve(options.projectRoot || process.cwd());
|
|
17
|
+
const generated = [];
|
|
18
|
+
const skipped = [];
|
|
19
|
+
const errors = [];
|
|
20
|
+
console.log(pc.cyan(`
|
|
21
|
+
${BRAND.name} Generate
|
|
22
|
+
`));
|
|
23
|
+
const fragmentsDir = join(projectRoot, BRAND.dataDir, BRAND.componentsDir);
|
|
24
|
+
await mkdir(fragmentsDir, { recursive: true });
|
|
25
|
+
const componentPattern = options.componentPattern || "src/components/**/*.tsx";
|
|
26
|
+
const componentFiles = await fg(componentPattern, {
|
|
27
|
+
cwd: projectRoot,
|
|
28
|
+
ignore: [
|
|
29
|
+
"**/node_modules/**",
|
|
30
|
+
"**/*.stories.*",
|
|
31
|
+
"**/*.segment.*",
|
|
32
|
+
"**/*.test.*",
|
|
33
|
+
"**/*.spec.*",
|
|
34
|
+
"**/*.d.ts",
|
|
35
|
+
"**/index.tsx"
|
|
36
|
+
// Often just re-exports
|
|
37
|
+
],
|
|
38
|
+
absolute: true
|
|
39
|
+
});
|
|
40
|
+
const storyFiles = await fg(["src/**/*.stories.tsx", "src/**/*.stories.ts"], {
|
|
41
|
+
cwd: projectRoot,
|
|
42
|
+
ignore: ["**/node_modules/**"],
|
|
43
|
+
absolute: true
|
|
44
|
+
});
|
|
45
|
+
const storyMap = /* @__PURE__ */ new Map();
|
|
46
|
+
for (const storyFile of storyFiles) {
|
|
47
|
+
try {
|
|
48
|
+
const content = await readFile(storyFile, "utf-8");
|
|
49
|
+
const componentName = extractComponentNameFromStory(content, storyFile);
|
|
50
|
+
if (componentName) {
|
|
51
|
+
const storyNames = extractStoryNames(content);
|
|
52
|
+
storyMap.set(componentName, storyNames);
|
|
53
|
+
}
|
|
54
|
+
} catch {
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
console.log(pc.dim(`Found ${componentFiles.length} component files
|
|
58
|
+
`));
|
|
59
|
+
for (const filePath of componentFiles) {
|
|
60
|
+
try {
|
|
61
|
+
const extracted = extractPropsFromFile(filePath);
|
|
62
|
+
if (!extracted || !extracted.componentName) {
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
const componentName = extracted.componentName;
|
|
66
|
+
if (options.component && componentName !== options.component) {
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
const fragmentPath = join(fragmentsDir, `${componentName}${BRAND.fileExtension}`);
|
|
70
|
+
let fragmentExists = false;
|
|
71
|
+
try {
|
|
72
|
+
await access(fragmentPath);
|
|
73
|
+
fragmentExists = true;
|
|
74
|
+
} catch {
|
|
75
|
+
}
|
|
76
|
+
if (fragmentExists && !options.force) {
|
|
77
|
+
skipped.push({ name: componentName, reason: "Fragment already exists" });
|
|
78
|
+
console.log(pc.dim(` Skipping ${componentName} (fragment exists)`));
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
const fragment = generateFragmentFromComponent(
|
|
82
|
+
componentName,
|
|
83
|
+
extracted,
|
|
84
|
+
filePath,
|
|
85
|
+
storyMap.get(componentName) || []
|
|
86
|
+
);
|
|
87
|
+
await writeFile(fragmentPath, JSON.stringify(fragment, null, 2), "utf-8");
|
|
88
|
+
generated.push({ name: componentName, path: relative(projectRoot, fragmentPath) });
|
|
89
|
+
console.log(pc.green(` \u2713 Generated ${componentName}${BRAND.fileExtension}`));
|
|
90
|
+
} catch (e) {
|
|
91
|
+
const fileName = basename(filePath);
|
|
92
|
+
errors.push({
|
|
93
|
+
name: fileName,
|
|
94
|
+
error: e instanceof Error ? e.message : String(e)
|
|
95
|
+
});
|
|
96
|
+
console.log(pc.red(` \u2717 Failed: ${fileName}`));
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
console.log();
|
|
100
|
+
if (generated.length > 0) {
|
|
101
|
+
console.log(pc.green(`\u2713 Generated ${generated.length} fragment(s)`));
|
|
102
|
+
}
|
|
103
|
+
if (skipped.length > 0) {
|
|
104
|
+
console.log(pc.dim(` Skipped ${skipped.length} (use --force to overwrite)`));
|
|
105
|
+
}
|
|
106
|
+
if (errors.length > 0) {
|
|
107
|
+
console.log(pc.yellow(` ${errors.length} error(s)`));
|
|
108
|
+
}
|
|
109
|
+
console.log();
|
|
110
|
+
return {
|
|
111
|
+
success: errors.length === 0,
|
|
112
|
+
generated,
|
|
113
|
+
skipped,
|
|
114
|
+
errors
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
function generateFragmentFromComponent(componentName, extracted, filePath, storyNames) {
|
|
118
|
+
const whenToUse = inferUsageFromStories(storyNames);
|
|
119
|
+
const description = generateDescription(componentName, extracted.props);
|
|
120
|
+
const accessibility = inferAccessibility(extracted.props);
|
|
121
|
+
const status = inferStatus(filePath);
|
|
122
|
+
const fragment = {
|
|
123
|
+
$schema: "https://fragments.dev/schema/v1.json",
|
|
124
|
+
name: componentName,
|
|
125
|
+
description,
|
|
126
|
+
usage: {
|
|
127
|
+
when: whenToUse,
|
|
128
|
+
doNot: []
|
|
129
|
+
},
|
|
130
|
+
meta: {
|
|
131
|
+
status
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
if (accessibility.role || accessibility.requirements && accessibility.requirements.length > 0) {
|
|
135
|
+
fragment.accessibility = accessibility;
|
|
136
|
+
}
|
|
137
|
+
return fragment;
|
|
138
|
+
}
|
|
139
|
+
function extractComponentNameFromStory(content, filePath) {
|
|
140
|
+
const titleMatch = content.match(/title:\s*['"](?:[^'"]+\/)?([^'"]+)['"]/);
|
|
141
|
+
if (titleMatch) {
|
|
142
|
+
return titleMatch[1];
|
|
143
|
+
}
|
|
144
|
+
const fileName = basename(filePath);
|
|
145
|
+
const componentName = fileName.replace(/\.stories\.(tsx?|jsx?)$/, "");
|
|
146
|
+
if (/^[A-Z]/.test(componentName)) {
|
|
147
|
+
return componentName;
|
|
148
|
+
}
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
function extractStoryNames(content) {
|
|
152
|
+
const names = [];
|
|
153
|
+
const exportMatches = content.matchAll(
|
|
154
|
+
/export\s+const\s+([A-Z][a-zA-Z0-9]*)\s*[=:]/g
|
|
155
|
+
);
|
|
156
|
+
for (const match of exportMatches) {
|
|
157
|
+
const name = match[1];
|
|
158
|
+
if (name !== "default" && !name.endsWith("Args") && !name.endsWith("Meta")) {
|
|
159
|
+
names.push(name);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return names;
|
|
163
|
+
}
|
|
164
|
+
function inferUsageFromStories(storyNames) {
|
|
165
|
+
const usage = [];
|
|
166
|
+
for (const name of storyNames) {
|
|
167
|
+
const sentence = name.replace(/([A-Z])/g, " $1").trim().toLowerCase();
|
|
168
|
+
if (["default", "primary", "basic", "example", "playground"].includes(
|
|
169
|
+
sentence.toLowerCase()
|
|
170
|
+
)) {
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
if (sentence.includes("loading")) {
|
|
174
|
+
usage.push("Showing loading states");
|
|
175
|
+
} else if (sentence.includes("disabled")) {
|
|
176
|
+
usage.push("Preventing user interaction");
|
|
177
|
+
} else if (sentence.includes("error")) {
|
|
178
|
+
usage.push("Displaying error states");
|
|
179
|
+
} else if (sentence.includes("success")) {
|
|
180
|
+
usage.push("Showing success feedback");
|
|
181
|
+
} else if (sentence.includes("empty")) {
|
|
182
|
+
usage.push("Handling empty states");
|
|
183
|
+
} else if (sentence.includes("with")) {
|
|
184
|
+
const withPart = sentence.replace("with ", "");
|
|
185
|
+
usage.push(`Displaying with ${withPart}`);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return usage;
|
|
189
|
+
}
|
|
190
|
+
function generateDescription(componentName, props) {
|
|
191
|
+
const words = componentName.replace(/([A-Z])/g, " $1").trim().toLowerCase();
|
|
192
|
+
const hasOnClick = "onClick" in props || "onPress" in props;
|
|
193
|
+
const hasValue = "value" in props || "defaultValue" in props;
|
|
194
|
+
const hasChildren = "children" in props;
|
|
195
|
+
if (hasOnClick && !hasValue) {
|
|
196
|
+
return `Interactive ${words} element for triggering actions`;
|
|
197
|
+
}
|
|
198
|
+
if (hasValue) {
|
|
199
|
+
return `Form ${words} for user input`;
|
|
200
|
+
}
|
|
201
|
+
if (hasChildren) {
|
|
202
|
+
return `Container ${words} for grouping content`;
|
|
203
|
+
}
|
|
204
|
+
return `${words.charAt(0).toUpperCase() + words.slice(1)} component`;
|
|
205
|
+
}
|
|
206
|
+
function inferAccessibility(props) {
|
|
207
|
+
const accessibility = {};
|
|
208
|
+
const hasOnClick = "onClick" in props || "onPress" in props;
|
|
209
|
+
const hasAriaLabel = "ariaLabel" in props || "aria-label" in props;
|
|
210
|
+
const hasRole = "role" in props;
|
|
211
|
+
const hasDisabled = "disabled" in props;
|
|
212
|
+
const hasHref = "href" in props;
|
|
213
|
+
if (hasOnClick && !hasHref) {
|
|
214
|
+
accessibility.role = "button";
|
|
215
|
+
} else if (hasHref) {
|
|
216
|
+
accessibility.role = "link";
|
|
217
|
+
}
|
|
218
|
+
const requirements = [];
|
|
219
|
+
if (hasOnClick && !hasAriaLabel) {
|
|
220
|
+
requirements.push("Should have visible text or aria-label");
|
|
221
|
+
}
|
|
222
|
+
if (hasDisabled) {
|
|
223
|
+
requirements.push("Disabled state should be conveyed to assistive technology");
|
|
224
|
+
}
|
|
225
|
+
if (requirements.length > 0) {
|
|
226
|
+
accessibility.requirements = requirements;
|
|
227
|
+
}
|
|
228
|
+
return accessibility;
|
|
229
|
+
}
|
|
230
|
+
function inferStatus(filePath) {
|
|
231
|
+
const lowerPath = filePath.toLowerCase();
|
|
232
|
+
if (lowerPath.includes("/experimental/") || lowerPath.includes("/labs/")) {
|
|
233
|
+
return "experimental";
|
|
234
|
+
}
|
|
235
|
+
if (lowerPath.includes("/beta/")) {
|
|
236
|
+
return "beta";
|
|
237
|
+
}
|
|
238
|
+
if (lowerPath.includes("/deprecated/") || lowerPath.includes("/legacy/")) {
|
|
239
|
+
return "deprecated";
|
|
240
|
+
}
|
|
241
|
+
if (lowerPath.includes("/draft/") || lowerPath.includes("/wip/")) {
|
|
242
|
+
return "draft";
|
|
243
|
+
}
|
|
244
|
+
return "stable";
|
|
245
|
+
}
|
|
246
|
+
export {
|
|
247
|
+
generate
|
|
248
|
+
};
|
|
249
|
+
//# sourceMappingURL=generate-4LQNJ7SX.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/generate.ts"],"sourcesContent":["/**\n * fragments generate - AI-assisted fragment generation\n *\n * Analyzes component source code and generates fragment files with:\n * - Extracted props from TypeScript\n * - Inferred usage patterns from story names\n * - Generated description\n */\n\nimport { readFile, writeFile, mkdir, access } from \"node:fs/promises\";\nimport { resolve, join, basename, dirname, relative } from \"node:path\";\nimport pc from \"picocolors\";\nimport fg from \"fast-glob\";\nimport { BRAND, type Fragment } from \"../core/index.js\";\nimport { extractPropsFromFile } from \"../core/node.js\";\n\nexport interface GenerateOptions {\n /** Project root directory */\n projectRoot?: string;\n /** Specific component name to generate (optional) */\n component?: string;\n /** Overwrite existing fragment files */\n force?: boolean;\n /** Pattern for component files */\n componentPattern?: string;\n}\n\nexport interface GenerateResult {\n success: boolean;\n generated: Array<{ name: string; path: string }>;\n skipped: Array<{ name: string; reason: string }>;\n errors: Array<{ name: string; error: string }>;\n}\n\n/**\n * Generate fragment files from component source code\n */\nexport async function generate(options: GenerateOptions = {}): Promise<GenerateResult> {\n const projectRoot = resolve(options.projectRoot || process.cwd());\n const generated: Array<{ name: string; path: string }> = [];\n const skipped: Array<{ name: string; reason: string }> = [];\n const errors: Array<{ name: string; error: string }> = [];\n\n console.log(pc.cyan(`\\n${BRAND.name} Generate\\n`));\n\n // Ensure .fragments/components directory exists\n const fragmentsDir = join(projectRoot, BRAND.dataDir, BRAND.componentsDir);\n await mkdir(fragmentsDir, { recursive: true });\n\n // Find component files\n const componentPattern =\n options.componentPattern ||\n \"src/components/**/*.tsx\";\n\n const componentFiles = await fg(componentPattern, {\n cwd: projectRoot,\n ignore: [\n \"**/node_modules/**\",\n \"**/*.stories.*\",\n \"**/*.segment.*\",\n \"**/*.test.*\",\n \"**/*.spec.*\",\n \"**/*.d.ts\",\n \"**/index.tsx\", // Often just re-exports\n ],\n absolute: true,\n });\n\n // Find story files for pattern inference\n const storyFiles = await fg([\"src/**/*.stories.tsx\", \"src/**/*.stories.ts\"], {\n cwd: projectRoot,\n ignore: [\"**/node_modules/**\"],\n absolute: true,\n });\n\n // Build story map for pattern inference\n const storyMap = new Map<string, string[]>();\n for (const storyFile of storyFiles) {\n try {\n const content = await readFile(storyFile, \"utf-8\");\n const componentName = extractComponentNameFromStory(content, storyFile);\n if (componentName) {\n const storyNames = extractStoryNames(content);\n storyMap.set(componentName, storyNames);\n }\n } catch {\n // Ignore parsing errors\n }\n }\n\n console.log(pc.dim(`Found ${componentFiles.length} component files\\n`));\n\n // Process each component file\n for (const filePath of componentFiles) {\n try {\n const extracted = extractPropsFromFile(filePath);\n\n if (!extracted || !extracted.componentName) {\n continue;\n }\n\n const componentName = extracted.componentName;\n\n // Filter by component name if specified\n if (options.component && componentName !== options.component) {\n continue;\n }\n\n // Check if fragment already exists\n const fragmentPath = join(fragmentsDir, `${componentName}${BRAND.fileExtension}`);\n let fragmentExists = false;\n try {\n await access(fragmentPath);\n fragmentExists = true;\n } catch {\n // Fragment doesn't exist\n }\n\n if (fragmentExists && !options.force) {\n skipped.push({ name: componentName, reason: \"Fragment already exists\" });\n console.log(pc.dim(` Skipping ${componentName} (fragment exists)`));\n continue;\n }\n\n // Generate fragment from analysis\n const fragment = generateFragmentFromComponent(\n componentName,\n extracted,\n filePath,\n storyMap.get(componentName) || []\n );\n\n // Write fragment file\n await writeFile(fragmentPath, JSON.stringify(fragment, null, 2), \"utf-8\");\n generated.push({ name: componentName, path: relative(projectRoot, fragmentPath) });\n console.log(pc.green(` ✓ Generated ${componentName}${BRAND.fileExtension}`));\n } catch (e) {\n const fileName = basename(filePath);\n errors.push({\n name: fileName,\n error: e instanceof Error ? e.message : String(e),\n });\n console.log(pc.red(` ✗ Failed: ${fileName}`));\n }\n }\n\n // Summary\n console.log();\n if (generated.length > 0) {\n console.log(pc.green(`✓ Generated ${generated.length} fragment(s)`));\n }\n if (skipped.length > 0) {\n console.log(pc.dim(` Skipped ${skipped.length} (use --force to overwrite)`));\n }\n if (errors.length > 0) {\n console.log(pc.yellow(` ${errors.length} error(s)`));\n }\n console.log();\n\n return {\n success: errors.length === 0,\n generated,\n skipped,\n errors,\n };\n}\n\n/**\n * Generate a Fragment from extracted component data\n */\nfunction generateFragmentFromComponent(\n componentName: string,\n extracted: ReturnType<typeof extractPropsFromFile> & { componentName: string },\n filePath: string,\n storyNames: string[]\n): Fragment {\n // Infer usage from story names\n const whenToUse = inferUsageFromStories(storyNames);\n\n // Generate description from component name\n const description = generateDescription(componentName, extracted.props);\n\n // Infer accessibility from props\n const accessibility = inferAccessibility(extracted.props);\n\n // Infer status from file path\n const status = inferStatus(filePath);\n\n const fragment: Fragment = {\n $schema: \"https://fragments.dev/schema/v1.json\",\n name: componentName,\n description,\n usage: {\n when: whenToUse,\n doNot: [],\n },\n meta: {\n status,\n },\n };\n\n // Add accessibility if any was inferred\n if (accessibility.role || (accessibility.requirements && accessibility.requirements.length > 0)) {\n fragment.accessibility = accessibility;\n }\n\n return fragment;\n}\n\n/**\n * Extract component name from story file\n */\nfunction extractComponentNameFromStory(content: string, filePath: string): string | null {\n // Try to extract from title in default export\n // e.g., export default { title: 'Components/Button' }\n const titleMatch = content.match(/title:\\s*['\"](?:[^'\"]+\\/)?([^'\"]+)['\"]/);\n if (titleMatch) {\n return titleMatch[1];\n }\n\n // Try to extract from file name\n const fileName = basename(filePath);\n const componentName = fileName.replace(/\\.stories\\.(tsx?|jsx?)$/, \"\");\n if (/^[A-Z]/.test(componentName)) {\n return componentName;\n }\n\n return null;\n}\n\n/**\n * Extract story names from story file content\n */\nfunction extractStoryNames(content: string): string[] {\n const names: string[] = [];\n\n // Match named exports that look like stories\n // e.g., export const Primary = ...\n const exportMatches = content.matchAll(\n /export\\s+const\\s+([A-Z][a-zA-Z0-9]*)\\s*[=:]/g\n );\n\n for (const match of exportMatches) {\n const name = match[1];\n if (name !== \"default\" && !name.endsWith(\"Args\") && !name.endsWith(\"Meta\")) {\n names.push(name);\n }\n }\n\n return names;\n}\n\n/**\n * Infer usage scenarios from story names\n */\nfunction inferUsageFromStories(storyNames: string[]): string[] {\n const usage: string[] = [];\n\n for (const name of storyNames) {\n // Convert PascalCase to sentence\n const sentence = name\n .replace(/([A-Z])/g, \" $1\")\n .trim()\n .toLowerCase();\n\n // Skip generic names\n if (\n [\"default\", \"primary\", \"basic\", \"example\", \"playground\"].includes(\n sentence.toLowerCase()\n )\n ) {\n continue;\n }\n\n // Generate usage from story name\n if (sentence.includes(\"loading\")) {\n usage.push(\"Showing loading states\");\n } else if (sentence.includes(\"disabled\")) {\n usage.push(\"Preventing user interaction\");\n } else if (sentence.includes(\"error\")) {\n usage.push(\"Displaying error states\");\n } else if (sentence.includes(\"success\")) {\n usage.push(\"Showing success feedback\");\n } else if (sentence.includes(\"empty\")) {\n usage.push(\"Handling empty states\");\n } else if (sentence.includes(\"with\")) {\n // \"WithIcon\" -> \"Adding icons\"\n const withPart = sentence.replace(\"with \", \"\");\n usage.push(`Displaying with ${withPart}`);\n }\n }\n\n return usage;\n}\n\n/**\n * Generate description from component name and props\n */\nfunction generateDescription(\n componentName: string,\n props: Record<string, unknown>\n): string {\n // Convert PascalCase to words\n const words = componentName\n .replace(/([A-Z])/g, \" $1\")\n .trim()\n .toLowerCase();\n\n // Detect component type from name or props\n const hasOnClick = \"onClick\" in props || \"onPress\" in props;\n const hasValue = \"value\" in props || \"defaultValue\" in props;\n const hasChildren = \"children\" in props;\n\n if (hasOnClick && !hasValue) {\n return `Interactive ${words} element for triggering actions`;\n }\n\n if (hasValue) {\n return `Form ${words} for user input`;\n }\n\n if (hasChildren) {\n return `Container ${words} for grouping content`;\n }\n\n return `${words.charAt(0).toUpperCase() + words.slice(1)} component`;\n}\n\n/**\n * Infer accessibility from props\n */\nfunction inferAccessibility(props: Record<string, unknown>): {\n role?: string;\n requirements?: string[];\n} {\n const accessibility: { role?: string; requirements?: string[] } = {};\n\n const hasOnClick = \"onClick\" in props || \"onPress\" in props;\n const hasAriaLabel = \"ariaLabel\" in props || \"aria-label\" in props;\n const hasRole = \"role\" in props;\n const hasDisabled = \"disabled\" in props;\n const hasHref = \"href\" in props;\n\n // Infer role\n if (hasOnClick && !hasHref) {\n accessibility.role = \"button\";\n } else if (hasHref) {\n accessibility.role = \"link\";\n }\n\n // Infer requirements\n const requirements: string[] = [];\n\n if (hasOnClick && !hasAriaLabel) {\n requirements.push(\"Should have visible text or aria-label\");\n }\n\n if (hasDisabled) {\n requirements.push(\"Disabled state should be conveyed to assistive technology\");\n }\n\n if (requirements.length > 0) {\n accessibility.requirements = requirements;\n }\n\n return accessibility;\n}\n\n/**\n * Infer status from file path\n */\nfunction inferStatus(\n filePath: string\n): \"draft\" | \"experimental\" | \"beta\" | \"stable\" | \"deprecated\" {\n const lowerPath = filePath.toLowerCase();\n\n if (lowerPath.includes(\"/experimental/\") || lowerPath.includes(\"/labs/\")) {\n return \"experimental\";\n }\n if (lowerPath.includes(\"/beta/\")) {\n return \"beta\";\n }\n if (lowerPath.includes(\"/deprecated/\") || lowerPath.includes(\"/legacy/\")) {\n return \"deprecated\";\n }\n if (lowerPath.includes(\"/draft/\") || lowerPath.includes(\"/wip/\")) {\n return \"draft\";\n }\n\n return \"stable\";\n}\n"],"mappings":";;;;;;;;;;AASA,SAAS,UAAU,WAAW,OAAO,cAAc;AACnD,SAAS,SAAS,MAAM,UAAmB,gBAAgB;AAC3D,OAAO,QAAQ;AACf,OAAO,QAAQ;AAyBf,eAAsB,SAAS,UAA2B,CAAC,GAA4B;AACrF,QAAM,cAAc,QAAQ,QAAQ,eAAe,QAAQ,IAAI,CAAC;AAChE,QAAM,YAAmD,CAAC;AAC1D,QAAM,UAAmD,CAAC;AAC1D,QAAM,SAAiD,CAAC;AAExD,UAAQ,IAAI,GAAG,KAAK;AAAA,EAAK,MAAM,IAAI;AAAA,CAAa,CAAC;AAGjD,QAAM,eAAe,KAAK,aAAa,MAAM,SAAS,MAAM,aAAa;AACzE,QAAM,MAAM,cAAc,EAAE,WAAW,KAAK,CAAC;AAG7C,QAAM,mBACJ,QAAQ,oBACR;AAEF,QAAM,iBAAiB,MAAM,GAAG,kBAAkB;AAAA,IAChD,KAAK;AAAA,IACL,QAAQ;AAAA,MACN;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,IACF;AAAA,IACA,UAAU;AAAA,EACZ,CAAC;AAGD,QAAM,aAAa,MAAM,GAAG,CAAC,wBAAwB,qBAAqB,GAAG;AAAA,IAC3E,KAAK;AAAA,IACL,QAAQ,CAAC,oBAAoB;AAAA,IAC7B,UAAU;AAAA,EACZ,CAAC;AAGD,QAAM,WAAW,oBAAI,IAAsB;AAC3C,aAAW,aAAa,YAAY;AAClC,QAAI;AACF,YAAM,UAAU,MAAM,SAAS,WAAW,OAAO;AACjD,YAAM,gBAAgB,8BAA8B,SAAS,SAAS;AACtE,UAAI,eAAe;AACjB,cAAM,aAAa,kBAAkB,OAAO;AAC5C,iBAAS,IAAI,eAAe,UAAU;AAAA,MACxC;AAAA,IACF,QAAQ;AAAA,IAER;AAAA,EACF;AAEA,UAAQ,IAAI,GAAG,IAAI,SAAS,eAAe,MAAM;AAAA,CAAoB,CAAC;AAGtE,aAAW,YAAY,gBAAgB;AACrC,QAAI;AACF,YAAM,YAAY,qBAAqB,QAAQ;AAE/C,UAAI,CAAC,aAAa,CAAC,UAAU,eAAe;AAC1C;AAAA,MACF;AAEA,YAAM,gBAAgB,UAAU;AAGhC,UAAI,QAAQ,aAAa,kBAAkB,QAAQ,WAAW;AAC5D;AAAA,MACF;AAGA,YAAM,eAAe,KAAK,cAAc,GAAG,aAAa,GAAG,MAAM,aAAa,EAAE;AAChF,UAAI,iBAAiB;AACrB,UAAI;AACF,cAAM,OAAO,YAAY;AACzB,yBAAiB;AAAA,MACnB,QAAQ;AAAA,MAER;AAEA,UAAI,kBAAkB,CAAC,QAAQ,OAAO;AACpC,gBAAQ,KAAK,EAAE,MAAM,eAAe,QAAQ,0BAA0B,CAAC;AACvE,gBAAQ,IAAI,GAAG,IAAI,cAAc,aAAa,oBAAoB,CAAC;AACnE;AAAA,MACF;AAGA,YAAM,WAAW;AAAA,QACf;AAAA,QACA;AAAA,QACA;AAAA,QACA,SAAS,IAAI,aAAa,KAAK,CAAC;AAAA,MAClC;AAGA,YAAM,UAAU,cAAc,KAAK,UAAU,UAAU,MAAM,CAAC,GAAG,OAAO;AACxE,gBAAU,KAAK,EAAE,MAAM,eAAe,MAAM,SAAS,aAAa,YAAY,EAAE,CAAC;AACjF,cAAQ,IAAI,GAAG,MAAM,sBAAiB,aAAa,GAAG,MAAM,aAAa,EAAE,CAAC;AAAA,IAC9E,SAAS,GAAG;AACV,YAAM,WAAW,SAAS,QAAQ;AAClC,aAAO,KAAK;AAAA,QACV,MAAM;AAAA,QACN,OAAO,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC;AAAA,MAClD,CAAC;AACD,cAAQ,IAAI,GAAG,IAAI,oBAAe,QAAQ,EAAE,CAAC;AAAA,IAC/C;AAAA,EACF;AAGA,UAAQ,IAAI;AACZ,MAAI,UAAU,SAAS,GAAG;AACxB,YAAQ,IAAI,GAAG,MAAM,oBAAe,UAAU,MAAM,cAAc,CAAC;AAAA,EACrE;AACA,MAAI,QAAQ,SAAS,GAAG;AACtB,YAAQ,IAAI,GAAG,IAAI,aAAa,QAAQ,MAAM,6BAA6B,CAAC;AAAA,EAC9E;AACA,MAAI,OAAO,SAAS,GAAG;AACrB,YAAQ,IAAI,GAAG,OAAO,KAAK,OAAO,MAAM,WAAW,CAAC;AAAA,EACtD;AACA,UAAQ,IAAI;AAEZ,SAAO;AAAA,IACL,SAAS,OAAO,WAAW;AAAA,IAC3B;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAKA,SAAS,8BACP,eACA,WACA,UACA,YACU;AAEV,QAAM,YAAY,sBAAsB,UAAU;AAGlD,QAAM,cAAc,oBAAoB,eAAe,UAAU,KAAK;AAGtE,QAAM,gBAAgB,mBAAmB,UAAU,KAAK;AAGxD,QAAM,SAAS,YAAY,QAAQ;AAEnC,QAAM,WAAqB;AAAA,IACzB,SAAS;AAAA,IACT,MAAM;AAAA,IACN;AAAA,IACA,OAAO;AAAA,MACL,MAAM;AAAA,MACN,OAAO,CAAC;AAAA,IACV;AAAA,IACA,MAAM;AAAA,MACJ;AAAA,IACF;AAAA,EACF;AAGA,MAAI,cAAc,QAAS,cAAc,gBAAgB,cAAc,aAAa,SAAS,GAAI;AAC/F,aAAS,gBAAgB;AAAA,EAC3B;AAEA,SAAO;AACT;AAKA,SAAS,8BAA8B,SAAiB,UAAiC;AAGvF,QAAM,aAAa,QAAQ,MAAM,wCAAwC;AACzE,MAAI,YAAY;AACd,WAAO,WAAW,CAAC;AAAA,EACrB;AAGA,QAAM,WAAW,SAAS,QAAQ;AAClC,QAAM,gBAAgB,SAAS,QAAQ,2BAA2B,EAAE;AACpE,MAAI,SAAS,KAAK,aAAa,GAAG;AAChC,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAKA,SAAS,kBAAkB,SAA2B;AACpD,QAAM,QAAkB,CAAC;AAIzB,QAAM,gBAAgB,QAAQ;AAAA,IAC5B;AAAA,EACF;AAEA,aAAW,SAAS,eAAe;AACjC,UAAM,OAAO,MAAM,CAAC;AACpB,QAAI,SAAS,aAAa,CAAC,KAAK,SAAS,MAAM,KAAK,CAAC,KAAK,SAAS,MAAM,GAAG;AAC1E,YAAM,KAAK,IAAI;AAAA,IACjB;AAAA,EACF;AAEA,SAAO;AACT;AAKA,SAAS,sBAAsB,YAAgC;AAC7D,QAAM,QAAkB,CAAC;AAEzB,aAAW,QAAQ,YAAY;AAE7B,UAAM,WAAW,KACd,QAAQ,YAAY,KAAK,EACzB,KAAK,EACL,YAAY;AAGf,QACE,CAAC,WAAW,WAAW,SAAS,WAAW,YAAY,EAAE;AAAA,MACvD,SAAS,YAAY;AAAA,IACvB,GACA;AACA;AAAA,IACF;AAGA,QAAI,SAAS,SAAS,SAAS,GAAG;AAChC,YAAM,KAAK,wBAAwB;AAAA,IACrC,WAAW,SAAS,SAAS,UAAU,GAAG;AACxC,YAAM,KAAK,6BAA6B;AAAA,IAC1C,WAAW,SAAS,SAAS,OAAO,GAAG;AACrC,YAAM,KAAK,yBAAyB;AAAA,IACtC,WAAW,SAAS,SAAS,SAAS,GAAG;AACvC,YAAM,KAAK,0BAA0B;AAAA,IACvC,WAAW,SAAS,SAAS,OAAO,GAAG;AACrC,YAAM,KAAK,uBAAuB;AAAA,IACpC,WAAW,SAAS,SAAS,MAAM,GAAG;AAEpC,YAAM,WAAW,SAAS,QAAQ,SAAS,EAAE;AAC7C,YAAM,KAAK,mBAAmB,QAAQ,EAAE;AAAA,IAC1C;AAAA,EACF;AAEA,SAAO;AACT;AAKA,SAAS,oBACP,eACA,OACQ;AAER,QAAM,QAAQ,cACX,QAAQ,YAAY,KAAK,EACzB,KAAK,EACL,YAAY;AAGf,QAAM,aAAa,aAAa,SAAS,aAAa;AACtD,QAAM,WAAW,WAAW,SAAS,kBAAkB;AACvD,QAAM,cAAc,cAAc;AAElC,MAAI,cAAc,CAAC,UAAU;AAC3B,WAAO,eAAe,KAAK;AAAA,EAC7B;AAEA,MAAI,UAAU;AACZ,WAAO,QAAQ,KAAK;AAAA,EACtB;AAEA,MAAI,aAAa;AACf,WAAO,aAAa,KAAK;AAAA,EAC3B;AAEA,SAAO,GAAG,MAAM,OAAO,CAAC,EAAE,YAAY,IAAI,MAAM,MAAM,CAAC,CAAC;AAC1D;AAKA,SAAS,mBAAmB,OAG1B;AACA,QAAM,gBAA4D,CAAC;AAEnE,QAAM,aAAa,aAAa,SAAS,aAAa;AACtD,QAAM,eAAe,eAAe,SAAS,gBAAgB;AAC7D,QAAM,UAAU,UAAU;AAC1B,QAAM,cAAc,cAAc;AAClC,QAAM,UAAU,UAAU;AAG1B,MAAI,cAAc,CAAC,SAAS;AAC1B,kBAAc,OAAO;AAAA,EACvB,WAAW,SAAS;AAClB,kBAAc,OAAO;AAAA,EACvB;AAGA,QAAM,eAAyB,CAAC;AAEhC,MAAI,cAAc,CAAC,cAAc;AAC/B,iBAAa,KAAK,wCAAwC;AAAA,EAC5D;AAEA,MAAI,aAAa;AACf,iBAAa,KAAK,2DAA2D;AAAA,EAC/E;AAEA,MAAI,aAAa,SAAS,GAAG;AAC3B,kBAAc,eAAe;AAAA,EAC/B;AAEA,SAAO;AACT;AAKA,SAAS,YACP,UAC6D;AAC7D,QAAM,YAAY,SAAS,YAAY;AAEvC,MAAI,UAAU,SAAS,gBAAgB,KAAK,UAAU,SAAS,QAAQ,GAAG;AACxE,WAAO;AAAA,EACT;AACA,MAAI,UAAU,SAAS,QAAQ,GAAG;AAChC,WAAO;AAAA,EACT;AACA,MAAI,UAAU,SAAS,cAAc,KAAK,UAAU,SAAS,UAAU,GAAG;AACxE,WAAO;AAAA,EACT;AACA,MAAI,UAAU,SAAS,SAAS,KAAK,UAAU,SAAS,OAAO,GAAG;AAChE,WAAO;AAAA,EACT;AAEA,SAAO;AACT;","names":[]}
|