From 1dcf8ad1f02d9dca842c6abb3dc77ef18ec4b883 Mon Sep 17 00:00:00 2001 From: 7000pctAUTO Date: Sat, 31 Jan 2026 17:12:01 +0000 Subject: [PATCH] Add generators module --- src/docgen/generators/html.py | 75 +++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 src/docgen/generators/html.py diff --git a/src/docgen/generators/html.py b/src/docgen/generators/html.py new file mode 100644 index 0000000..7728eb5 --- /dev/null +++ b/src/docgen/generators/html.py @@ -0,0 +1,75 @@ +"""HTML documentation generator.""" + +import re +from pathlib import Path +from jinja2 import Environment, FileSystemLoader, select_autoescape +from docgen.models import DocConfig, Endpoint +from docgen.generators import BaseGenerator + + +def slugify(text: str) -> str: + """Convert text to URL-friendly slug.""" + return re.sub(r'[^a-z0-9]+', '-', text.lower()).strip('-') + + +class HTMLGenerator(BaseGenerator): + """Generator for Stripe-like interactive HTML documentation.""" + + def __init__(self, config: DocConfig = None): + super().__init__(config) + template_dir = Path(__file__).parent.parent / "templates" + self.env = Environment( + loader=FileSystemLoader(template_dir), + autoescape=select_autoescape(["html", "xml"]), + ) + self.env.filters["slugify"] = slugify + + def generate(self, endpoints: list[Endpoint], output_dir: Path) -> Path: + """Generate HTML documentation.""" + output_dir = self._ensure_output_dir(output_dir) + + grouped = self._group_endpoints(endpoints) + base_template = self.env.get_template("base.html.jinja2") + sidebar_template = self.env.get_template("sidebar.html.jinja2") + + rendered_content = base_template.render( + title=self.config.title, + description=self.config.description, + version=self.config.version, + endpoints=endpoints, + grouped_endpoints=grouped, + sidebar=sidebar_template.render(grouped_endpoints=grouped), + theme=self.config.theme, + ) + + index_path = output_dir / "index.html" + index_path.write_text(rendered_content) + + self._copy_static_files(output_dir) + + return index_path + + def _group_endpoints(self, endpoints: list[Endpoint]) -> dict[str, list[Endpoint]]: + """Group endpoints by tags or path prefixes.""" + grouped = {} + for endpoint in endpoints: + if endpoint.tags: + tag = endpoint.tags[0] if endpoint.tags else "Other" + else: + parts = endpoint.path.strip("/").split("/") + tag = parts[0] if parts else "Other" + + if tag not in grouped: + grouped[tag] = [] + grouped[tag].append(endpoint) + return dict(sorted(grouped.items())) + + def _copy_static_files(self, output_dir: Path) -> None: + """Copy static CSS and JS files to output directory.""" + static_dir = Path(__file__).parent.parent / "static" + output_static = output_dir / "static" + output_static.mkdir(exist_ok=True) + + for file in static_dir.iterdir(): + if file.is_file(): + (output_static / file.name).write_text(file.read_text())