@auto-engineer/component-implementor-react 1.110.0 → 1.110.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/.turbo/turbo-build.log +1 -1
- package/.turbo/turbo-test.log +3 -3
- package/.turbo/turbo-type-check.log +1 -1
- package/CHANGELOG.md +36 -0
- package/dist/src/commands/implement-component.d.ts.map +1 -1
- package/dist/src/commands/implement-component.js +5 -1
- package/dist/src/commands/implement-component.js.map +1 -1
- package/dist/src/index.d.ts +0 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +0 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/pipeline/run-pipeline.d.ts +4 -3
- package/dist/src/pipeline/run-pipeline.d.ts.map +1 -1
- package/dist/src/pipeline/run-pipeline.js +2 -2
- package/dist/src/pipeline/run-pipeline.js.map +1 -1
- package/dist/src/pipeline/run-pipeline.test.js +4 -2
- package/dist/src/pipeline/run-pipeline.test.js.map +1 -1
- package/dist/src/pipeline/steps/generate-story.d.ts +2 -1
- package/dist/src/pipeline/steps/generate-story.d.ts.map +1 -1
- package/dist/src/pipeline/steps/generate-story.js +11 -5
- package/dist/src/pipeline/steps/generate-story.js.map +1 -1
- package/dist/src/pipeline/steps/generate-story.test.js +11 -5
- package/dist/src/pipeline/steps/generate-story.test.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -3
- package/src/commands/implement-component.ts +6 -1
- package/src/index.ts +0 -1
- package/src/pipeline/run-pipeline.test.ts +4 -2
- package/src/pipeline/run-pipeline.ts +6 -5
- package/src/pipeline/steps/generate-story.test.ts +14 -5
- package/src/pipeline/steps/generate-story.ts +13 -5
- package/vitest.config.ts +1 -1
- package/dist/src/generate-story-deterministic.d.ts +0 -30
- package/dist/src/generate-story-deterministic.d.ts.map +0 -1
- package/dist/src/generate-story-deterministic.js +0 -229
- package/dist/src/generate-story-deterministic.js.map +0 -1
- package/src/generate-story-deterministic.ts +0 -267
|
@@ -1,267 +0,0 @@
|
|
|
1
|
-
// ═══════════════════════════════════════════════════════════════════
|
|
2
|
-
// Deterministic story generator — no LLM call, pure template logic
|
|
3
|
-
// ═══════════════════════════════════════════════════════════════════
|
|
4
|
-
|
|
5
|
-
export interface DeterministicStoryProp {
|
|
6
|
-
name: string;
|
|
7
|
-
type: string;
|
|
8
|
-
required: boolean;
|
|
9
|
-
default?: string;
|
|
10
|
-
description: string;
|
|
11
|
-
category: 'data' | 'callback' | 'slot' | 'visual' | 'state' | 'config';
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export interface StoryVariant {
|
|
15
|
-
name: string;
|
|
16
|
-
description: string;
|
|
17
|
-
args: Record<string, unknown>;
|
|
18
|
-
needsPlayFunction?: boolean;
|
|
19
|
-
playDescription?: string;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export interface DeterministicStoryInput {
|
|
23
|
-
componentName: string;
|
|
24
|
-
componentImportPath: string;
|
|
25
|
-
props?: DeterministicStoryProp[];
|
|
26
|
-
storyVariants?: StoryVariant[];
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Generates a Storybook CSF3 story file deterministically (no LLM call).
|
|
31
|
-
*
|
|
32
|
-
* Uses structured prop data and optional story variants to emit a complete
|
|
33
|
-
* `.stories.tsx` file string. When no variants are provided, a Default
|
|
34
|
-
* story is synthesized from the props with sensible placeholder values.
|
|
35
|
-
*/
|
|
36
|
-
export function generateStoryDeterministic(input: DeterministicStoryInput): string {
|
|
37
|
-
const { componentName, componentImportPath, props, storyVariants } = input;
|
|
38
|
-
|
|
39
|
-
const lines: string[] = [];
|
|
40
|
-
|
|
41
|
-
// ── Imports ──────────────────────────────────────────────────────
|
|
42
|
-
lines.push(`import type { Meta, StoryObj } from '@storybook/react-vite';`);
|
|
43
|
-
lines.push(`import { ${componentName} } from '${componentImportPath}';`);
|
|
44
|
-
lines.push('');
|
|
45
|
-
|
|
46
|
-
// ── Meta ─────────────────────────────────────────────────────────
|
|
47
|
-
lines.push(`const meta: Meta<typeof ${componentName}> = {`);
|
|
48
|
-
lines.push(` title: 'UI Components/${componentName}',`);
|
|
49
|
-
lines.push(` component: ${componentName},`);
|
|
50
|
-
lines.push(`};`);
|
|
51
|
-
lines.push(`export default meta;`);
|
|
52
|
-
lines.push('');
|
|
53
|
-
|
|
54
|
-
// ── Story type alias ─────────────────────────────────────────────
|
|
55
|
-
lines.push(`type Story = StoryObj<typeof ${componentName}>;`);
|
|
56
|
-
|
|
57
|
-
// ── Stories ──────────────────────────────────────────────────────
|
|
58
|
-
if (storyVariants && storyVariants.length > 0) {
|
|
59
|
-
for (const variant of storyVariants) {
|
|
60
|
-
lines.push('');
|
|
61
|
-
lines.push(`export const ${variant.name}: Story = {`);
|
|
62
|
-
const argsBlock = serializeArgs(variant.args);
|
|
63
|
-
if (argsBlock) {
|
|
64
|
-
lines.push(` args: {`);
|
|
65
|
-
lines.push(argsBlock);
|
|
66
|
-
lines.push(` },`);
|
|
67
|
-
}
|
|
68
|
-
lines.push(`};`);
|
|
69
|
-
}
|
|
70
|
-
} else {
|
|
71
|
-
// Derive a Default story from props
|
|
72
|
-
const defaultArgs = deriveDefaultArgs(props ?? []);
|
|
73
|
-
lines.push('');
|
|
74
|
-
lines.push(`export const Default: Story = {`);
|
|
75
|
-
const argsBlock = serializeArgs(defaultArgs);
|
|
76
|
-
if (argsBlock) {
|
|
77
|
-
lines.push(` args: {`);
|
|
78
|
-
lines.push(argsBlock);
|
|
79
|
-
lines.push(` },`);
|
|
80
|
-
}
|
|
81
|
-
lines.push(`};`);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
lines.push('');
|
|
85
|
-
return lines.join('\n');
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// ═══════════════════════════════════════════════════════════════════
|
|
89
|
-
// Internals
|
|
90
|
-
// ═══════════════════════════════════════════════════════════════════
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* Derives sensible default arg values from structured prop definitions.
|
|
94
|
-
*
|
|
95
|
-
* - `callback` props are omitted (Storybook auto-detects them via argTypes).
|
|
96
|
-
* - `slot` props are omitted (ReactNode cannot be serialized to args).
|
|
97
|
-
* - `data` props get placeholder values based on their TypeScript type.
|
|
98
|
-
* - `visual` props use the declared default if present, otherwise a placeholder.
|
|
99
|
-
* - `state` props default to `false`.
|
|
100
|
-
* - `config` props use the declared default if present, otherwise a placeholder.
|
|
101
|
-
*/
|
|
102
|
-
function deriveDefaultArgs(props: DeterministicStoryProp[]): Record<string, unknown> {
|
|
103
|
-
const args: Record<string, unknown> = {};
|
|
104
|
-
|
|
105
|
-
for (const prop of props) {
|
|
106
|
-
// Skip categories that cannot or should not be serialized in args
|
|
107
|
-
if (prop.category === 'callback' || prop.category === 'slot') {
|
|
108
|
-
continue;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
if (prop.category === 'state') {
|
|
112
|
-
args[prop.name] = false;
|
|
113
|
-
continue;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
// If a default is declared, try to use it
|
|
117
|
-
if (prop.default !== undefined) {
|
|
118
|
-
const parsed = parseDefaultValue(prop.default);
|
|
119
|
-
if (parsed !== undefined) {
|
|
120
|
-
args[prop.name] = parsed;
|
|
121
|
-
continue;
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
// Otherwise generate a placeholder based on the TypeScript type string
|
|
126
|
-
args[prop.name] = placeholderForType(prop.type);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
return args;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* Attempts to parse a default value string into a JS primitive.
|
|
134
|
-
* Returns `undefined` when the string cannot be meaningfully parsed
|
|
135
|
-
* (e.g. an arrow function or complex expression).
|
|
136
|
-
*/
|
|
137
|
-
function parseDefaultValue(raw: string): unknown {
|
|
138
|
-
const trimmed = raw.trim();
|
|
139
|
-
if (trimmed === 'true') return true;
|
|
140
|
-
if (trimmed === 'false') return false;
|
|
141
|
-
if (trimmed === 'null') return null;
|
|
142
|
-
if (trimmed === 'undefined') return undefined;
|
|
143
|
-
|
|
144
|
-
// Quoted string
|
|
145
|
-
const stringMatch = trimmed.match(/^['"](.*)['"]$/);
|
|
146
|
-
if (stringMatch) return stringMatch[1];
|
|
147
|
-
|
|
148
|
-
// Numeric
|
|
149
|
-
const num = Number(trimmed);
|
|
150
|
-
if (!Number.isNaN(num) && trimmed !== '') return num;
|
|
151
|
-
|
|
152
|
-
// Array or object literal — attempt JSON parse
|
|
153
|
-
if (trimmed.startsWith('[') || trimmed.startsWith('{')) {
|
|
154
|
-
try {
|
|
155
|
-
return JSON.parse(trimmed);
|
|
156
|
-
} catch {
|
|
157
|
-
return undefined;
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
// Anything else (functions, expressions) → skip
|
|
162
|
-
return undefined;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
/**
|
|
166
|
-
* Returns a sensible placeholder value for a given TypeScript type string.
|
|
167
|
-
*/
|
|
168
|
-
function placeholderForType(typeStr: string): unknown {
|
|
169
|
-
const t = typeStr.trim();
|
|
170
|
-
|
|
171
|
-
if (t === 'string') return 'Example';
|
|
172
|
-
if (t === 'number') return 42;
|
|
173
|
-
if (t === 'boolean') return true;
|
|
174
|
-
|
|
175
|
-
// Union of string literals — pick the first literal
|
|
176
|
-
const literalUnion = t.match(/^['"]([^'"]+)['"]/);
|
|
177
|
-
if (literalUnion) return literalUnion[1];
|
|
178
|
-
|
|
179
|
-
// Array types
|
|
180
|
-
if (t.endsWith('[]') || t.startsWith('Array<')) return [];
|
|
181
|
-
|
|
182
|
-
// Generic object / Record
|
|
183
|
-
if (t === 'object' || t.startsWith('Record<') || t.startsWith('{')) return {};
|
|
184
|
-
|
|
185
|
-
// Fallback
|
|
186
|
-
return undefined;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
/**
|
|
190
|
-
* Serializes an args record into indented TypeScript object body lines.
|
|
191
|
-
* Returns an empty string when the args record is empty (caller should
|
|
192
|
-
* omit the `args` block entirely).
|
|
193
|
-
*
|
|
194
|
-
* Each value is formatted as a valid TypeScript literal:
|
|
195
|
-
* - strings → quoted
|
|
196
|
-
* - numbers / booleans → as-is
|
|
197
|
-
* - arrays / plain objects → JSON.stringify
|
|
198
|
-
* - undefined, null, functions → skipped
|
|
199
|
-
*/
|
|
200
|
-
function serializeArgs(args: Record<string, unknown>): string {
|
|
201
|
-
const entries: string[] = [];
|
|
202
|
-
|
|
203
|
-
for (const [key, value] of Object.entries(args)) {
|
|
204
|
-
const formatted = formatArgValue(value);
|
|
205
|
-
if (formatted === null) continue;
|
|
206
|
-
entries.push(` ${key}: ${formatted},`);
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
return entries.join('\n');
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
function formatArgValue(value: unknown): string | null {
|
|
213
|
-
if (value === undefined) return null;
|
|
214
|
-
if (value === null) return null;
|
|
215
|
-
if (typeof value === 'function') return null;
|
|
216
|
-
|
|
217
|
-
if (typeof value === 'string') {
|
|
218
|
-
// Callback strings like "() => {}" — emit as raw JS, not quoted
|
|
219
|
-
if (/^\s*\(/.test(value) || /^\s*function\s*\(/.test(value)) {
|
|
220
|
-
return value;
|
|
221
|
-
}
|
|
222
|
-
return JSON.stringify(value);
|
|
223
|
-
}
|
|
224
|
-
if (typeof value === 'number') return String(value);
|
|
225
|
-
if (typeof value === 'boolean') return String(value);
|
|
226
|
-
|
|
227
|
-
// Arrays and plain objects — deep-fix stringified JSON values first
|
|
228
|
-
if (Array.isArray(value) || typeof value === 'object') {
|
|
229
|
-
const fixed = deepParseStringifiedJson(value);
|
|
230
|
-
return JSON.stringify(fixed);
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
return null;
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
/**
|
|
237
|
-
* Recursively walks a value and parses any string that looks like
|
|
238
|
-
* stringified JSON back into an actual object/array. This fixes
|
|
239
|
-
* FEA output where nested objects are accidentally double-stringified.
|
|
240
|
-
*/
|
|
241
|
-
function deepParseStringifiedJson(value: unknown): unknown {
|
|
242
|
-
if (typeof value === 'string') {
|
|
243
|
-
const trimmed = value.trim();
|
|
244
|
-
if ((trimmed.startsWith('{') && trimmed.endsWith('}')) || (trimmed.startsWith('[') && trimmed.endsWith(']'))) {
|
|
245
|
-
try {
|
|
246
|
-
return deepParseStringifiedJson(JSON.parse(trimmed));
|
|
247
|
-
} catch {
|
|
248
|
-
return value;
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
return value;
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
if (Array.isArray(value)) {
|
|
255
|
-
return value.map(deepParseStringifiedJson);
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
if (value !== null && typeof value === 'object') {
|
|
259
|
-
const result: Record<string, unknown> = {};
|
|
260
|
-
for (const [k, v] of Object.entries(value)) {
|
|
261
|
-
result[k] = deepParseStringifiedJson(v);
|
|
262
|
-
}
|
|
263
|
-
return result;
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
return value;
|
|
267
|
-
}
|