mirror of
https://github.com/wshobson/agents.git
synced 2026-03-18 09:37:15 +00:00
style: format all files with prettier
This commit is contained in:
@@ -17,12 +17,12 @@ $ARGUMENTS
|
||||
```typescript
|
||||
interface ComponentSpec {
|
||||
name: string;
|
||||
type: 'functional' | 'page' | 'layout' | 'form' | 'data-display';
|
||||
type: "functional" | "page" | "layout" | "form" | "data-display";
|
||||
props: PropDefinition[];
|
||||
state?: StateDefinition[];
|
||||
hooks?: string[];
|
||||
styling: 'css-modules' | 'styled-components' | 'tailwind';
|
||||
platform: 'web' | 'native' | 'universal';
|
||||
styling: "css-modules" | "styled-components" | "tailwind";
|
||||
platform: "web" | "native" | "universal";
|
||||
}
|
||||
|
||||
interface PropDefinition {
|
||||
@@ -43,7 +43,7 @@ class ComponentAnalyzer {
|
||||
state: this.extractState(input),
|
||||
hooks: this.identifyHooks(input),
|
||||
styling: this.detectStylingApproach(),
|
||||
platform: this.detectPlatform()
|
||||
platform: this.detectPlatform(),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -67,13 +67,13 @@ class ReactComponentGenerator {
|
||||
styles: this.generateStyles(spec),
|
||||
tests: options.testing ? this.generateTests(spec) : null,
|
||||
stories: options.storybook ? this.generateStories(spec) : null,
|
||||
index: this.generateIndex(spec)
|
||||
index: this.generateIndex(spec),
|
||||
};
|
||||
}
|
||||
|
||||
generateComponent(spec: ComponentSpec, options: GeneratorOptions): string {
|
||||
const imports = this.generateImports(spec, options);
|
||||
const types = options.typescript ? this.generatePropTypes(spec) : '';
|
||||
const types = options.typescript ? this.generatePropTypes(spec) : "";
|
||||
const component = this.generateComponentBody(spec, options);
|
||||
const exports = this.generateExports(spec);
|
||||
|
||||
@@ -83,9 +83,9 @@ class ReactComponentGenerator {
|
||||
generateImports(spec: ComponentSpec, options: GeneratorOptions): string {
|
||||
const imports = ["import React, { useState, useEffect } from 'react';"];
|
||||
|
||||
if (spec.styling === 'css-modules') {
|
||||
if (spec.styling === "css-modules") {
|
||||
imports.push(`import styles from './${spec.name}.module.css';`);
|
||||
} else if (spec.styling === 'styled-components') {
|
||||
} else if (spec.styling === "styled-components") {
|
||||
imports.push("import styled from 'styled-components';");
|
||||
}
|
||||
|
||||
@@ -93,35 +93,43 @@ class ReactComponentGenerator {
|
||||
imports.push("import { useA11y } from '@/hooks/useA11y';");
|
||||
}
|
||||
|
||||
return imports.join('\n');
|
||||
return imports.join("\n");
|
||||
}
|
||||
|
||||
generatePropTypes(spec: ComponentSpec): string {
|
||||
const props = spec.props.map(p => {
|
||||
const optional = p.required ? '' : '?';
|
||||
const comment = p.description ? ` /** ${p.description} */\n` : '';
|
||||
return `${comment} ${p.name}${optional}: ${p.type};`;
|
||||
}).join('\n');
|
||||
const props = spec.props
|
||||
.map((p) => {
|
||||
const optional = p.required ? "" : "?";
|
||||
const comment = p.description ? ` /** ${p.description} */\n` : "";
|
||||
return `${comment} ${p.name}${optional}: ${p.type};`;
|
||||
})
|
||||
.join("\n");
|
||||
|
||||
return `export interface ${spec.name}Props {\n${props}\n}`;
|
||||
}
|
||||
|
||||
generateComponentBody(spec: ComponentSpec, options: GeneratorOptions): string {
|
||||
const propsType = options.typescript ? `: React.FC<${spec.name}Props>` : '';
|
||||
const destructuredProps = spec.props.map(p => p.name).join(', ');
|
||||
generateComponentBody(
|
||||
spec: ComponentSpec,
|
||||
options: GeneratorOptions,
|
||||
): string {
|
||||
const propsType = options.typescript ? `: React.FC<${spec.name}Props>` : "";
|
||||
const destructuredProps = spec.props.map((p) => p.name).join(", ");
|
||||
|
||||
let body = `export const ${spec.name}${propsType} = ({ ${destructuredProps} }) => {\n`;
|
||||
|
||||
// Add state hooks
|
||||
if (spec.state) {
|
||||
body += spec.state.map(s =>
|
||||
` const [${s.name}, set${this.capitalize(s.name)}] = useState${options.typescript ? `<${s.type}>` : ''}(${s.initial});\n`
|
||||
).join('');
|
||||
body += '\n';
|
||||
body += spec.state
|
||||
.map(
|
||||
(s) =>
|
||||
` const [${s.name}, set${this.capitalize(s.name)}] = useState${options.typescript ? `<${s.type}>` : ""}(${s.initial});\n`,
|
||||
)
|
||||
.join("");
|
||||
body += "\n";
|
||||
}
|
||||
|
||||
// Add effects
|
||||
if (spec.hooks?.includes('useEffect')) {
|
||||
if (spec.hooks?.includes("useEffect")) {
|
||||
body += ` useEffect(() => {\n`;
|
||||
body += ` // TODO: Add effect logic\n`;
|
||||
body += ` }, [${destructuredProps}]);\n\n`;
|
||||
@@ -131,7 +139,7 @@ class ReactComponentGenerator {
|
||||
if (options.accessibility) {
|
||||
body += ` const a11yProps = useA11y({\n`;
|
||||
body += ` role: '${this.inferAriaRole(spec.type)}',\n`;
|
||||
body += ` label: ${spec.props.find(p => p.name === 'label')?.name || `'${spec.name}'`}\n`;
|
||||
body += ` label: ${spec.props.find((p) => p.name === "label")?.name || `'${spec.name}'`}\n`;
|
||||
body += ` });\n\n`;
|
||||
}
|
||||
|
||||
@@ -145,12 +153,17 @@ class ReactComponentGenerator {
|
||||
}
|
||||
|
||||
generateJSX(spec: ComponentSpec, options: GeneratorOptions): string {
|
||||
const className = spec.styling === 'css-modules' ? `className={styles.${this.camelCase(spec.name)}}` : '';
|
||||
const a11y = options.accessibility ? '{...a11yProps}' : '';
|
||||
const className =
|
||||
spec.styling === "css-modules"
|
||||
? `className={styles.${this.camelCase(spec.name)}}`
|
||||
: "";
|
||||
const a11y = options.accessibility ? "{...a11yProps}" : "";
|
||||
|
||||
return ` <div ${className} ${a11y}>\n` +
|
||||
` {/* TODO: Add component content */}\n` +
|
||||
` </div>\n`;
|
||||
return (
|
||||
` <div ${className} ${a11y}>\n` +
|
||||
` {/* TODO: Add component content */}\n` +
|
||||
` </div>\n`
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -171,11 +184,11 @@ import {
|
||||
} from 'react-native';
|
||||
|
||||
interface ${spec.name}Props {
|
||||
${spec.props.map(p => ` ${p.name}${p.required ? '' : '?'}: ${this.mapNativeType(p.type)};`).join('\n')}
|
||||
${spec.props.map((p) => ` ${p.name}${p.required ? "" : "?"}: ${this.mapNativeType(p.type)};`).join("\n")}
|
||||
}
|
||||
|
||||
export const ${spec.name}: React.FC<${spec.name}Props> = ({
|
||||
${spec.props.map(p => p.name).join(',\n ')}
|
||||
${spec.props.map((p) => p.name).join(",\n ")}
|
||||
}) => {
|
||||
return (
|
||||
<View
|
||||
@@ -206,11 +219,11 @@ const styles = StyleSheet.create({
|
||||
|
||||
mapNativeType(webType: string): string {
|
||||
const typeMap: Record<string, string> = {
|
||||
'string': 'string',
|
||||
'number': 'number',
|
||||
'boolean': 'boolean',
|
||||
'React.ReactNode': 'React.ReactNode',
|
||||
'Function': '() => void'
|
||||
string: "string",
|
||||
number: "number",
|
||||
boolean: "boolean",
|
||||
"React.ReactNode": "React.ReactNode",
|
||||
Function: "() => void",
|
||||
};
|
||||
return typeMap[webType] || webType;
|
||||
}
|
||||
@@ -228,7 +241,10 @@ import { ${spec.name} } from './${spec.name}';
|
||||
|
||||
describe('${spec.name}', () => {
|
||||
const defaultProps = {
|
||||
${spec.props.filter(p => p.required).map(p => ` ${p.name}: ${this.getMockValue(p.type)},`).join('\n')}
|
||||
${spec.props
|
||||
.filter((p) => p.required)
|
||||
.map((p) => ` ${p.name}: ${this.getMockValue(p.type)},`)
|
||||
.join("\n")}
|
||||
};
|
||||
|
||||
it('renders without crashing', () => {
|
||||
@@ -241,7 +257,10 @@ ${spec.props.filter(p => p.required).map(p => ` ${p.name}: ${this.getMockValu
|
||||
expect(screen.getByText(/content/i)).toBeVisible();
|
||||
});
|
||||
|
||||
${spec.props.filter(p => p.type.includes('()') || p.name.startsWith('on')).map(p => `
|
||||
${spec.props
|
||||
.filter((p) => p.type.includes("()") || p.name.startsWith("on"))
|
||||
.map(
|
||||
(p) => `
|
||||
it('calls ${p.name} when triggered', () => {
|
||||
const mock${this.capitalize(p.name)} = jest.fn();
|
||||
render(<${spec.name} {...defaultProps} ${p.name}={mock${this.capitalize(p.name)}} />);
|
||||
@@ -250,7 +269,9 @@ ${spec.props.filter(p => p.type.includes('()') || p.name.startsWith('on')).map(p
|
||||
fireEvent.click(trigger);
|
||||
|
||||
expect(mock${this.capitalize(p.name)}).toHaveBeenCalledTimes(1);
|
||||
});`).join('\n')}
|
||||
});`,
|
||||
)
|
||||
.join("\n")}
|
||||
|
||||
it('meets accessibility standards', async () => {
|
||||
const { container } = render(<${spec.name} {...defaultProps} />);
|
||||
@@ -262,12 +283,12 @@ ${spec.props.filter(p => p.type.includes('()') || p.name.startsWith('on')).map(p
|
||||
}
|
||||
|
||||
getMockValue(type: string): string {
|
||||
if (type === 'string') return "'test value'";
|
||||
if (type === 'number') return '42';
|
||||
if (type === 'boolean') return 'true';
|
||||
if (type.includes('[]')) return '[]';
|
||||
if (type.includes('()')) return 'jest.fn()';
|
||||
return '{}';
|
||||
if (type === "string") return "'test value'";
|
||||
if (type === "number") return "42";
|
||||
if (type === "boolean") return "true";
|
||||
if (type.includes("[]")) return "[]";
|
||||
if (type.includes("()")) return "jest.fn()";
|
||||
return "{}";
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -345,7 +366,7 @@ const meta: Meta<typeof ${spec.name}> = {
|
||||
component: ${spec.name},
|
||||
tags: ['autodocs'],
|
||||
argTypes: {
|
||||
${spec.props.map(p => ` ${p.name}: { control: '${this.inferControl(p.type)}', description: '${p.description}' },`).join('\n')}
|
||||
${spec.props.map((p) => ` ${p.name}: { control: '${this.inferControl(p.type)}', description: '${p.description}' },`).join("\n")}
|
||||
},
|
||||
};
|
||||
|
||||
@@ -354,7 +375,7 @@ type Story = StoryObj<typeof ${spec.name}>;
|
||||
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
${spec.props.map(p => ` ${p.name}: ${p.defaultValue || this.getMockValue(p.type)},`).join('\n')}
|
||||
${spec.props.map((p) => ` ${p.name}: ${p.defaultValue || this.getMockValue(p.type)},`).join("\n")}
|
||||
},
|
||||
};
|
||||
|
||||
@@ -367,11 +388,11 @@ export const Interactive: Story = {
|
||||
}
|
||||
|
||||
inferControl(type: string): string {
|
||||
if (type === 'string') return 'text';
|
||||
if (type === 'number') return 'number';
|
||||
if (type === 'boolean') return 'boolean';
|
||||
if (type.includes('[]')) return 'object';
|
||||
return 'text';
|
||||
if (type === "string") return "text";
|
||||
if (type === "number") return "number";
|
||||
if (type === "boolean") return "boolean";
|
||||
if (type.includes("[]")) return "object";
|
||||
return "text";
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user