Back to Skills

code-generation-template

aj-geddes
Updated Today
21 views
7
7
View on GitHub
Metageneral

About

This Claude Skill generates code using templates and patterns for scaffolding projects, creating boilerplate, and automating repetitive coding tasks. It supports template engines, AST-based code generation, and is ideal for scaffolding new components, generating CRUD operations, or creating API clients from specifications. Use it to boost development productivity and ensure code consistency through automated generation.

Documentation

Code Generation & Templates

Overview

Comprehensive guide to code generation techniques including template engines, AST manipulation, code scaffolding, and automated boilerplate generation for increased productivity and consistency.

When to Use

  • Scaffolding new projects or components
  • Generating repetitive boilerplate code
  • Creating CRUD operations automatically
  • Generating API clients from OpenAPI specs
  • Building code from templates
  • Creating database models from schemas
  • Generating TypeScript types from JSON Schema
  • Building custom CLI generators

Instructions

1. Template Engines

Handlebars Templates

// templates/component.hbs
import React from 'react';

export interface {{pascalCase name}}Props {
  {{#each props}}
  {{this.name}}{{#if this.optional}}?{{/if}}: {{this.type}};
  {{/each}}
}

export const {{pascalCase name}}: React.FC<{{pascalCase name}}Props> = ({
  {{#each props}}{{this.name}},{{/each}}
}) => {
  return (
    <div className="{{kebabCase name}}">
      {/* Component implementation */}
    </div>
  );
};
// generator.ts
import Handlebars from 'handlebars';
import fs from 'fs';

// Register helpers
Handlebars.registerHelper('pascalCase', (str: string) =>
  str.replace(/(\w)(\w*)/g, (_, first, rest) =>
    first.toUpperCase() + rest.toLowerCase()
  )
);

Handlebars.registerHelper('kebabCase', (str: string) =>
  str.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase()
);

// Load template
const templateSource = fs.readFileSync('templates/component.hbs', 'utf8');
const template = Handlebars.compile(templateSource);

// Generate code
const code = template({
  name: 'userProfile',
  props: [
    { name: 'userId', type: 'string', optional: false },
    { name: 'onUpdate', type: '() => void', optional: true }
  ]
});

fs.writeFileSync('src/components/UserProfile.tsx', code);

EJS Templates

// templates/api-endpoint.ejs
import { Router } from 'express';
import { <%= modelName %>Service } from '../services/<%= kebabCase(modelName) %>.service';

const router = Router();
const service = new <%= modelName %>Service();

// GET /<%= pluralize(kebabCase(modelName)) %>
router.get('/', async (req, res) => {
  try {
    const items = await service.findAll();
    res.json(items);
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

// GET /<%= pluralize(kebabCase(modelName)) %>/:id
router.get('/:id', async (req, res) => {
  try {
    const item = await service.findById(req.params.id);
    if (!item) {
      return res.status(404).json({ error: 'Not found' });
    }
    res.json(item);
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

// POST /<%= pluralize(kebabCase(modelName)) %>
router.post('/', async (req, res) => {
  try {
    const item = await service.create(req.body);
    res.status(201).json(item);
  } catch (error) {
    res.status(400).json({ error: error.message });
  }
});

export default router;
// Using EJS
import ejs from 'ejs';

const code = await ejs.renderFile('templates/api-endpoint.ejs', {
  modelName: 'User',
  kebabCase: (str: string) => str.replace(/([A-Z])/g, '-$1').toLowerCase().slice(1),
  pluralize: (str: string) => str + 's'
});

2. AST-Based Code Generation

Using Babel/TypeScript AST

// ast-generator.ts
import * as ts from 'typescript';

export class TypeScriptGenerator {
  // Generate interface
  generateInterface(name: string, properties: Array<{ name: string; type: string; optional?: boolean }>) {
    const members = properties.map(prop =>
      ts.factory.createPropertySignature(
        undefined,
        ts.factory.createIdentifier(prop.name),
        prop.optional ? ts.factory.createToken(ts.SyntaxKind.QuestionToken) : undefined,
        ts.factory.createTypeReferenceNode(prop.type)
      )
    );

    const interfaceDecl = ts.factory.createInterfaceDeclaration(
      [ts.factory.createToken(ts.SyntaxKind.ExportKeyword)],
      ts.factory.createIdentifier(name),
      undefined,
      undefined,
      members
    );

    return this.printNode(interfaceDecl);
  }

  // Generate class
  generateClass(name: string, properties: Array<{ name: string; type: string }>) {
    const propertyDecls = properties.map(prop =>
      ts.factory.createPropertyDeclaration(
        [ts.factory.createToken(ts.SyntaxKind.PrivateKeyword)],
        ts.factory.createIdentifier(prop.name),
        undefined,
        ts.factory.createTypeReferenceNode(prop.type),
        undefined
      )
    );

    const constructor = ts.factory.createConstructorDeclaration(
      undefined,
      properties.map(prop =>
        ts.factory.createParameterDeclaration(
          undefined,
          undefined,
          ts.factory.createIdentifier(prop.name),
          undefined,
          ts.factory.createTypeReferenceNode(prop.type)
        )
      ),
      ts.factory.createBlock(
        properties.map(prop =>
          ts.factory.createExpressionStatement(
            ts.factory.createBinaryExpression(
              ts.factory.createPropertyAccessExpression(
                ts.factory.createThis(),
                prop.name
              ),
              ts.SyntaxKind.EqualsToken,
              ts.factory.createIdentifier(prop.name)
            )
          )
        ),
        true
      )
    );

    const classDecl = ts.factory.createClassDeclaration(
      [ts.factory.createToken(ts.SyntaxKind.ExportKeyword)],
      ts.factory.createIdentifier(name),
      undefined,
      undefined,
      [...propertyDecls, constructor]
    );

    return this.printNode(classDecl);
  }

  private printNode(node: ts.Node): string {
    const sourceFile = ts.createSourceFile(
      'temp.ts',
      '',
      ts.ScriptTarget.Latest,
      false,
      ts.ScriptKind.TS
    );

    const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
    return printer.printNode(ts.EmitHint.Unspecified, node, sourceFile);
  }
}

// Usage
const generator = new TypeScriptGenerator();

const interfaceCode = generator.generateInterface('User', [
  { name: 'id', type: 'string' },
  { name: 'email', type: 'string' },
  { name: 'name', type: 'string', optional: true }
]);

const classCode = generator.generateClass('UserService', [
  { name: 'repository', type: 'UserRepository' },
  { name: 'logger', type: 'Logger' }
]);

3. Project Scaffolding

Simple CLI Generator

// cli/generate.ts
#!/usr/bin/env node
import { Command } from 'commander';
import inquirer from 'inquirer';
import fs from 'fs-extra';
import path from 'path';

const program = new Command();

program
  .name('generate')
  .description('Code generator CLI')
  .version('1.0.0');

program
  .command('component <name>')
  .description('Generate a React component')
  .option('-d, --dir <directory>', 'Output directory', 'src/components')
  .action(async (name, options) => {
    const answers = await inquirer.prompt([
      {
        type: 'list',
        name: 'type',
        message: 'Component type?',
        choices: ['functional', 'class']
      },
      {
        type: 'confirm',
        name: 'typescript',
        message: 'Use TypeScript?',
        default: true
      },
      {
        type: 'confirm',
        name: 'test',
        message: 'Generate test file?',
        default: true
      }
    ]);

    await generateComponent(name, options.dir, answers);
  });

program
  .command('api <resource>')
  .description('Generate API endpoint with controller, service, and model')
  .action(async (resource) => {
    await generateApiResource(resource);
  });

program.parse();

async function generateComponent(name: string, dir: string, options: any) {
  const componentName = pascalCase(name);
  const ext = options.typescript ? 'tsx' : 'jsx';

  const template = options.type === 'functional'
    ? getFunctionalComponentTemplate(componentName, options.typescript)
    : getClassComponentTemplate(componentName, options.typescript);

  const componentPath = path.join(dir, `${componentName}.${ext}`);

  await fs.ensureDir(dir);
  await fs.writeFile(componentPath, template);

  console.log(`✓ Created ${componentPath}`);

  if (options.test) {
    const testTemplate = getTestTemplate(componentName, options.typescript);
    const testPath = path.join(dir, `${componentName}.test.${ext}`);
    await fs.writeFile(testPath, testTemplate);
    console.log(`✓ Created ${testPath}`);
  }
}

function getFunctionalComponentTemplate(name: string, ts: boolean): string {
  if (ts) {
    return `import React from 'react';

export interface ${name}Props {
  // Add props here
}

export const ${name}: React.FC<${name}Props> = (props) => {
  return (
    <div className="${kebabCase(name)}">
      <h1>${name}</h1>
    </div>
  );
};
`;
  }

  return `import React from 'react';

export const ${name} = (props) => {
  return (
    <div className="${kebabCase(name)}">
      <h1>${name}</h1>
    </div>
  );
};
`;
}

async function generateApiResource(resource: string) {
  const name = pascalCase(resource);

  // Generate model
  const modelCode = `export interface ${name} {
  id: string;
  createdAt: Date;
  updatedAt: Date;
  // Add fields here
}
`;
  await fs.writeFile(`src/models/${kebabCase(resource)}.model.ts`, modelCode);

  // Generate service
  const serviceCode = `import { ${name} } from '../models/${kebabCase(resource)}.model';

export class ${name}Service {
  async findAll(): Promise<${name}[]> {
    // Implement
    return [];
  }

  async findById(id: string): Promise<${name} | null> {
    // Implement
    return null;
  }

  async create(data: Partial<${name}>): Promise<${name}> {
    // Implement
    throw new Error('Not implemented');
  }

  async update(id: string, data: Partial<${name}>): Promise<${name}> {
    // Implement
    throw new Error('Not implemented');
  }

  async delete(id: string): Promise<void> {
    // Implement
  }
}
`;
  await fs.writeFile(`src/services/${kebabCase(resource)}.service.ts`, serviceCode);

  // Generate controller
  const controllerCode = `import { Router } from 'express';
import { ${name}Service } from '../services/${kebabCase(resource)}.service';

const router = Router();
const service = new ${name}Service();

router.get('/', async (req, res) => {
  const items = await service.findAll();
  res.json(items);
});

router.get('/:id', async (req, res) => {
  const item = await service.findById(req.params.id);
  if (!item) return res.status(404).json({ error: 'Not found' });
  res.json(item);
});

router.post('/', async (req, res) => {
  const item = await service.create(req.body);
  res.status(201).json(item);
});

router.put('/:id', async (req, res) => {
  const item = await service.update(req.params.id, req.body);
  res.json(item);
});

router.delete('/:id', async (req, res) => {
  await service.delete(req.params.id);
  res.status(204).send();
});

export default router;
`;
  await fs.writeFile(`src/controllers/${kebabCase(resource)}.controller.ts`, controllerCode);

  console.log(`✓ Generated API resource: ${name}`);
}

4. OpenAPI Client Generation

// openapi-client-generator.ts
import SwaggerParser from '@apidevtools/swagger-parser';
import { compile } from 'json-schema-to-typescript';

export class OpenAPIClientGenerator {
  async generate(specPath: string, outputDir: string) {
    const api = await SwaggerParser.parse(specPath);

    // Generate TypeScript types from schemas
    if (api.components?.schemas) {
      for (const [name, schema] of Object.entries(api.components.schemas)) {
        const ts = await compile(schema as any, name, {
          bannerComment: ''
        });
        await fs.writeFile(
          path.join(outputDir, 'types', `${name}.ts`),
          ts
        );
      }
    }

    // Generate API client methods
    for (const [path, pathItem] of Object.entries(api.paths)) {
      for (const [method, operation] of Object.entries(pathItem)) {
        if (['get', 'post', 'put', 'delete', 'patch'].includes(method)) {
          const clientMethod = this.generateClientMethod(
            method,
            path,
            operation as any
          );
          // Write to file...
        }
      }
    }
  }

  private generateClientMethod(
    method: string,
    path: string,
    operation: any
  ): string {
    const functionName = operation.operationId || this.pathToFunctionName(method, path);
    const parameters = operation.parameters || [];

    return `
async ${functionName}(${this.generateParameters(parameters)}): Promise<${this.getResponseType(operation)}> {
  const response = await this.request('${method.toUpperCase()}', '${path}', {
    ${this.generateRequestOptions(parameters)}
  });
  return response.json();
}
`;
  }

  private generateParameters(parameters: any[]): string {
    return parameters
      .map(p => `${p.name}${p.required ? '' : '?'}: ${this.schemaToType(p.schema)}`)
      .join(', ');
  }

  private getResponseType(operation: any): string {
    const successResponse = operation.responses['200'] || operation.responses['201'];
    if (!successResponse) return 'any';

    const schema = successResponse.content?.['application/json']?.schema;
    return schema ? this.schemaToType(schema) : 'any';
  }

  private schemaToType(schema: any): string {
    if (schema.$ref) {
      return schema.$ref.split('/').pop();
    }
    if (schema.type === 'string') return 'string';
    if (schema.type === 'number' || schema.type === 'integer') return 'number';
    if (schema.type === 'boolean') return 'boolean';
    if (schema.type === 'array') return `${this.schemaToType(schema.items)}[]`;
    return 'any';
  }

  private pathToFunctionName(method: string, path: string): string {
    const cleanPath = path.replace(/\{.*?\}/g, 'By').replace(/[^a-zA-Z0-9]/g, '');
    return `${method}${cleanPath}`;
  }
}

5. Database Model Generation

// prisma-schema-generator.ts
export class PrismaSchemaGenerator {
  generateModel(table: DatabaseTable): string {
    return `model ${pascalCase(table.name)} {
${table.columns.map(col => this.generateField(col)).join('\n')}

${this.generateRelations(table.relations)}
${this.generateIndexes(table.indexes)}
}
`;
  }

  private generateField(column: Column): string {
    const optional = !column.required ? '?' : '';
    const unique = column.unique ? ' @unique' : '';
    const defaultValue = column.default ? ` @default(${column.default})` : '';

    return `  ${column.name} ${this.mapType(column.type)}${optional}${unique}${defaultValue}`;
  }

  private mapType(sqlType: string): string {
    const typeMap: Record<string, string> = {
      'varchar': 'String',
      'text': 'String',
      'integer': 'Int',
      'bigint': 'BigInt',
      'boolean': 'Boolean',
      'timestamp': 'DateTime',
      'date': 'DateTime',
      'json': 'Json'
    };
    return typeMap[sqlType.toLowerCase()] || 'String';
  }

  private generateRelations(relations: Relation[]): string {
    return relations.map(rel => {
      if (rel.type === 'hasMany') {
        return `  ${rel.name} ${rel.model}[]`;
      } else if (rel.type === 'belongsTo') {
        return `  ${rel.name} ${rel.model} @relation(fields: [${rel.foreignKey}], references: [id])`;
      }
      return '';
    }).join('\n');
  }
}

6. GraphQL Code Generation

// graphql-codegen.config.ts
import type { CodegenConfig } from '@graphql-codegen/cli';

const config: CodegenConfig = {
  schema: 'http://localhost:4000/graphql',
  documents: ['src/**/*.tsx', 'src/**/*.ts'],
  generates: {
    './src/generated/graphql.ts': {
      plugins: [
        'typescript',
        'typescript-operations',
        'typescript-react-apollo'
      ],
      config: {
        withHooks: true,
        withComponent: false,
        withHOC: false
      }
    },
    './src/generated/introspection.json': {
      plugins: ['introspection']
    }
  }
};

export default config;

7. Plop.js Generator

// plopfile.ts
import { NodePlopAPI } from 'plop';

export default function (plop: NodePlopAPI) {
  // Component generator
  plop.setGenerator('component', {
    description: 'React component',
    prompts: [
      {
        type: 'input',
        name: 'name',
        message: 'Component name:'
      },
      {
        type: 'list',
        name: 'type',
        message: 'Component type:',
        choices: ['functional', 'class']
      }
    ],
    actions: [
      {
        type: 'add',
        path: 'src/components/{{pascalCase name}}/{{pascalCase name}}.tsx',
        templateFile: 'templates/component.hbs'
      },
      {
        type: 'add',
        path: 'src/components/{{pascalCase name}}/{{pascalCase name}}.test.tsx',
        templateFile: 'templates/component.test.hbs'
      },
      {
        type: 'add',
        path: 'src/components/{{pascalCase name}}/index.ts',
        template: "export { {{pascalCase name}} } from './{{pascalCase name}}';\n"
      }
    ]
  });

  // API generator
  plop.setGenerator('api', {
    description: 'API endpoint with full stack',
    prompts: [
      {
        type: 'input',
        name: 'name',
        message: 'Resource name (e.g., user, post):'
      }
    ],
    actions: [
      {
        type: 'add',
        path: 'src/models/{{kebabCase name}}.model.ts',
        templateFile: 'templates/model.hbs'
      },
      {
        type: 'add',
        path: 'src/services/{{kebabCase name}}.service.ts',
        templateFile: 'templates/service.hbs'
      },
      {
        type: 'add',
        path: 'src/controllers/{{kebabCase name}}.controller.ts',
        templateFile: 'templates/controller.hbs'
      },
      {
        type: 'add',
        path: 'src/routes/{{kebabCase name}}.routes.ts',
        templateFile: 'templates/routes.hbs'
      }
    ]
  });
}

Best Practices

✅ DO

  • Use templates for repetitive code patterns
  • Generate TypeScript types from schemas
  • Include tests in generated code
  • Follow project conventions in templates
  • Add comments to explain generated code
  • Version control your templates
  • Make templates configurable
  • Generate documentation alongside code
  • Validate inputs before generating
  • Use consistent naming conventions
  • Keep templates simple and maintainable
  • Provide CLI for easy generation

❌ DON'T

  • Over-generate (avoid unnecessary complexity)
  • Generate code that's hard to maintain
  • Forget to validate generated code
  • Hardcode values in templates
  • Generate code without documentation
  • Create generators for one-off use cases
  • Mix business logic in templates
  • Generate code without formatting
  • Skip error handling in generators
  • Create overly complex templates

Common Patterns

Pattern 1: CRUD Generator

export function generateCRUD(entityName: string) {
  return {
    model: generateModel(entityName),
    service: generateService(entityName),
    controller: generateController(entityName),
    routes: generateRoutes(entityName),
    tests: generateTests(entityName)
  };
}

Pattern 2: Migration Generator

export function generateMigration(name: string, changes: SchemaChange[]) {
  return {
    up: generateUpMigration(changes),
    down: generateDownMigration(changes)
  };
}

Pattern 3: Factory Generator

export function generateFactory(model: Model) {
  return `export const create${model.name} = (overrides?: Partial<${model.name}>): ${model.name} => ({
  ${model.fields.map(f => `${f.name}: ${getDefaultValue(f)}`).join(',\n  ')},
  ...overrides
});`;
}

Tools & Resources

  • Plop: Micro-generator framework
  • Yeoman: Scaffolding tool
  • Hygen: Code generator with templates
  • GraphQL Code Generator: Generate code from GraphQL
  • Prisma: Database ORM with code generation
  • OpenAPI Generator: Generate clients from OpenAPI
  • json-schema-to-typescript: Generate TS types
  • TypeScript Compiler API: AST manipulation

Quick Install

/plugin add https://github.com/aj-geddes/useful-ai-prompts/tree/main/code-generation-template

Copy and paste this command in Claude Code to install this skill

GitHub 仓库

aj-geddes/useful-ai-prompts
Path: skills/code-generation-template

Related Skills

subagent-driven-development

Development

This skill executes implementation plans by dispatching a fresh subagent for each independent task, with code review between tasks. It enables fast iteration while maintaining quality gates through this review process. Use it when working on mostly independent tasks within the same session to ensure continuous progress with built-in quality checks.

View skill

algorithmic-art

Meta

This Claude Skill creates original algorithmic art using p5.js with seeded randomness and interactive parameters. It generates .md files for algorithmic philosophies, plus .html and .js files for interactive generative art implementations. Use it when developers need to create flow fields, particle systems, or other computational art while avoiding copyright issues.

View skill

executing-plans

Design

Use the executing-plans skill when you have a complete implementation plan to execute in controlled batches with review checkpoints. It loads and critically reviews the plan, then executes tasks in small batches (default 3 tasks) while reporting progress between each batch for architect review. This ensures systematic implementation with built-in quality control checkpoints.

View skill

cost-optimization

Other

This Claude Skill helps developers optimize cloud costs through resource rightsizing, tagging strategies, and spending analysis. It provides a framework for reducing cloud expenses and implementing cost governance across AWS, Azure, and GCP. Use it when you need to analyze infrastructure costs, right-size resources, or meet budget constraints.

View skill