@tpitre/story-ui 4.7.0 → 4.8.0
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/dist/cli/index.js +50 -9
- package/dist/mcp-server/index.js +2 -2
- package/dist/mcp-server/mcp-stdio-server.js +15 -2
- package/dist/mcp-server/routes/components.d.ts.map +1 -1
- package/dist/mcp-server/routes/components.js +30 -12
- package/dist/mcp-server/routes/generateStory.d.ts.map +1 -1
- package/dist/mcp-server/routes/generateStory.js +7 -1
- package/dist/story-generator/componentDiscovery.d.ts +14 -0
- package/dist/story-generator/componentDiscovery.d.ts.map +1 -1
- package/dist/story-generator/enhancedComponentDiscovery.d.ts +34 -0
- package/dist/story-generator/enhancedComponentDiscovery.d.ts.map +1 -1
- package/dist/story-generator/enhancedComponentDiscovery.js +461 -1
- package/dist/story-generator/framework-adapters/angular-adapter.d.ts +0 -4
- package/dist/story-generator/framework-adapters/angular-adapter.d.ts.map +1 -1
- package/dist/story-generator/framework-adapters/angular-adapter.js +1 -9
- package/dist/story-generator/framework-adapters/base-adapter.d.ts +8 -0
- package/dist/story-generator/framework-adapters/base-adapter.d.ts.map +1 -1
- package/dist/story-generator/framework-adapters/base-adapter.js +89 -2
- package/dist/story-generator/framework-adapters/react-adapter.d.ts.map +1 -1
- package/dist/story-generator/framework-adapters/react-adapter.js +68 -10
- package/dist/story-generator/framework-adapters/web-components-adapter.d.ts +0 -4
- package/dist/story-generator/framework-adapters/web-components-adapter.d.ts.map +1 -1
- package/dist/story-generator/framework-adapters/web-components-adapter.js +1 -9
- package/dist/story-generator/postProcessStory.d.ts +23 -0
- package/dist/story-generator/postProcessStory.d.ts.map +1 -1
- package/dist/story-generator/postProcessStory.js +134 -0
- package/dist/story-generator/promptGenerator.d.ts.map +1 -1
- package/dist/story-generator/promptGenerator.js +165 -11
- package/dist/story-ui.config.d.ts +16 -0
- package/dist/story-ui.config.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -112,17 +112,54 @@ export const MyStory: Story = {
|
|
|
112
112
|
IMPORTANT: Without ChakraProvider, you will see "useContext returned undefined" errors.
|
|
113
113
|
`;
|
|
114
114
|
}
|
|
115
|
+
// Build import instructions based on importStyle
|
|
116
|
+
const importPath = config.importPath || 'your-library';
|
|
117
|
+
const useIndividualImports = config.importStyle === 'individual';
|
|
118
|
+
let importInstructions;
|
|
119
|
+
let additionalImportRules;
|
|
120
|
+
if (useIndividualImports) {
|
|
121
|
+
importInstructions = `MANDATORY IMPORTS - First lines of every story file:
|
|
122
|
+
1. import React from 'react';
|
|
123
|
+
2. import type { Meta, StoryObj } from '@storybook/react';
|
|
124
|
+
3. import { ComponentName } from '${importPath}/component-name'; // INDIVIDUAL FILE IMPORTS REQUIRED
|
|
125
|
+
|
|
126
|
+
🚫 INDIVIDUAL FILE IMPORTS REQUIRED 🚫
|
|
127
|
+
This library does NOT have a barrel export. Each component MUST be imported from its own file:
|
|
128
|
+
- import { Button } from '${importPath}/button';
|
|
129
|
+
- import { Card, CardHeader, CardContent } from '${importPath}/card';
|
|
130
|
+
- import { Dialog, DialogTrigger, DialogContent } from '${importPath}/dialog';
|
|
131
|
+
|
|
132
|
+
WRONG - DO NOT USE (will cause "Failed to fetch dynamically imported module" error):
|
|
133
|
+
- import { Button, Card, Dialog } from '${importPath}'; // ❌ NO BARREL EXPORT EXISTS
|
|
134
|
+
|
|
135
|
+
File naming convention:
|
|
136
|
+
- Components are PascalCase: Button, AlertDialog, NavigationMenu
|
|
137
|
+
- Files are kebab-case: button, alert-dialog, navigation-menu
|
|
138
|
+
- Sub-components share files: CardHeader → card, DialogTrigger → dialog`;
|
|
139
|
+
additionalImportRules = `- 🚫 INDIVIDUAL IMPORTS REQUIRED: Each component from its own file
|
|
140
|
+
- Sub-components share the same file (CardHeader, CardContent → '${importPath}/card')
|
|
141
|
+
- File names use kebab-case (AlertDialog → alert-dialog)`;
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
importInstructions = `MANDATORY IMPORTS - First lines of every story file:
|
|
145
|
+
1. import React from 'react';
|
|
146
|
+
2. import type { Meta, StoryObj } from '@storybook/react';
|
|
147
|
+
3. import { ComponentName } from '${importPath}';`;
|
|
148
|
+
additionalImportRules = '';
|
|
149
|
+
}
|
|
150
|
+
// Build the example import based on importStyle
|
|
151
|
+
const exampleImport = useIndividualImports
|
|
152
|
+
? `import { Button } from '${importPath}/button';`
|
|
153
|
+
: `import { Button } from '${importPath}';`;
|
|
115
154
|
return `You are an expert React developer creating Storybook stories using CSF 3.0 format.
|
|
116
155
|
Use ONLY the React components from the ${componentSystemName} listed below.
|
|
117
156
|
|
|
118
|
-
|
|
119
|
-
1. import React from 'react';
|
|
120
|
-
2. import type { Meta, StoryObj } from '@storybook/react';
|
|
121
|
-
3. import { ComponentName } from '${config.importPath || 'your-library'}';
|
|
157
|
+
${importInstructions}
|
|
122
158
|
|
|
123
159
|
${typescript ? 'Use TypeScript with proper type annotations.' : 'Use JavaScript.'}
|
|
124
160
|
${chakraInstructions}
|
|
125
161
|
COMPONENT IMPORT RULES:
|
|
162
|
+
${additionalImportRules}
|
|
126
163
|
- ONLY import components listed in the "Available Components" section
|
|
127
164
|
- Use the EXACT import path shown after each component name
|
|
128
165
|
- Components not in the list DO NOT EXIST
|
|
@@ -141,7 +178,7 @@ Example structure:
|
|
|
141
178
|
\`\`\`tsx
|
|
142
179
|
import React from 'react';
|
|
143
180
|
import type { Meta, StoryObj } from '@storybook/react';
|
|
144
|
-
|
|
181
|
+
${exampleImport}
|
|
145
182
|
|
|
146
183
|
const meta: Meta<typeof Button> = {
|
|
147
184
|
title: 'Components/Button',
|
|
@@ -179,6 +216,19 @@ ${this.getCommonRules()}`;
|
|
|
179
216
|
}
|
|
180
217
|
generateExamples(config) {
|
|
181
218
|
const lib = config.importPath || 'your-library';
|
|
219
|
+
const useIndividualImports = config.importStyle === 'individual';
|
|
220
|
+
// Generate import statements based on importStyle
|
|
221
|
+
let singleComponentImport;
|
|
222
|
+
let multiComponentImport;
|
|
223
|
+
if (useIndividualImports) {
|
|
224
|
+
singleComponentImport = `import { Button } from '${lib}/button';`;
|
|
225
|
+
multiComponentImport = `import { Card } from '${lib}/card';
|
|
226
|
+
import { Button } from '${lib}/button';`;
|
|
227
|
+
}
|
|
228
|
+
else {
|
|
229
|
+
singleComponentImport = `import { Button } from '${lib}';`;
|
|
230
|
+
multiComponentImport = `import { Card, Button, Text } from '${lib}';`;
|
|
231
|
+
}
|
|
182
232
|
return `
|
|
183
233
|
## Example Stories
|
|
184
234
|
|
|
@@ -186,7 +236,7 @@ ${this.getCommonRules()}`;
|
|
|
186
236
|
\`\`\`tsx
|
|
187
237
|
import React from 'react';
|
|
188
238
|
import type { Meta, StoryObj } from '@storybook/react';
|
|
189
|
-
|
|
239
|
+
${singleComponentImport}
|
|
190
240
|
|
|
191
241
|
const meta: Meta<typeof Button> = {
|
|
192
242
|
title: 'Components/Button',
|
|
@@ -217,7 +267,7 @@ export const Primary: Story = {
|
|
|
217
267
|
\`\`\`tsx
|
|
218
268
|
import React from 'react';
|
|
219
269
|
import type { Meta, StoryObj } from '@storybook/react';
|
|
220
|
-
|
|
270
|
+
${multiComponentImport}
|
|
221
271
|
|
|
222
272
|
const meta: Meta = {
|
|
223
273
|
title: 'Examples/Card Layout',
|
|
@@ -232,8 +282,8 @@ export const ProductCard: Story = {
|
|
|
232
282
|
render: () => (
|
|
233
283
|
<Card style={{ width: 300 }}>
|
|
234
284
|
<img src="https://picsum.photos/300/200" alt="Product" />
|
|
235
|
-
<
|
|
236
|
-
<
|
|
285
|
+
<div>Product Name</div>
|
|
286
|
+
<div>$99.00</div>
|
|
237
287
|
<Button variant="primary">Add to Cart</Button>
|
|
238
288
|
</Card>
|
|
239
289
|
),
|
|
@@ -243,6 +293,7 @@ export const ProductCard: Story = {
|
|
|
243
293
|
}
|
|
244
294
|
generateSampleStory(config, components) {
|
|
245
295
|
const lib = config.importPath || 'your-library';
|
|
296
|
+
const useIndividualImports = config.importStyle === 'individual';
|
|
246
297
|
const firstComponent = components[0];
|
|
247
298
|
if (!firstComponent) {
|
|
248
299
|
return `
|
|
@@ -262,10 +313,17 @@ export const Default: Story = {
|
|
|
262
313
|
};
|
|
263
314
|
`;
|
|
264
315
|
}
|
|
316
|
+
// Convert PascalCase to kebab-case for individual imports
|
|
317
|
+
const kebabName = firstComponent.name
|
|
318
|
+
.replace(/([a-z])([A-Z])/g, '$1-$2')
|
|
319
|
+
.toLowerCase();
|
|
320
|
+
const importStatement = useIndividualImports
|
|
321
|
+
? `import { ${firstComponent.name} } from '${lib}/${kebabName}';`
|
|
322
|
+
: `import { ${firstComponent.name} } from '${lib}';`;
|
|
265
323
|
return `
|
|
266
324
|
import React from 'react';
|
|
267
325
|
import type { Meta, StoryObj } from '@storybook/react';
|
|
268
|
-
|
|
326
|
+
${importStatement}
|
|
269
327
|
|
|
270
328
|
const meta: Meta<typeof ${firstComponent.name}> = {
|
|
271
329
|
title: 'Components/${firstComponent.name}',
|
|
@@ -30,10 +30,6 @@ export declare class WebComponentsAdapter extends BaseFrameworkAdapter {
|
|
|
30
30
|
generateExamples(config: StoryUIConfig): string;
|
|
31
31
|
generateSampleStory(config: StoryUIConfig, components: DiscoveredComponent[]): string;
|
|
32
32
|
getStoryTemplate(options?: StoryGenerationOptions): string;
|
|
33
|
-
/**
|
|
34
|
-
* Convert PascalCase to kebab-case
|
|
35
|
-
*/
|
|
36
|
-
private toKebabCase;
|
|
37
33
|
/**
|
|
38
34
|
* Post-process Web Components stories
|
|
39
35
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"web-components-adapter.d.ts","sourceRoot":"","sources":["../../../story-generator/framework-adapters/web-components-adapter.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EACL,aAAa,EACb,cAAc,EACd,sBAAsB,EACvB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAE/D,qBAAa,oBAAqB,SAAQ,oBAAoB;IAC5D,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAoB;IAChD,QAAQ,CAAC,IAAI,oBAAoB;IACjC,QAAQ,CAAC,wBAAwB,EAAE,cAAc,EAAE,CAGjD;IACF,QAAQ,CAAC,gBAAgB,iBAAiB;IAE1C;;OAEG;IACH,wBAAwB,IAAI,MAAM,EAAE;IAIpC;;;OAGG;IACH,6BAA6B,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE;IAoE1E;;OAEG;IACH,OAAO,CAAC,eAAe;IAOvB,oBAAoB,CAClB,MAAM,EAAE,aAAa,EACrB,OAAO,CAAC,EAAE,sBAAsB,GAC/B,MAAM;IA4FT,gBAAgB,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM;IA0G/C,mBAAmB,CACjB,MAAM,EAAE,aAAa,EACrB,UAAU,EAAE,mBAAmB,EAAE,GAChC,MAAM;IA8CT,gBAAgB,CAAC,OAAO,CAAC,EAAE,sBAAsB,GAAG,MAAM;
|
|
1
|
+
{"version":3,"file":"web-components-adapter.d.ts","sourceRoot":"","sources":["../../../story-generator/framework-adapters/web-components-adapter.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EACL,aAAa,EACb,cAAc,EACd,sBAAsB,EACvB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAE/D,qBAAa,oBAAqB,SAAQ,oBAAoB;IAC5D,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAoB;IAChD,QAAQ,CAAC,IAAI,oBAAoB;IACjC,QAAQ,CAAC,wBAAwB,EAAE,cAAc,EAAE,CAGjD;IACF,QAAQ,CAAC,gBAAgB,iBAAiB;IAE1C;;OAEG;IACH,wBAAwB,IAAI,MAAM,EAAE;IAIpC;;;OAGG;IACH,6BAA6B,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE;IAoE1E;;OAEG;IACH,OAAO,CAAC,eAAe;IAOvB,oBAAoB,CAClB,MAAM,EAAE,aAAa,EACrB,OAAO,CAAC,EAAE,sBAAsB,GAC/B,MAAM;IA4FT,gBAAgB,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM;IA0G/C,mBAAmB,CACjB,MAAM,EAAE,aAAa,EACrB,UAAU,EAAE,mBAAmB,EAAE,GAChC,MAAM;IA8CT,gBAAgB,CAAC,OAAO,CAAC,EAAE,sBAAsB,GAAG,MAAM;IAwB1D;;OAEG;IACH,WAAW,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM;IAuCzC;;;;;;;;OAQG;IACH,OAAO,CAAC,yBAAyB;IAiCjC;;OAEG;IACH,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG;QAAE,KAAK,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,MAAM,EAAE,CAAA;KAAE;IAwBpE;;OAEG;IACH,eAAe,CACb,UAAU,EAAE,mBAAmB,EAAE,EACjC,MAAM,EAAE,aAAa,GACpB,MAAM;CAYV;AAED;;GAEG;AACH,wBAAgB,0BAA0B,IAAI,oBAAoB,CAEjE"}
|
|
@@ -352,15 +352,7 @@ export const Default: Story = {
|
|
|
352
352
|
};
|
|
353
353
|
`;
|
|
354
354
|
}
|
|
355
|
-
|
|
356
|
-
* Convert PascalCase to kebab-case
|
|
357
|
-
*/
|
|
358
|
-
toKebabCase(str) {
|
|
359
|
-
return str
|
|
360
|
-
.replace(/([a-z0-9])([A-Z])/g, '$1-$2')
|
|
361
|
-
.replace(/([A-Z])([A-Z][a-z])/g, '$1-$2')
|
|
362
|
-
.toLowerCase();
|
|
363
|
-
}
|
|
355
|
+
// toKebabCase inherited from BaseFrameworkAdapter
|
|
364
356
|
/**
|
|
365
357
|
* Post-process Web Components stories
|
|
366
358
|
*/
|
|
@@ -1,3 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interface for discovered component data
|
|
3
|
+
* This matches the DiscoveredComponent interface from componentDiscovery.ts
|
|
4
|
+
*/
|
|
5
|
+
export interface ComponentInfo {
|
|
6
|
+
name: string;
|
|
7
|
+
filePath: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Fix barrel imports to individual file imports
|
|
11
|
+
* DESIGN-SYSTEM AGNOSTIC: Uses actual component discovery data
|
|
12
|
+
*
|
|
13
|
+
* Converts: import { Button, Card, CardHeader } from '@/components/ui';
|
|
14
|
+
* To: import { Button } from '@/components/ui/button';
|
|
15
|
+
* import { Card, CardHeader } from '@/components/ui/card';
|
|
16
|
+
*
|
|
17
|
+
* @param code - The generated story code
|
|
18
|
+
* @param importPath - The import path alias (e.g., '@/components/ui')
|
|
19
|
+
* @param importStyle - Import style ('individual' or 'barrel')
|
|
20
|
+
* @param componentsPath - Optional: Actual filesystem path (e.g., './src/components/ui')
|
|
21
|
+
* @param discoveredComponents - Optional: Array of discovered components with file paths
|
|
22
|
+
*/
|
|
23
|
+
export declare function fixBarrelImports(code: string, importPath: string, importStyle?: string, componentsPath?: string, discoveredComponents?: ComponentInfo[]): string;
|
|
1
24
|
/**
|
|
2
25
|
* Post-process generated stories to fix common issues
|
|
3
26
|
* This module is completely design-system agnostic
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"postProcessStory.d.ts","sourceRoot":"","sources":["../../story-generator/postProcessStory.ts"],"names":[],"mappings":"AAEA;;;GAGG;AAEH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAkB1E"}
|
|
1
|
+
{"version":3,"file":"postProcessStory.d.ts","sourceRoot":"","sources":["../../story-generator/postProcessStory.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CAClB;AAyDD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,MAAM,EAClB,WAAW,CAAC,EAAE,MAAM,EACpB,cAAc,CAAC,EAAE,MAAM,EACvB,oBAAoB,CAAC,EAAE,aAAa,EAAE,GACrC,MAAM,CA2FR;AASD;;;GAGG;AAEH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAkB1E"}
|
|
@@ -1,4 +1,138 @@
|
|
|
1
1
|
import { logger } from './logger.js';
|
|
2
|
+
/**
|
|
3
|
+
* Convert PascalCase to kebab-case
|
|
4
|
+
* Used as fallback when component is not in discovery
|
|
5
|
+
*/
|
|
6
|
+
function toKebabCase(str) {
|
|
7
|
+
return str.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Normalize a file path for comparison
|
|
11
|
+
* Removes leading ./ and trailing extensions
|
|
12
|
+
*/
|
|
13
|
+
function normalizePath(filePath) {
|
|
14
|
+
return filePath
|
|
15
|
+
.replace(/^\.\//, '') // Remove leading ./
|
|
16
|
+
.replace(/\.(tsx?|jsx?|vue|svelte)$/, ''); // Remove extension
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Build a map of component names to their import paths
|
|
20
|
+
* Uses actual discovery data rather than hardcoded patterns
|
|
21
|
+
*/
|
|
22
|
+
function buildComponentToImportMap(discoveredComponents, componentsPath, importPath) {
|
|
23
|
+
const componentMap = new Map();
|
|
24
|
+
// Normalize the componentsPath for comparison
|
|
25
|
+
const normalizedComponentsPath = normalizePath(componentsPath);
|
|
26
|
+
for (const component of discoveredComponents) {
|
|
27
|
+
const normalizedFilePath = normalizePath(component.filePath);
|
|
28
|
+
// Check if this component is from our configured components path
|
|
29
|
+
if (normalizedFilePath.startsWith(normalizedComponentsPath)) {
|
|
30
|
+
// Extract the relative path after componentsPath
|
|
31
|
+
const relativePath = normalizedFilePath.slice(normalizedComponentsPath.length);
|
|
32
|
+
// Remove leading slash if present
|
|
33
|
+
const cleanRelativePath = relativePath.replace(/^\//, '');
|
|
34
|
+
// Build the import path
|
|
35
|
+
const componentImportPath = cleanRelativePath
|
|
36
|
+
? `${importPath}/${cleanRelativePath}`
|
|
37
|
+
: importPath;
|
|
38
|
+
componentMap.set(component.name, componentImportPath);
|
|
39
|
+
logger.log(`[DISCOVERY] ${component.name} -> ${componentImportPath}`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return componentMap;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Fix barrel imports to individual file imports
|
|
46
|
+
* DESIGN-SYSTEM AGNOSTIC: Uses actual component discovery data
|
|
47
|
+
*
|
|
48
|
+
* Converts: import { Button, Card, CardHeader } from '@/components/ui';
|
|
49
|
+
* To: import { Button } from '@/components/ui/button';
|
|
50
|
+
* import { Card, CardHeader } from '@/components/ui/card';
|
|
51
|
+
*
|
|
52
|
+
* @param code - The generated story code
|
|
53
|
+
* @param importPath - The import path alias (e.g., '@/components/ui')
|
|
54
|
+
* @param importStyle - Import style ('individual' or 'barrel')
|
|
55
|
+
* @param componentsPath - Optional: Actual filesystem path (e.g., './src/components/ui')
|
|
56
|
+
* @param discoveredComponents - Optional: Array of discovered components with file paths
|
|
57
|
+
*/
|
|
58
|
+
export function fixBarrelImports(code, importPath, importStyle, componentsPath, discoveredComponents) {
|
|
59
|
+
// Early return if not using individual imports
|
|
60
|
+
if (importStyle !== 'individual') {
|
|
61
|
+
return code;
|
|
62
|
+
}
|
|
63
|
+
logger.log('🔧 Fixing barrel imports to individual file imports (design-system agnostic)');
|
|
64
|
+
logger.log(`[DEBUG] importPath: ${importPath}`);
|
|
65
|
+
logger.log(`[DEBUG] componentsPath: ${componentsPath || 'not provided'}`);
|
|
66
|
+
logger.log(`[DEBUG] discoveredComponents count: ${discoveredComponents?.length || 0}`);
|
|
67
|
+
// Build component to import path mapping from discovery data
|
|
68
|
+
let componentToImport = null;
|
|
69
|
+
if (componentsPath && discoveredComponents && discoveredComponents.length > 0) {
|
|
70
|
+
componentToImport = buildComponentToImportMap(discoveredComponents, componentsPath, importPath);
|
|
71
|
+
logger.log(`[DEBUG] Built component map with ${componentToImport.size} entries`);
|
|
72
|
+
}
|
|
73
|
+
// Match imports from the barrel path (without a subpath)
|
|
74
|
+
// e.g., import { Button, Card } from '@/components/ui';
|
|
75
|
+
const barrelImportRegex = new RegExp(`import\\s*\\{([^}]+)\\}\\s*from\\s*['"]${escapeRegExp(importPath)}['"];?`, 'g');
|
|
76
|
+
logger.log(`[DEBUG] regex pattern: ${barrelImportRegex.source}`);
|
|
77
|
+
let processedCode = code;
|
|
78
|
+
let match;
|
|
79
|
+
let foundMatches = false;
|
|
80
|
+
while ((match = barrelImportRegex.exec(code)) !== null) {
|
|
81
|
+
foundMatches = true;
|
|
82
|
+
const fullMatch = match[0];
|
|
83
|
+
const componentList = match[1];
|
|
84
|
+
logger.log(`[DEBUG] Found match: "${fullMatch}"`);
|
|
85
|
+
logger.log(`[DEBUG] Component list: "${componentList}"`);
|
|
86
|
+
// Parse component names
|
|
87
|
+
const components = componentList
|
|
88
|
+
.split(',')
|
|
89
|
+
.map(c => c.trim())
|
|
90
|
+
.filter(c => c.length > 0);
|
|
91
|
+
logger.log(`[DEBUG] Parsed components: ${components.join(', ')}`);
|
|
92
|
+
// Group components by their import path
|
|
93
|
+
const groupedByImportPath = new Map();
|
|
94
|
+
for (const component of components) {
|
|
95
|
+
let componentImportPath;
|
|
96
|
+
if (componentToImport && componentToImport.has(component)) {
|
|
97
|
+
// Use discovery-based mapping
|
|
98
|
+
componentImportPath = componentToImport.get(component);
|
|
99
|
+
logger.log(`[DISCOVERY] ${component} -> ${componentImportPath}`);
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
// Fallback: Use simple PascalCase to kebab-case conversion
|
|
103
|
+
// This handles components not found in discovery
|
|
104
|
+
const fileName = toKebabCase(component);
|
|
105
|
+
componentImportPath = `${importPath}/${fileName}`;
|
|
106
|
+
logger.log(`[FALLBACK] ${component} -> ${componentImportPath} (not in discovery)`);
|
|
107
|
+
}
|
|
108
|
+
if (!groupedByImportPath.has(componentImportPath)) {
|
|
109
|
+
groupedByImportPath.set(componentImportPath, []);
|
|
110
|
+
}
|
|
111
|
+
groupedByImportPath.get(componentImportPath).push(component);
|
|
112
|
+
}
|
|
113
|
+
// Build individual imports
|
|
114
|
+
const individualImports = Array.from(groupedByImportPath.entries())
|
|
115
|
+
.map(([path, comps]) => {
|
|
116
|
+
return `import { ${comps.join(', ')} } from '${path}';`;
|
|
117
|
+
})
|
|
118
|
+
.join('\n');
|
|
119
|
+
logger.log(`[DEBUG] Replacement: "${individualImports}"`);
|
|
120
|
+
// Replace the barrel import with individual imports
|
|
121
|
+
processedCode = processedCode.replace(fullMatch, individualImports);
|
|
122
|
+
logger.log(`✅ Converted barrel import to ${groupedByImportPath.size} individual import(s)`);
|
|
123
|
+
}
|
|
124
|
+
if (!foundMatches) {
|
|
125
|
+
logger.log('[DEBUG] No barrel imports found to fix');
|
|
126
|
+
}
|
|
127
|
+
logger.log(`[DEBUG] First 500 chars of processed code: ${processedCode.substring(0, 500)}`);
|
|
128
|
+
return processedCode;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Escape special regex characters
|
|
132
|
+
*/
|
|
133
|
+
function escapeRegExp(string) {
|
|
134
|
+
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
135
|
+
}
|
|
2
136
|
/**
|
|
3
137
|
* Post-process generated stories to fix common issues
|
|
4
138
|
* This module is completely design-system agnostic
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"promptGenerator.d.ts","sourceRoot":"","sources":["../../story-generator/promptGenerator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAE,mBAAmB,
|
|
1
|
+
{"version":3,"file":"promptGenerator.d.ts","sourceRoot":"","sources":["../../story-generator/promptGenerator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAE,mBAAmB,EAAY,MAAM,yBAAyB,CAAC;AAIxE,OAAO,EAEL,eAAe,EACf,sBAAsB,EACtB,aAAa,EACb,gBAAgB,EACjB,MAAM,+BAA+B,CAAC;AAyOvC;;;GAGG;AACH,MAAM,WAAW,oBAAqB,SAAQ,IAAI,CAAC,eAAe,EAAE,oBAAoB,CAAC;IACvF,kBAAkB,EAAE,MAAM,EAAE,CAAC;CAC9B;AAED,MAAM,WAAW,eAAe;IAC9B,YAAY,EAAE,MAAM,CAAC;IACrB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,kBAAkB,EAAE,MAAM,EAAE,CAAC;IAC7B,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,aAAa,EAAE,UAAU,EAAE,mBAAmB,EAAE,GAAG,eAAe,CAkBxG;AA6bD;;;;GAIG;AACH,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM,EAAE,CA6D1E;AAyND;;GAEG;AACH,wBAAsB,iBAAiB,CACrC,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,aAAa,EACrB,UAAU,EAAE,mBAAmB,EAAE,GAChC,OAAO,CAAC,MAAM,CAAC,CAyKjB;AAED;;;GAGG;AACH,wBAAsB,4BAA4B,CAChD,MAAM,EAAE,aAAa,EACrB,UAAU,EAAE,mBAAmB,EAAE,EACjC,OAAO,CAAC,EAAE,sBAAsB,GAC/B,OAAO,CAAC,oBAAoB,CAAC,CAqB/B;AAED;;;GAGG;AACH,wBAAsB,yBAAyB,CAC7C,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,aAAa,EACrB,UAAU,EAAE,mBAAmB,EAAE,EACjC,OAAO,CAAC,EAAE,sBAAsB,GAC/B,OAAO,CAAC,MAAM,CAAC,CA4HjB;AA4ED;;GAEG;AACH,wBAAsB,sBAAsB,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAIzF;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,aAAa,GAAG,gBAAgB,CAG9E;AAED;;GAEG;AACH,wBAAgB,sBAAsB,IAAI,aAAa,EAAE,CAGxD"}
|
|
@@ -169,6 +169,9 @@ export function generatePrompt(config, components) {
|
|
|
169
169
|
const examples = generateExamples(config);
|
|
170
170
|
const systemPrompt = generateSystemPrompt(config);
|
|
171
171
|
const sampleStory = config.sampleStory || generateDefaultSampleStory(config, components);
|
|
172
|
+
// DEBUG: Log component reference to see import paths
|
|
173
|
+
console.log('[DEBUG] Import style:', config.importStyle);
|
|
174
|
+
console.log('[DEBUG] Sample component reference (first 500 chars):', componentReference.substring(0, 500));
|
|
172
175
|
return {
|
|
173
176
|
systemPrompt,
|
|
174
177
|
componentReference,
|
|
@@ -306,6 +309,46 @@ CRITICAL IMPORT RULES:
|
|
|
306
309
|
import type { Meta, StoryObj } from '${storybookFramework}';
|
|
307
310
|
import { ComponentName } from '[your-import-path]';`;
|
|
308
311
|
}
|
|
312
|
+
// Determine import style - individual file imports vs barrel imports
|
|
313
|
+
const useIndividualImports = config.importStyle === 'individual';
|
|
314
|
+
// Generate appropriate import instruction based on importStyle
|
|
315
|
+
let importInstructionText;
|
|
316
|
+
let importExampleText;
|
|
317
|
+
if (useIndividualImports) {
|
|
318
|
+
// Individual file imports - each component from its own file
|
|
319
|
+
importInstructionText = `║ IMPORT STYLE: INDIVIDUAL FILE IMPORTS ║
|
|
320
|
+
╠════════════════════════════════════════════════════════════════════╣
|
|
321
|
+
║ Each component MUST be imported from its own file: ║
|
|
322
|
+
║ import { Button } from '${importPath}/button';${' '.repeat(Math.max(0, 20 - importPath.length))}║
|
|
323
|
+
║ import { Card, CardHeader } from '${importPath}/card';${' '.repeat(Math.max(0, 11 - importPath.length))}║
|
|
324
|
+
║ import { Input } from '${importPath}/input';${' '.repeat(Math.max(0, 21 - importPath.length))}║
|
|
325
|
+
╠════════════════════════════════════════════════════════════════════╣
|
|
326
|
+
║ 🚫 DO NOT use barrel imports like: ║
|
|
327
|
+
║ import { Button, Card } from '${importPath}';${' '.repeat(Math.max(0, 18 - importPath.length))}║`;
|
|
328
|
+
importExampleText = `
|
|
329
|
+
🔴 CRITICAL: INDIVIDUAL FILE IMPORTS REQUIRED 🔴
|
|
330
|
+
This library does NOT have a barrel export (no index.ts). Each component MUST be imported from its own file!
|
|
331
|
+
|
|
332
|
+
CORRECT import pattern:
|
|
333
|
+
import { Button } from '${importPath}/button';
|
|
334
|
+
import { Card, CardHeader, CardContent } from '${importPath}/card';
|
|
335
|
+
import { Dialog, DialogContent, DialogTrigger } from '${importPath}/dialog';
|
|
336
|
+
import { Input } from '${importPath}/input';
|
|
337
|
+
|
|
338
|
+
WRONG - DO NOT USE (will cause "Failed to fetch dynamically imported module" error):
|
|
339
|
+
import { Button, Card, Input } from '${importPath}';
|
|
340
|
+
|
|
341
|
+
File naming convention:
|
|
342
|
+
- Component names are PascalCase: Button, AlertDialog, NavigationMenu
|
|
343
|
+
- File names are kebab-case: button, alert-dialog, navigation-menu
|
|
344
|
+
- Sub-components share the same file: CardHeader, CardContent → card`;
|
|
345
|
+
}
|
|
346
|
+
else {
|
|
347
|
+
// Barrel imports - all components from single entry point (default)
|
|
348
|
+
importInstructionText = `║ ALL component imports MUST use: ║
|
|
349
|
+
║ import { ComponentName } from '${importPath}';${' '.repeat(Math.max(0, 32 - importPath.length))}║`;
|
|
350
|
+
importExampleText = '';
|
|
351
|
+
}
|
|
309
352
|
return `
|
|
310
353
|
╔════════════════════════════════════════════════════════════════════╗
|
|
311
354
|
║ 🚨 MANDATORY LIBRARY CONSTRAINT 🚨 ║
|
|
@@ -314,8 +357,7 @@ import { ComponentName } from '[your-import-path]';`;
|
|
|
314
357
|
║ IMPORT PATH: ${importPath.padEnd(46)}║
|
|
315
358
|
║ FRAMEWORK: ${framework.padEnd(46)}║
|
|
316
359
|
╠════════════════════════════════════════════════════════════════════╣
|
|
317
|
-
|
|
318
|
-
║ import { ComponentName } from '${importPath}';${' '.repeat(Math.max(0, 32 - importPath.length))}║
|
|
360
|
+
${importInstructionText}
|
|
319
361
|
╠════════════════════════════════════════════════════════════════════╣
|
|
320
362
|
║ 🚫 FORBIDDEN LIBRARIES - DO NOT USE: ║
|
|
321
363
|
║ - tamagui, @tamagui/core (NEVER USE) ║
|
|
@@ -323,7 +365,7 @@ import { ComponentName } from '[your-import-path]';`;
|
|
|
323
365
|
║ - @mui/material (unless configured) ║
|
|
324
366
|
║ - antd (unless configured) ║
|
|
325
367
|
║ - Any library NOT matching: ${importPath.padEnd(36)}║
|
|
326
|
-
|
|
368
|
+
╚════════════════════════════════════════════════════════════════════╝${importExampleText}
|
|
327
369
|
${frameworkImportInstructions}
|
|
328
370
|
|
|
329
371
|
🔴 CRITICAL RULE: NEVER use children in args for ANY component or layout. Always use render functions. 🔴
|
|
@@ -434,16 +476,104 @@ function generateComponentReference(components, config) {
|
|
|
434
476
|
}
|
|
435
477
|
return reference;
|
|
436
478
|
}
|
|
479
|
+
/**
|
|
480
|
+
* Convert PascalCase to kebab-case
|
|
481
|
+
*/
|
|
482
|
+
function toKebabCase(str) {
|
|
483
|
+
return str.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
|
|
484
|
+
}
|
|
485
|
+
/**
|
|
486
|
+
* Get the base component name (for sub-components like CardHeader, returns 'Card')
|
|
487
|
+
*/
|
|
488
|
+
function getBaseComponentName(componentName) {
|
|
489
|
+
// Common sub-component patterns in shadcn/ui and other design systems
|
|
490
|
+
const subComponentPatterns = [
|
|
491
|
+
/^(Card)(Header|Footer|Title|Action|Description|Content)$/,
|
|
492
|
+
/^(Dialog)(Close|Content|Description|Footer|Header|Overlay|Portal|Title|Trigger)$/,
|
|
493
|
+
/^(AlertDialog)(Portal|Overlay|Trigger|Content|Header|Footer|Title|Description|Action|Cancel)$/,
|
|
494
|
+
/^(DropdownMenu)(Portal|Trigger|Content|Group|Label|Item|CheckboxItem|RadioGroup|RadioItem|Separator|Shortcut|Sub|SubTrigger|SubContent)$/,
|
|
495
|
+
/^(ContextMenu)(Trigger|Content|Item|CheckboxItem|RadioItem|Label|Separator|Shortcut|Group|Portal|Sub|SubContent|SubTrigger|RadioGroup)$/,
|
|
496
|
+
/^(NavigationMenu)(List|Item|Content|Trigger|Link|Indicator|Viewport)$/,
|
|
497
|
+
/^(Select)(Content|Group|Item|Label|ScrollDownButton|ScrollUpButton|Separator|Trigger|Value)$/,
|
|
498
|
+
/^(Menubar)(Portal|Menu|Trigger|Content|Group|Separator|Label|Item|Shortcut|CheckboxItem|RadioGroup|RadioItem|Sub|SubTrigger|SubContent)$/,
|
|
499
|
+
/^(Accordion)(Item|Trigger|Content)$/,
|
|
500
|
+
/^(Tabs)(List|Trigger|Content)$/,
|
|
501
|
+
/^(Sheet)(Trigger|Close|Content|Header|Footer|Title|Description)$/,
|
|
502
|
+
/^(Avatar)(Image|Fallback)$/,
|
|
503
|
+
/^(Breadcrumb)(List|Item|Link|Page|Separator|Ellipsis)$/,
|
|
504
|
+
/^(Command)(Dialog|Input|List|Empty|Group|Item|Shortcut|Separator)$/,
|
|
505
|
+
/^(HoverCard)(Trigger|Content)$/,
|
|
506
|
+
/^(Popover)(Trigger|Content|Anchor)$/,
|
|
507
|
+
/^(Collapsible)(Trigger|Content)$/,
|
|
508
|
+
/^(Drawer)(Portal|Overlay|Trigger|Close|Content|Header|Footer|Title|Description)$/,
|
|
509
|
+
/^(RadioGroup)(Item)$/,
|
|
510
|
+
/^(ToggleGroup)(Item)$/,
|
|
511
|
+
/^(Tooltip)(Trigger|Content|Provider)$/,
|
|
512
|
+
/^(Table)(Header|Body|Footer|Head|Row|Cell|Caption)$/,
|
|
513
|
+
/^(InputOTP)(Group|Slot|Separator)$/,
|
|
514
|
+
/^(Resizable)(PanelGroup|Panel|Handle)$/,
|
|
515
|
+
/^(ScrollArea|ScrollBar)$/,
|
|
516
|
+
/^(Pagination)(Content|Link|Item|Previous|Next|Ellipsis)$/,
|
|
517
|
+
/^(Alert)(Title|Description)$/,
|
|
518
|
+
];
|
|
519
|
+
for (const pattern of subComponentPatterns) {
|
|
520
|
+
const match = componentName.match(pattern);
|
|
521
|
+
if (match) {
|
|
522
|
+
return match[1];
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
return componentName;
|
|
526
|
+
}
|
|
437
527
|
/**
|
|
438
528
|
* Formats a single component reference
|
|
439
529
|
*/
|
|
440
530
|
function formatComponentReference(component, config) {
|
|
441
531
|
let reference = `- ${component.name}`;
|
|
442
|
-
// Add import path information
|
|
532
|
+
// Add import path information
|
|
533
|
+
let importPath;
|
|
443
534
|
if (component.__componentPath) {
|
|
444
|
-
|
|
535
|
+
importPath = component.__componentPath;
|
|
445
536
|
}
|
|
446
|
-
if (
|
|
537
|
+
else if (config.importStyle === 'individual') {
|
|
538
|
+
// Generate individual import path based on component name
|
|
539
|
+
const basePath = config.importPath || 'unknown';
|
|
540
|
+
const baseComponentName = getBaseComponentName(component.name);
|
|
541
|
+
const kebabName = toKebabCase(baseComponentName);
|
|
542
|
+
importPath = `${basePath}/${kebabName}`;
|
|
543
|
+
}
|
|
544
|
+
else {
|
|
545
|
+
importPath = config.importPath || '';
|
|
546
|
+
}
|
|
547
|
+
if (importPath) {
|
|
548
|
+
reference += ` (import from '${importPath}')`;
|
|
549
|
+
}
|
|
550
|
+
// Use rich prop types if available, otherwise fall back to simple prop names
|
|
551
|
+
if (component.propTypes && component.propTypes.length > 0) {
|
|
552
|
+
reference += '\n Props:';
|
|
553
|
+
for (const prop of component.propTypes) {
|
|
554
|
+
let propLine = `\n - ${prop.name}`;
|
|
555
|
+
// Add type information
|
|
556
|
+
if (prop.type !== 'unknown') {
|
|
557
|
+
propLine += `: ${prop.type}`;
|
|
558
|
+
}
|
|
559
|
+
// Add options for select/radio types
|
|
560
|
+
if (prop.options && prop.options.length > 0) {
|
|
561
|
+
const optionsStr = prop.options.slice(0, 5).map(o => `"${o}"`).join(' | ');
|
|
562
|
+
propLine += ` [${optionsStr}${prop.options.length > 5 ? '...' : ''}]`;
|
|
563
|
+
}
|
|
564
|
+
// Add description
|
|
565
|
+
if (prop.description) {
|
|
566
|
+
propLine += ` - ${prop.description}`;
|
|
567
|
+
}
|
|
568
|
+
// Add default value
|
|
569
|
+
if (prop.defaultValue !== undefined) {
|
|
570
|
+
propLine += ` (default: ${JSON.stringify(prop.defaultValue)})`;
|
|
571
|
+
}
|
|
572
|
+
reference += propLine;
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
else if (component.props && component.props.length > 0) {
|
|
576
|
+
// Fall back to simple prop names
|
|
447
577
|
reference += `: Props: ${component.props.join(', ')}`;
|
|
448
578
|
}
|
|
449
579
|
if (component.slots && component.slots.length > 0) {
|
|
@@ -608,6 +738,8 @@ function generateExamples(config) {
|
|
|
608
738
|
*/
|
|
609
739
|
function generateImportStatements(config, components, componentNames) {
|
|
610
740
|
const importMap = new Map();
|
|
741
|
+
// Check if individual file imports are configured (for libraries without barrel exports)
|
|
742
|
+
const useIndividualImports = config.importStyle === 'individual';
|
|
611
743
|
for (const componentName of componentNames) {
|
|
612
744
|
// Find the component in our discovered components to get its specific import path
|
|
613
745
|
const component = components.find(c => c.name === componentName);
|
|
@@ -619,8 +751,20 @@ function generateImportStatements(config, components, componentNames) {
|
|
|
619
751
|
}
|
|
620
752
|
importMap.get(importPath).push(componentName);
|
|
621
753
|
}
|
|
754
|
+
else if (useIndividualImports) {
|
|
755
|
+
// Generate individual file imports for libraries without barrel exports
|
|
756
|
+
// Group by base component: CardHeader, CardContent -> card.tsx
|
|
757
|
+
// This pattern works for React (shadcn/ui), Vue (PrimeVue), Angular (Material), Web Components (Shoelace)
|
|
758
|
+
const baseComponent = getBaseComponentName(componentName);
|
|
759
|
+
const fileName = toKebabCase(baseComponent);
|
|
760
|
+
const importPath = `${config.importPath}/${fileName}`;
|
|
761
|
+
if (!importMap.has(importPath)) {
|
|
762
|
+
importMap.set(importPath, []);
|
|
763
|
+
}
|
|
764
|
+
importMap.get(importPath).push(componentName);
|
|
765
|
+
}
|
|
622
766
|
else {
|
|
623
|
-
//
|
|
767
|
+
// Use barrel import (default) - import all from single entry point
|
|
624
768
|
if (!importMap.has(config.importPath)) {
|
|
625
769
|
importMap.set(config.importPath, []);
|
|
626
770
|
}
|
|
@@ -629,8 +773,8 @@ function generateImportStatements(config, components, componentNames) {
|
|
|
629
773
|
}
|
|
630
774
|
// Generate import statements
|
|
631
775
|
const importLines = [];
|
|
632
|
-
for (const [importPath,
|
|
633
|
-
importLines.push(`import { ${
|
|
776
|
+
for (const [importPath, comps] of importMap) {
|
|
777
|
+
importLines.push(`import { ${comps.join(', ')} } from '${importPath}';`);
|
|
634
778
|
}
|
|
635
779
|
return importLines.join('\n');
|
|
636
780
|
}
|
|
@@ -815,7 +959,12 @@ export async function buildClaudePrompt(userPrompt, config, components) {
|
|
|
815
959
|
frameworkFirstLineRules = `🚨 FINAL CRITICAL REMINDERS 🚨
|
|
816
960
|
Follow the framework-specific import patterns shown above.`;
|
|
817
961
|
}
|
|
818
|
-
|
|
962
|
+
// Add import style specific rules
|
|
963
|
+
const importStyleRules = [];
|
|
964
|
+
if (config.importStyle === 'individual') {
|
|
965
|
+
importStyleRules.push(`- 🚫 INDIVIDUAL IMPORTS REQUIRED: Import each component from its own file (e.g., '${config.importPath}/button', NOT '${config.importPath}')`, `- Sub-components share files: CardHeader, CardContent → '${config.importPath}/card'`, '- File names use kebab-case: AlertDialog → alert-dialog, NavigationMenu → navigation-menu');
|
|
966
|
+
}
|
|
967
|
+
promptParts.push(`Output a complete Storybook story file in TypeScript. Import components as shown in the sample template below. Use the following sample as a template. Respond ONLY with a single code block containing the full file, and nothing else.`, '', '<rules>', frameworkFirstLineRules, '', 'OTHER CRITICAL RULES:', ...importStyleRules, '- Story title MUST always start with "Generated/" (e.g., title: "Generated/Recipe Card")', '- Do NOT use prefixes like "Content/", "Components/", or any other section name', '- ONLY import components that are listed in the "Available components" section', '- ALWAYS use the exact import path shown in parentheses after each component', '- NEVER use main package imports when specific subpath imports are shown', '- Do NOT import story exports - these are NOT real components', '- Check every import against the Available components list before using it', '- FORBIDDEN: Any component not explicitly listed in the Available components section', '- FORBIDDEN: Theme setup components (providers should be configured at the app level, not in individual stories)', '- All images MUST have a src attribute with placeholder URLs (use https://picsum.photos/)', '- Never create <img> tags without src attributes', '- MUST use ES modules syntax: "export default meta;" NOT "module.exports = meta;"', '- The file MUST have a default export for the meta object', '- Keep the story concise and focused - avoid overly complex layouts that might exceed token limits', '- Ensure all JSX tags are properly closed', '- Story must be complete and syntactically valid', '- CRITICAL: Never put ANY content in args.children - always use render function', '- Use render functions for ALL layouts and component compositions', '- For layouts: DO NOT set component in meta', '- Only set component in meta when showcasing a SINGLE component', '- Use appropriate styling for the component library (design tokens, className, or inline styles as needed)', '</rules>', '', 'Sample story format:', generated.sampleStory, '', 'User request:', userPrompt);
|
|
819
968
|
return promptParts.join('\n');
|
|
820
969
|
}
|
|
821
970
|
/**
|
|
@@ -913,7 +1062,12 @@ export async function buildFrameworkAwarePrompt(userPrompt, config, components,
|
|
|
913
1062
|
promptParts.push(`${frameworkType.toUpperCase()} SPECIFIC RULES:`);
|
|
914
1063
|
promptParts.push(...frameworkRules);
|
|
915
1064
|
}
|
|
916
|
-
|
|
1065
|
+
// Add import style specific rules for framework-aware prompts
|
|
1066
|
+
const importStyleRulesFramework = [];
|
|
1067
|
+
if (config.importStyle === 'individual') {
|
|
1068
|
+
importStyleRulesFramework.push(`- 🚫 INDIVIDUAL IMPORTS REQUIRED: Import each component from its own file (e.g., '${config.importPath}/button', NOT '${config.importPath}')`, `- Sub-components share files: CardHeader, CardContent → '${config.importPath}/card'`, '- File names use kebab-case: AlertDialog → alert-dialog, NavigationMenu → navigation-menu');
|
|
1069
|
+
}
|
|
1070
|
+
promptParts.push('', `Output a complete Storybook story file in TypeScript. Import components as shown in the sample template below. Use the following sample as a template. Respond ONLY with a single code block containing the full file, and nothing else.`, '', '<rules>', 'CRITICAL REMINDERS:', ...importStyleRulesFramework, '- Story title MUST always start with "Generated/" (e.g., title: "Generated/Recipe Card")', '- ONLY import components that are listed in the "Available components" section', '- ALWAYS use the exact import path shown in parentheses after each component', '- NEVER use main package imports when specific subpath imports are shown', '- Do NOT import story exports - these are NOT real components', '- All images MUST have a src attribute with placeholder URLs (use https://picsum.photos/)', '- MUST use ES modules syntax: "export default meta;" NOT "module.exports = meta;"', '- The file MUST have a default export for the meta object', '- Keep the story concise and focused - avoid overly complex layouts', '- Ensure all tags are properly closed and syntax is valid', '- Story must be complete and syntactically valid', '</rules>', '', 'Sample story format:', generated.sampleStory, '', 'User request:', userPrompt);
|
|
917
1071
|
return promptParts.join('\n');
|
|
918
1072
|
}
|
|
919
1073
|
/**
|
|
@@ -81,6 +81,22 @@ export interface StoryUIConfig {
|
|
|
81
81
|
additionalImports?: AdditionalImport[];
|
|
82
82
|
considerationsPath?: string;
|
|
83
83
|
storybookFramework?: string;
|
|
84
|
+
/**
|
|
85
|
+
* Import style for generated stories:
|
|
86
|
+
* - 'barrel': Use barrel imports from a single entry point (e.g., `import { Button } from 'library'`)
|
|
87
|
+
* - 'individual': Use individual file imports (e.g., `import { Button } from 'library/button'`)
|
|
88
|
+
*
|
|
89
|
+
* Use 'individual' for libraries without barrel exports (index.ts), such as:
|
|
90
|
+
* - shadcn/ui, Radix Vue, PrimeVue (Vue)
|
|
91
|
+
* - Angular Material, PrimeNG (Angular)
|
|
92
|
+
* - Shoelace, Lion (Web Components)
|
|
93
|
+
*
|
|
94
|
+
* Use 'barrel' (default) for libraries with barrel exports, such as:
|
|
95
|
+
* - Chakra UI, Mantine, Ant Design (React)
|
|
96
|
+
* - Vuetify, Quasar (Vue)
|
|
97
|
+
* - Skeleton UI (Svelte)
|
|
98
|
+
*/
|
|
99
|
+
importStyle?: 'barrel' | 'individual';
|
|
84
100
|
designSystemGuidelines?: DesignSystemGuidelines;
|
|
85
101
|
/** Icon imports configuration (auto-detected from package.json or manually configured) */
|
|
86
102
|
iconImports?: IconImportsConfig;
|