@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,551 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AST-Based Segment Parser
|
|
3
|
+
*
|
|
4
|
+
* Extracts segment metadata from .segment.tsx files WITHOUT executing them.
|
|
5
|
+
* This allows `segments build` to work without any project dependencies.
|
|
6
|
+
*
|
|
7
|
+
* Uses TypeScript's compiler API to parse and analyze the AST.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import ts from "typescript";
|
|
11
|
+
import type { SegmentMeta, SegmentUsage, PropDefinition } from "./types.js";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Parsed segment metadata (extracted statically from AST)
|
|
15
|
+
*/
|
|
16
|
+
export interface ParsedSegmentMetadata {
|
|
17
|
+
/** Component import path (e.g., "./Button" or "@/components/Button") */
|
|
18
|
+
componentImport: string | null;
|
|
19
|
+
|
|
20
|
+
/** Component name as used in the file */
|
|
21
|
+
componentName: string | null;
|
|
22
|
+
|
|
23
|
+
/** Component metadata */
|
|
24
|
+
meta: Partial<SegmentMeta>;
|
|
25
|
+
|
|
26
|
+
/** Usage guidelines */
|
|
27
|
+
usage: Partial<SegmentUsage>;
|
|
28
|
+
|
|
29
|
+
/** Props documentation */
|
|
30
|
+
props: Record<string, Partial<PropDefinition>>;
|
|
31
|
+
|
|
32
|
+
/** Variant definitions */
|
|
33
|
+
variants: Array<{
|
|
34
|
+
name: string;
|
|
35
|
+
description: string;
|
|
36
|
+
code?: string;
|
|
37
|
+
figma?: string;
|
|
38
|
+
args?: Record<string, unknown>;
|
|
39
|
+
}>;
|
|
40
|
+
|
|
41
|
+
/** Related components */
|
|
42
|
+
relations: Array<{
|
|
43
|
+
component: string;
|
|
44
|
+
relationship: string;
|
|
45
|
+
note: string;
|
|
46
|
+
}>;
|
|
47
|
+
|
|
48
|
+
/** Parse warnings */
|
|
49
|
+
warnings: string[];
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Parse a segment file and extract metadata without executing it.
|
|
54
|
+
*/
|
|
55
|
+
export function parseSegmentFile(
|
|
56
|
+
fileContent: string,
|
|
57
|
+
filePath?: string
|
|
58
|
+
): ParsedSegmentMetadata {
|
|
59
|
+
const warnings: string[] = [];
|
|
60
|
+
|
|
61
|
+
// Parse the file
|
|
62
|
+
const sourceFile = ts.createSourceFile(
|
|
63
|
+
filePath ?? "segment.tsx",
|
|
64
|
+
fileContent,
|
|
65
|
+
ts.ScriptTarget.Latest,
|
|
66
|
+
true,
|
|
67
|
+
ts.ScriptKind.TSX
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
// Find imports
|
|
71
|
+
const imports = extractImports(sourceFile);
|
|
72
|
+
|
|
73
|
+
// Find the defineSegment call
|
|
74
|
+
const defineSegmentCall = findDefineSegmentCall(sourceFile);
|
|
75
|
+
|
|
76
|
+
if (!defineSegmentCall) {
|
|
77
|
+
warnings.push("No defineSegment() call found");
|
|
78
|
+
return {
|
|
79
|
+
componentImport: null,
|
|
80
|
+
componentName: null,
|
|
81
|
+
meta: {},
|
|
82
|
+
usage: { when: [], whenNot: [] },
|
|
83
|
+
props: {},
|
|
84
|
+
variants: [],
|
|
85
|
+
relations: [],
|
|
86
|
+
warnings,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Extract the argument (object literal)
|
|
91
|
+
const arg = defineSegmentCall.arguments[0];
|
|
92
|
+
if (!arg || !ts.isObjectLiteralExpression(arg)) {
|
|
93
|
+
warnings.push("defineSegment() argument is not an object literal");
|
|
94
|
+
return {
|
|
95
|
+
componentImport: null,
|
|
96
|
+
componentName: null,
|
|
97
|
+
meta: {},
|
|
98
|
+
usage: { when: [], whenNot: [] },
|
|
99
|
+
props: {},
|
|
100
|
+
variants: [],
|
|
101
|
+
relations: [],
|
|
102
|
+
warnings,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Extract component reference
|
|
107
|
+
const componentProp = findProperty(arg, "component");
|
|
108
|
+
let componentName: string | null = null;
|
|
109
|
+
let componentImport: string | null = null;
|
|
110
|
+
|
|
111
|
+
if (componentProp && ts.isIdentifier(componentProp)) {
|
|
112
|
+
componentName = componentProp.text;
|
|
113
|
+
// Find the import for this component
|
|
114
|
+
componentImport = imports.get(componentName) ?? null;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Extract meta
|
|
118
|
+
const meta = extractMeta(arg, warnings);
|
|
119
|
+
|
|
120
|
+
// Extract usage
|
|
121
|
+
const usage = extractUsage(arg, warnings);
|
|
122
|
+
|
|
123
|
+
// Extract props
|
|
124
|
+
const props = extractProps(arg, warnings);
|
|
125
|
+
|
|
126
|
+
// Extract variants
|
|
127
|
+
const variants = extractVariants(arg, sourceFile, warnings);
|
|
128
|
+
|
|
129
|
+
// Extract relations
|
|
130
|
+
const relations = extractRelations(arg, warnings);
|
|
131
|
+
|
|
132
|
+
return {
|
|
133
|
+
componentImport,
|
|
134
|
+
componentName,
|
|
135
|
+
meta,
|
|
136
|
+
usage,
|
|
137
|
+
props,
|
|
138
|
+
variants,
|
|
139
|
+
relations,
|
|
140
|
+
warnings,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Extract all imports from the source file.
|
|
146
|
+
* Returns a map of imported name -> module path
|
|
147
|
+
*/
|
|
148
|
+
function extractImports(sourceFile: ts.SourceFile): Map<string, string> {
|
|
149
|
+
const imports = new Map<string, string>();
|
|
150
|
+
|
|
151
|
+
ts.forEachChild(sourceFile, (node) => {
|
|
152
|
+
if (ts.isImportDeclaration(node)) {
|
|
153
|
+
const moduleSpecifier = node.moduleSpecifier;
|
|
154
|
+
if (ts.isStringLiteral(moduleSpecifier)) {
|
|
155
|
+
const modulePath = moduleSpecifier.text;
|
|
156
|
+
|
|
157
|
+
// Handle named imports
|
|
158
|
+
const importClause = node.importClause;
|
|
159
|
+
if (importClause) {
|
|
160
|
+
// Default import
|
|
161
|
+
if (importClause.name) {
|
|
162
|
+
imports.set(importClause.name.text, modulePath);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Named imports
|
|
166
|
+
const namedBindings = importClause.namedBindings;
|
|
167
|
+
if (namedBindings && ts.isNamedImports(namedBindings)) {
|
|
168
|
+
for (const element of namedBindings.elements) {
|
|
169
|
+
imports.set(element.name.text, modulePath);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
return imports;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Find the defineSegment() call in the source file.
|
|
182
|
+
*/
|
|
183
|
+
function findDefineSegmentCall(
|
|
184
|
+
sourceFile: ts.SourceFile
|
|
185
|
+
): ts.CallExpression | null {
|
|
186
|
+
let result: ts.CallExpression | null = null;
|
|
187
|
+
|
|
188
|
+
function visit(node: ts.Node): void {
|
|
189
|
+
if (ts.isCallExpression(node)) {
|
|
190
|
+
const expression = node.expression;
|
|
191
|
+
if (ts.isIdentifier(expression) && expression.text === "defineSegment") {
|
|
192
|
+
result = node;
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
ts.forEachChild(node, visit);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
visit(sourceFile);
|
|
200
|
+
return result;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Find a property in an object literal by name.
|
|
205
|
+
*/
|
|
206
|
+
function findProperty(
|
|
207
|
+
obj: ts.ObjectLiteralExpression,
|
|
208
|
+
name: string
|
|
209
|
+
): ts.Expression | null {
|
|
210
|
+
for (const prop of obj.properties) {
|
|
211
|
+
if (ts.isPropertyAssignment(prop)) {
|
|
212
|
+
const propName = prop.name;
|
|
213
|
+
if (ts.isIdentifier(propName) && propName.text === name) {
|
|
214
|
+
return prop.initializer;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
return null;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Extract meta object from defineSegment call.
|
|
223
|
+
*/
|
|
224
|
+
function extractMeta(
|
|
225
|
+
arg: ts.ObjectLiteralExpression,
|
|
226
|
+
warnings: string[]
|
|
227
|
+
): Partial<SegmentMeta> {
|
|
228
|
+
const metaProp = findProperty(arg, "meta");
|
|
229
|
+
if (!metaProp || !ts.isObjectLiteralExpression(metaProp)) {
|
|
230
|
+
warnings.push("No meta object found");
|
|
231
|
+
return {};
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const meta: Partial<SegmentMeta> = {};
|
|
235
|
+
|
|
236
|
+
// Extract string properties
|
|
237
|
+
const name = extractStringProperty(metaProp, "name");
|
|
238
|
+
if (name) meta.name = name;
|
|
239
|
+
|
|
240
|
+
const description = extractStringProperty(metaProp, "description");
|
|
241
|
+
if (description) meta.description = description;
|
|
242
|
+
|
|
243
|
+
const category = extractStringProperty(metaProp, "category");
|
|
244
|
+
if (category) meta.category = category;
|
|
245
|
+
|
|
246
|
+
const status = extractStringProperty(metaProp, "status") as SegmentMeta["status"];
|
|
247
|
+
if (status) meta.status = status;
|
|
248
|
+
|
|
249
|
+
const since = extractStringProperty(metaProp, "since");
|
|
250
|
+
if (since) meta.since = since;
|
|
251
|
+
|
|
252
|
+
// Extract figma URL
|
|
253
|
+
const figma = extractStringProperty(metaProp, "figma");
|
|
254
|
+
if (figma) meta.figma = figma;
|
|
255
|
+
|
|
256
|
+
// Extract tags array
|
|
257
|
+
const tags = extractStringArray(metaProp, "tags");
|
|
258
|
+
if (tags.length > 0) meta.tags = tags;
|
|
259
|
+
|
|
260
|
+
return meta;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Extract usage object from defineSegment call.
|
|
265
|
+
*/
|
|
266
|
+
function extractUsage(
|
|
267
|
+
arg: ts.ObjectLiteralExpression,
|
|
268
|
+
warnings: string[]
|
|
269
|
+
): Partial<SegmentUsage> {
|
|
270
|
+
const usageProp = findProperty(arg, "usage");
|
|
271
|
+
if (!usageProp || !ts.isObjectLiteralExpression(usageProp)) {
|
|
272
|
+
return { when: [], whenNot: [] };
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
return {
|
|
276
|
+
when: extractStringArray(usageProp, "when"),
|
|
277
|
+
whenNot: extractStringArray(usageProp, "whenNot"),
|
|
278
|
+
guidelines: extractStringArray(usageProp, "guidelines"),
|
|
279
|
+
accessibility: extractStringArray(usageProp, "accessibility"),
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Extract props object from defineSegment call.
|
|
285
|
+
*/
|
|
286
|
+
function extractProps(
|
|
287
|
+
arg: ts.ObjectLiteralExpression,
|
|
288
|
+
warnings: string[]
|
|
289
|
+
): Record<string, Partial<PropDefinition>> {
|
|
290
|
+
const propsProp = findProperty(arg, "props");
|
|
291
|
+
if (!propsProp || !ts.isObjectLiteralExpression(propsProp)) {
|
|
292
|
+
return {};
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
const props: Record<string, Partial<PropDefinition>> = {};
|
|
296
|
+
|
|
297
|
+
for (const prop of propsProp.properties) {
|
|
298
|
+
if (ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name)) {
|
|
299
|
+
const propName = prop.name.text;
|
|
300
|
+
const propValue = prop.initializer;
|
|
301
|
+
|
|
302
|
+
if (ts.isObjectLiteralExpression(propValue)) {
|
|
303
|
+
props[propName] = extractPropDefinition(propValue);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
return props;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Extract a single prop definition.
|
|
313
|
+
*/
|
|
314
|
+
function extractPropDefinition(
|
|
315
|
+
obj: ts.ObjectLiteralExpression
|
|
316
|
+
): Partial<PropDefinition> {
|
|
317
|
+
const def: Partial<PropDefinition> = {};
|
|
318
|
+
|
|
319
|
+
const type = extractStringProperty(obj, "type");
|
|
320
|
+
if (type) def.type = type as PropDefinition["type"];
|
|
321
|
+
|
|
322
|
+
const description = extractStringProperty(obj, "description");
|
|
323
|
+
if (description) def.description = description;
|
|
324
|
+
|
|
325
|
+
const values = extractStringArray(obj, "values");
|
|
326
|
+
if (values.length > 0) def.values = values;
|
|
327
|
+
|
|
328
|
+
const required = extractBooleanProperty(obj, "required");
|
|
329
|
+
if (required !== null) def.required = required;
|
|
330
|
+
|
|
331
|
+
// Default value - try to extract as literal
|
|
332
|
+
const defaultProp = findProperty(obj, "default");
|
|
333
|
+
if (defaultProp) {
|
|
334
|
+
def.default = extractLiteralValue(defaultProp);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
const constraints = extractStringArray(obj, "constraints");
|
|
338
|
+
if (constraints.length > 0) def.constraints = constraints;
|
|
339
|
+
|
|
340
|
+
return def;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Extract variants array from defineSegment call.
|
|
345
|
+
*/
|
|
346
|
+
function extractVariants(
|
|
347
|
+
arg: ts.ObjectLiteralExpression,
|
|
348
|
+
sourceFile: ts.SourceFile,
|
|
349
|
+
warnings: string[]
|
|
350
|
+
): Array<{ name: string; description: string; code?: string; figma?: string; args?: Record<string, unknown> }> {
|
|
351
|
+
const variantsProp = findProperty(arg, "variants");
|
|
352
|
+
if (!variantsProp || !ts.isArrayLiteralExpression(variantsProp)) {
|
|
353
|
+
return [];
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
const variants: Array<{ name: string; description: string; code?: string; figma?: string; args?: Record<string, unknown> }> = [];
|
|
357
|
+
|
|
358
|
+
for (const element of variantsProp.elements) {
|
|
359
|
+
if (ts.isObjectLiteralExpression(element)) {
|
|
360
|
+
const name = extractStringProperty(element, "name");
|
|
361
|
+
const description = extractStringProperty(element, "description");
|
|
362
|
+
|
|
363
|
+
if (name) {
|
|
364
|
+
const variant: { name: string; description: string; code?: string; figma?: string; args?: Record<string, unknown> } = {
|
|
365
|
+
name,
|
|
366
|
+
description: description ?? "",
|
|
367
|
+
};
|
|
368
|
+
|
|
369
|
+
// Try to extract code property if present
|
|
370
|
+
const codeProp = findProperty(element, "code");
|
|
371
|
+
if (codeProp && ts.isStringLiteral(codeProp)) {
|
|
372
|
+
variant.code = codeProp.text;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// Try to extract render function and convert to code string
|
|
376
|
+
const renderProp = findProperty(element, "render");
|
|
377
|
+
if (renderProp && !variant.code) {
|
|
378
|
+
variant.code = extractRenderCode(renderProp, sourceFile);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// Extract figma URL for variant
|
|
382
|
+
const figma = extractStringProperty(element, "figma");
|
|
383
|
+
if (figma) {
|
|
384
|
+
variant.figma = figma;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// Extract args object for code generation
|
|
388
|
+
const argsProp = findProperty(element, "args");
|
|
389
|
+
if (argsProp && ts.isObjectLiteralExpression(argsProp)) {
|
|
390
|
+
const argsValue = extractLiteralValue(argsProp);
|
|
391
|
+
if (argsValue && typeof argsValue === 'object' && !Array.isArray(argsValue)) {
|
|
392
|
+
variant.args = argsValue as Record<string, unknown>;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
variants.push(variant);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
return variants;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/**
|
|
405
|
+
* Extract the code from a render function.
|
|
406
|
+
*/
|
|
407
|
+
function extractRenderCode(
|
|
408
|
+
renderProp: ts.Expression,
|
|
409
|
+
sourceFile: ts.SourceFile
|
|
410
|
+
): string | undefined {
|
|
411
|
+
// Handle arrow function: () => <Component />
|
|
412
|
+
if (ts.isArrowFunction(renderProp)) {
|
|
413
|
+
const body = renderProp.body;
|
|
414
|
+
// Get the source text of the body
|
|
415
|
+
const start = body.getStart(sourceFile);
|
|
416
|
+
const end = body.getEnd();
|
|
417
|
+
let code = sourceFile.text.substring(start, end).trim();
|
|
418
|
+
|
|
419
|
+
// Remove parentheses if present
|
|
420
|
+
if (code.startsWith("(") && code.endsWith(")")) {
|
|
421
|
+
code = code.slice(1, -1).trim();
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
return code;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
return undefined;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
/**
|
|
431
|
+
* Extract relations array from defineSegment call.
|
|
432
|
+
*/
|
|
433
|
+
function extractRelations(
|
|
434
|
+
arg: ts.ObjectLiteralExpression,
|
|
435
|
+
warnings: string[]
|
|
436
|
+
): Array<{ component: string; relationship: string; note: string }> {
|
|
437
|
+
const relationsProp = findProperty(arg, "relations");
|
|
438
|
+
if (!relationsProp || !ts.isArrayLiteralExpression(relationsProp)) {
|
|
439
|
+
return [];
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
const relations: Array<{ component: string; relationship: string; note: string }> = [];
|
|
443
|
+
|
|
444
|
+
for (const element of relationsProp.elements) {
|
|
445
|
+
if (ts.isObjectLiteralExpression(element)) {
|
|
446
|
+
const component = extractStringProperty(element, "component");
|
|
447
|
+
const relationship = extractStringProperty(element, "relationship");
|
|
448
|
+
const note = extractStringProperty(element, "note");
|
|
449
|
+
|
|
450
|
+
if (component && relationship) {
|
|
451
|
+
relations.push({
|
|
452
|
+
component,
|
|
453
|
+
relationship,
|
|
454
|
+
note: note ?? "",
|
|
455
|
+
});
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
return relations;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
/**
|
|
464
|
+
* Extract a string property from an object literal.
|
|
465
|
+
*/
|
|
466
|
+
function extractStringProperty(
|
|
467
|
+
obj: ts.ObjectLiteralExpression,
|
|
468
|
+
name: string
|
|
469
|
+
): string | null {
|
|
470
|
+
const prop = findProperty(obj, name);
|
|
471
|
+
if (prop && ts.isStringLiteral(prop)) {
|
|
472
|
+
return prop.text;
|
|
473
|
+
}
|
|
474
|
+
// Handle template literals without expressions
|
|
475
|
+
if (prop && ts.isNoSubstitutionTemplateLiteral(prop)) {
|
|
476
|
+
return prop.text;
|
|
477
|
+
}
|
|
478
|
+
return null;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
/**
|
|
482
|
+
* Extract a boolean property from an object literal.
|
|
483
|
+
*/
|
|
484
|
+
function extractBooleanProperty(
|
|
485
|
+
obj: ts.ObjectLiteralExpression,
|
|
486
|
+
name: string
|
|
487
|
+
): boolean | null {
|
|
488
|
+
const prop = findProperty(obj, name);
|
|
489
|
+
if (prop) {
|
|
490
|
+
if (prop.kind === ts.SyntaxKind.TrueKeyword) return true;
|
|
491
|
+
if (prop.kind === ts.SyntaxKind.FalseKeyword) return false;
|
|
492
|
+
}
|
|
493
|
+
return null;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
/**
|
|
497
|
+
* Extract a string array from an object literal.
|
|
498
|
+
*/
|
|
499
|
+
function extractStringArray(
|
|
500
|
+
obj: ts.ObjectLiteralExpression,
|
|
501
|
+
name: string
|
|
502
|
+
): string[] {
|
|
503
|
+
const prop = findProperty(obj, name);
|
|
504
|
+
if (!prop || !ts.isArrayLiteralExpression(prop)) {
|
|
505
|
+
return [];
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
const result: string[] = [];
|
|
509
|
+
for (const element of prop.elements) {
|
|
510
|
+
if (ts.isStringLiteral(element)) {
|
|
511
|
+
result.push(element.text);
|
|
512
|
+
} else if (ts.isNoSubstitutionTemplateLiteral(element)) {
|
|
513
|
+
result.push(element.text);
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
return result;
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
/**
|
|
520
|
+
* Extract a literal value from an expression.
|
|
521
|
+
*/
|
|
522
|
+
function extractLiteralValue(expr: ts.Expression): unknown {
|
|
523
|
+
if (ts.isStringLiteral(expr)) {
|
|
524
|
+
return expr.text;
|
|
525
|
+
}
|
|
526
|
+
if (ts.isNumericLiteral(expr)) {
|
|
527
|
+
return Number(expr.text);
|
|
528
|
+
}
|
|
529
|
+
if (expr.kind === ts.SyntaxKind.TrueKeyword) {
|
|
530
|
+
return true;
|
|
531
|
+
}
|
|
532
|
+
if (expr.kind === ts.SyntaxKind.FalseKeyword) {
|
|
533
|
+
return false;
|
|
534
|
+
}
|
|
535
|
+
if (expr.kind === ts.SyntaxKind.NullKeyword) {
|
|
536
|
+
return null;
|
|
537
|
+
}
|
|
538
|
+
if (ts.isArrayLiteralExpression(expr)) {
|
|
539
|
+
return expr.elements.map(extractLiteralValue);
|
|
540
|
+
}
|
|
541
|
+
if (ts.isObjectLiteralExpression(expr)) {
|
|
542
|
+
const obj: Record<string, unknown> = {};
|
|
543
|
+
for (const prop of expr.properties) {
|
|
544
|
+
if (ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name)) {
|
|
545
|
+
obj[prop.name.text] = extractLiteralValue(prop.initializer);
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
return obj;
|
|
549
|
+
}
|
|
550
|
+
return undefined;
|
|
551
|
+
}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Loader for Storybook .storybook/preview.{tsx,ts,jsx,js} configuration.
|
|
3
|
+
*
|
|
4
|
+
* This module detects and loads Storybook preview configuration to extract:
|
|
5
|
+
* - decorators: Global decorators applied to all stories
|
|
6
|
+
* - parameters: Global parameters for all stories
|
|
7
|
+
* - globalTypes: Global type definitions (e.g., for toolbar controls)
|
|
8
|
+
* - args: Default args for all stories
|
|
9
|
+
* - argTypes: Default argTypes for all stories
|
|
10
|
+
* - loaders: Global loaders for all stories
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { existsSync } from "node:fs";
|
|
14
|
+
import { join, resolve } from "node:path";
|
|
15
|
+
import type { PreviewConfig } from "./storyAdapter.js";
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Possible file extensions and names for preview config
|
|
19
|
+
*/
|
|
20
|
+
const PREVIEW_FILES = [
|
|
21
|
+
"preview.tsx",
|
|
22
|
+
"preview.ts",
|
|
23
|
+
"preview.jsx",
|
|
24
|
+
"preview.js",
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Find the preview config file in a .storybook directory
|
|
29
|
+
*
|
|
30
|
+
* @param storybookDir - Path to the .storybook directory
|
|
31
|
+
* @returns The full path to the preview file, or null if not found
|
|
32
|
+
*/
|
|
33
|
+
export function findPreviewConfigPath(storybookDir: string): string | null {
|
|
34
|
+
for (const fileName of PREVIEW_FILES) {
|
|
35
|
+
const filePath = join(storybookDir, fileName);
|
|
36
|
+
if (existsSync(filePath)) {
|
|
37
|
+
return filePath;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Find the .storybook directory starting from a project root.
|
|
45
|
+
* Checks common locations.
|
|
46
|
+
*
|
|
47
|
+
* @param projectRoot - The project root directory
|
|
48
|
+
* @returns The path to the .storybook directory, or null if not found
|
|
49
|
+
*/
|
|
50
|
+
export function findStorybookDir(projectRoot: string): string | null {
|
|
51
|
+
const possiblePaths = [
|
|
52
|
+
join(projectRoot, ".storybook"),
|
|
53
|
+
join(projectRoot, "storybook"),
|
|
54
|
+
];
|
|
55
|
+
|
|
56
|
+
for (const dir of possiblePaths) {
|
|
57
|
+
if (existsSync(dir)) {
|
|
58
|
+
return dir;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Load and parse the Storybook preview configuration.
|
|
66
|
+
*
|
|
67
|
+
* This function dynamically imports the preview file and extracts
|
|
68
|
+
* the relevant configuration exports.
|
|
69
|
+
*
|
|
70
|
+
* @param previewPath - Full path to the preview config file
|
|
71
|
+
* @returns The parsed preview configuration
|
|
72
|
+
*/
|
|
73
|
+
export async function loadPreviewConfig(
|
|
74
|
+
previewPath: string
|
|
75
|
+
): Promise<PreviewConfig> {
|
|
76
|
+
try {
|
|
77
|
+
// Dynamic import with file:// URL for ESM compatibility
|
|
78
|
+
const fileUrl = new URL(`file://${resolve(previewPath)}`);
|
|
79
|
+
const module = await import(fileUrl.href);
|
|
80
|
+
|
|
81
|
+
// Extract configuration from the module
|
|
82
|
+
const config: PreviewConfig = {
|
|
83
|
+
decorators: module.decorators ?? module.default?.decorators ?? [],
|
|
84
|
+
parameters: module.parameters ?? module.default?.parameters ?? {},
|
|
85
|
+
globalTypes: module.globalTypes ?? module.default?.globalTypes ?? {},
|
|
86
|
+
args: module.args ?? module.default?.args ?? {},
|
|
87
|
+
argTypes: module.argTypes ?? module.default?.argTypes ?? {},
|
|
88
|
+
loaders: module.loaders ?? module.default?.loaders ?? [],
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
return config;
|
|
92
|
+
} catch (error) {
|
|
93
|
+
console.warn(
|
|
94
|
+
`[Segments] Failed to load preview config from ${previewPath}:`,
|
|
95
|
+
error instanceof Error ? error.message : error
|
|
96
|
+
);
|
|
97
|
+
return {};
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Auto-detect and load preview configuration from a project.
|
|
103
|
+
*
|
|
104
|
+
* @param projectRoot - The project root directory
|
|
105
|
+
* @returns The preview configuration, or empty config if not found
|
|
106
|
+
*/
|
|
107
|
+
export async function autoLoadPreviewConfig(
|
|
108
|
+
projectRoot: string
|
|
109
|
+
): Promise<PreviewConfig> {
|
|
110
|
+
const storybookDir = findStorybookDir(projectRoot);
|
|
111
|
+
if (!storybookDir) {
|
|
112
|
+
return {};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const previewPath = findPreviewConfigPath(storybookDir);
|
|
116
|
+
if (!previewPath) {
|
|
117
|
+
return {};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return loadPreviewConfig(previewPath);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Generate a virtual module that imports and re-exports preview config.
|
|
125
|
+
* This is used in the Vite plugin to make preview config available at runtime.
|
|
126
|
+
*
|
|
127
|
+
* @param previewPath - Full path to the preview config file, or null
|
|
128
|
+
* @returns JavaScript code for the virtual module
|
|
129
|
+
*/
|
|
130
|
+
export function generatePreviewModule(previewPath: string | null): string {
|
|
131
|
+
if (!previewPath) {
|
|
132
|
+
// No preview config found - export empty defaults
|
|
133
|
+
return `
|
|
134
|
+
export const decorators = [];
|
|
135
|
+
export const parameters = {};
|
|
136
|
+
export const globalTypes = {};
|
|
137
|
+
export const args = {};
|
|
138
|
+
export const argTypes = {};
|
|
139
|
+
export const loaders = [];
|
|
140
|
+
|
|
141
|
+
export default {
|
|
142
|
+
decorators,
|
|
143
|
+
parameters,
|
|
144
|
+
globalTypes,
|
|
145
|
+
args,
|
|
146
|
+
argTypes,
|
|
147
|
+
loaders,
|
|
148
|
+
};
|
|
149
|
+
`;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Import from the actual preview file and re-export
|
|
153
|
+
return `
|
|
154
|
+
import * as preview from "${previewPath}";
|
|
155
|
+
|
|
156
|
+
export const decorators = preview.decorators ?? preview.default?.decorators ?? [];
|
|
157
|
+
export const parameters = preview.parameters ?? preview.default?.parameters ?? {};
|
|
158
|
+
export const globalTypes = preview.globalTypes ?? preview.default?.globalTypes ?? {};
|
|
159
|
+
export const args = preview.args ?? preview.default?.args ?? {};
|
|
160
|
+
export const argTypes = preview.argTypes ?? preview.default?.argTypes ?? {};
|
|
161
|
+
export const loaders = preview.loaders ?? preview.default?.loaders ?? [];
|
|
162
|
+
|
|
163
|
+
export default {
|
|
164
|
+
decorators,
|
|
165
|
+
parameters,
|
|
166
|
+
globalTypes,
|
|
167
|
+
args,
|
|
168
|
+
argTypes,
|
|
169
|
+
loaders,
|
|
170
|
+
};
|
|
171
|
+
`;
|
|
172
|
+
}
|