#!/usr/bin/env python3 """ HTML Documentation Generator Generates beautiful HTML documentation from project analysis. """ import os import sys import json import re from pathlib import Path from datetime import datetime from typing import Dict, List, Any, Optional # Try to import yaml try: import yaml except ImportError: yaml = None def load_template(template_path: Path) -> str: """Load the HTML template.""" with open(template_path, 'r', encoding='utf-8') as f: return f.read() def load_analysis(analysis_path: Path) -> Dict[str, Any]: """Load project analysis from YAML or JSON.""" with open(analysis_path, 'r', encoding='utf-8') as f: content = f.read() if yaml and (analysis_path.suffix in ['.yml', '.yaml']): return yaml.safe_load(content) return json.loads(content) def escape_html(text: str) -> str: """Escape HTML special characters.""" if not text: return '' return (str(text) .replace('&', '&') .replace('<', '<') .replace('>', '>') .replace('"', '"') .replace("'", ''')) def generate_capabilities_html(capabilities: List[Dict]) -> str: """Generate HTML for capabilities cards.""" icons = ['✨', '⚑', 'πŸ”', 'πŸ“Š', 'πŸš€', 'πŸ’‘', '🎯', 'πŸ”§'] html_parts = [] for i, cap in enumerate(capabilities[:8]): icon = icons[i % len(icons)] html_parts.append(f'''
{icon}
{escape_html(cap.get('capability', cap.get('name', 'Feature')))}

{escape_html(cap.get('description', ''))}

''') return '\n'.join(html_parts) def generate_prerequisites_html(prerequisites: List[Dict]) -> str: """Generate HTML for prerequisites table rows.""" html_parts = [] for prereq in prerequisites: tool = prereq.get('tool', prereq.get('name', '')) purpose = prereq.get('purpose', prereq.get('description', '')) html_parts.append(f''' {escape_html(tool)} {escape_html(purpose)} ''') return '\n'.join(html_parts) if html_parts else ''' Node.js JavaScript runtime environment ''' def generate_tech_stack_html(tech_stack: Dict) -> str: """Generate HTML for technology stack table rows.""" html_parts = [] stack_items = [ ('Language', tech_stack.get('language')), ('Framework', tech_stack.get('framework')), ('Database', tech_stack.get('database')), ('UI Framework', tech_stack.get('ui_framework')), ] purposes = { 'TypeScript': 'Type-safe JavaScript for better code quality', 'JavaScript': 'Programming language for web applications', 'Python': 'General-purpose programming language', 'Next.js': 'Full-stack React framework with SSR', 'React': 'Component-based UI library', 'Vue.js': 'Progressive JavaScript framework', 'Express': 'Minimal web server framework', 'Prisma': 'Type-safe database ORM', 'MongoDB': 'NoSQL document database', 'PostgreSQL': 'Relational database', 'Tailwind CSS': 'Utility-first CSS framework', 'Material UI': 'React component library', } for layer, tech in stack_items: if tech: purpose = purposes.get(tech, f'{tech} for {layer.lower()}') html_parts.append(f''' {escape_html(layer)} {escape_html(tech)} {escape_html(purpose)} ''') # Add key dependencies for dep in tech_stack.get('key_dependencies', [])[:5]: html_parts.append(f''' Dependency {escape_html(dep.get('name', ''))} {escape_html(dep.get('purpose', ''))} ''') return '\n'.join(html_parts) def generate_directory_structure(structure: Dict) -> str: """Generate directory structure text.""" lines = ['project/'] for i, dir_info in enumerate(structure.get('directories', [])[:10]): prefix = '└── ' if i == len(structure.get('directories', [])) - 1 else 'β”œβ”€β”€ ' path = dir_info.get('path', '') purpose = dir_info.get('purpose', '') lines.append(f"{prefix}{path}/ # {purpose}") return '\n'.join(lines) def generate_features_html(features: List[Dict]) -> str: """Generate HTML for features section.""" icons = ['πŸ”', 'πŸ‘€', 'πŸ”Œ', 'πŸ’Ύ', 'πŸ“', 'πŸ”', 'πŸ“§', 'βš™οΈ'] html_parts = [] for i, feature in enumerate(features[:8]): icon = icons[i % len(icons)] name = feature.get('name', 'Feature') description = feature.get('description', '') technical_notes = feature.get('technical_notes', '') files = feature.get('files', []) files_html = '\n'.join([f'
  • {escape_html(f)}
  • ' for f in files[:3]]) html_parts.append(f'''
    {icon}

    {escape_html(name)}

    {escape_html(description)}

    πŸ”§ Technical Details For Engineers

    {escape_html(technical_notes)}

    Key Files:

      {files_html}
    ''') return '\n'.join(html_parts) if html_parts else '''
    ✨

    Core Functionality

    Main features of the application.

    ''' def generate_api_endpoints_html(endpoints: List[Dict]) -> str: """Generate HTML for API endpoints.""" if not endpoints: return '''

    No API endpoints detected. This project may use a different API pattern or may not have an API layer.

    ''' html_parts = ['', '', '', '', '', '', '', '', ''] for endpoint in endpoints[:15]: method = endpoint.get('method', 'GET') method_class = f'method-{method.lower()}' path = endpoint.get('path', '') description = endpoint.get('description', '') html_parts.append(f''' ''') html_parts.extend(['', '
    MethodEndpointDescription
    {escape_html(method)} {escape_html(path)} {escape_html(description)}
    ']) return '\n'.join(html_parts) def generate_components_html(components: List[Dict]) -> str: """Generate HTML for component catalog.""" if not components: return '''

    No UI components detected. This project may not have a frontend layer or uses a different component pattern.

    ''' html_parts = [] for comp in components[:10]: name = comp.get('name', 'Component') description = comp.get('description', f'{name} component') path = comp.get('path', '') props = comp.get('props', 'See source file') html_parts.append(f'''
    🧩
    {escape_html(name)}

    {escape_html(description)}

    {escape_html(path)}

    πŸ”§ Props & Usage Technical

    Props: {escape_html(props)}

    Usage Example

    <{escape_html(name)} />
    ''') return '\n'.join(html_parts) def generate_data_models_html(models: List[Dict]) -> str: """Generate HTML for data models.""" if not models: return '''

    No data models detected. This project may not use a database or uses a different data pattern.

    ''' html_parts = [] for model in models[:10]: name = model.get('name', 'Model') description = model.get('description', f'{name} data model') fields = model.get('fields', []) fields_html = '' if fields: fields_html = '' for field in fields[:10]: field_name = field.get('name', '') field_type = field.get('type', 'unknown') field_desc = field.get('description', '') fields_html += f''' ''' fields_html += '
    FieldTypeDescription
    {escape_html(field_name)} {escape_html(field_type)} {escape_html(field_desc)}
    ' html_parts.append(f'''

    {escape_html(name)}

    What it represents: {escape_html(description)}

    {fields_html}''') return '\n'.join(html_parts) def generate_er_diagram(models: List[Dict]) -> str: """Generate ASCII ER diagram.""" if not models: return '''β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ No data models detected β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜''' lines = [] for model in models[:4]: name = model.get('name', 'Model') fields = model.get('fields', [])[:4] width = max(len(name) + 4, max([len(f.get('name', '')) + len(f.get('type', '')) + 5 for f in fields] or [20])) lines.append('β”Œ' + '─' * width + '┐') lines.append('β”‚' + f' {name} '.center(width) + 'β”‚') lines.append('β”œ' + '─' * width + '─') for field in fields: field_str = f" {field.get('name', '')} : {field.get('type', '')}" lines.append('β”‚' + field_str.ljust(width) + 'β”‚') lines.append('β””' + '─' * width + 'β”˜') lines.append('') return '\n'.join(lines) def generate_glossary_html(terms: List[Dict]) -> str: """Generate HTML for glossary.""" html_parts = [] for term in terms: word = term.get('term', '') definition = term.get('definition', '') html_parts.append(f'''
    {escape_html(word)} {escape_html(definition)}
    ''') return '\n'.join(html_parts) def generate_system_diagram(tech_stack: Dict, structure: Dict) -> str: """Generate ASCII system architecture diagram.""" framework = tech_stack.get('framework', 'Application') database = tech_stack.get('database', '') ui = tech_stack.get('ui_framework', 'UI') diagram = f'''β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ Application Architecture β”‚ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”‚ β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ Client │───▢│ API │───▢│ Database β”‚ β”‚ β”‚ β”‚ ({ui or 'UI'}) β”‚ β”‚ ({framework or 'Server'}) β”‚ β”‚ ({database or 'Storage'}) β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜''' return diagram def generate_html(analysis: Dict, template: str) -> str: """Generate final HTML from analysis and template.""" project = analysis.get('project', {}) tech_stack = analysis.get('tech_stack', {}) structure = analysis.get('structure', {}) features = analysis.get('features', []) components = analysis.get('components', []) endpoints = analysis.get('api_endpoints', []) models = analysis.get('data_models', []) glossary = analysis.get('glossary_terms', []) # Basic replacements replacements = { '{{PROJECT_NAME}}': escape_html(project.get('name', 'Project')), '{{VERSION}}': escape_html(project.get('version', '1.0.0')), '{{TAGLINE}}': escape_html(project.get('description', 'Project documentation')), '{{DESCRIPTION}}': escape_html(project.get('description', 'This project provides various features and capabilities.')), '{{AUDIENCE}}': 'Developers, stakeholders, and anyone interested in understanding this project.', '{{GENERATED_DATE}}': datetime.now().strftime('%Y-%m-%d'), } # Generate complex sections html = template # Replace simple placeholders for key, value in replacements.items(): html = html.replace(key, value) # Replace capabilities section capabilities = [{'capability': cap.get('name'), 'description': cap.get('description')} for cap in features[:4]] if features else [ {'capability': 'Core Features', 'description': 'Main application functionality'}, {'capability': 'Easy Integration', 'description': 'Simple setup and configuration'} ] # Find and replace the capabilities placeholder section cap_html = generate_capabilities_html(capabilities) html = re.sub( r'.*?\s*', f'\n{cap_html}', html, flags=re.DOTALL ) # Replace technology stack html = re.sub( r'.*?', generate_tech_stack_html(tech_stack), html, flags=re.DOTALL ) # Generate and replace diagrams html = html.replace('{{SYSTEM_DIAGRAM}}', generate_system_diagram(tech_stack, structure)) html = html.replace('{{DIRECTORY_STRUCTURE}}', generate_directory_structure(structure)) html = html.replace('{{ER_DIAGRAM}}', generate_er_diagram(models)) # Replace features section html = re.sub( r'.*?\s*\s*', f'\n{generate_features_html(features)}', html, flags=re.DOTALL ) # Replace API endpoints html = re.sub( r'.*?', f'

    API Endpoints

    \n{generate_api_endpoints_html(endpoints)}', html, flags=re.DOTALL ) # Replace components html = re.sub( r'.*?\s*', f'\n{generate_components_html(components)}', html, flags=re.DOTALL ) # Replace data models html = re.sub( r'.*?', f'\n{generate_data_models_html(models)}', html, flags=re.DOTALL ) # Replace glossary html = re.sub( r'.*?', f'\n{generate_glossary_html(glossary)}\n', html, flags=re.DOTALL ) # Clean up remaining placeholders html = re.sub(r'\{\{[A-Z_]+\}\}', '', html) return html def main(): """Main entry point.""" if len(sys.argv) < 3: print("Usage: generate_html.py [output.html]") sys.exit(1) analysis_path = Path(sys.argv[1]) template_path = Path(sys.argv[2]) output_path = Path(sys.argv[3]) if len(sys.argv) > 3 else Path('documentation.html') if not analysis_path.exists(): print(f"Error: Analysis file not found: {analysis_path}", file=sys.stderr) sys.exit(1) if not template_path.exists(): print(f"Error: Template file not found: {template_path}", file=sys.stderr) sys.exit(1) analysis = load_analysis(analysis_path) template = load_template(template_path) html = generate_html(analysis, template) output_path.write_text(html, encoding='utf-8') print(f"HTML documentation generated: {output_path}") if __name__ == '__main__': main()