rendered MkDocs
This commit is contained in:
@@ -7,9 +7,11 @@ until their contracts are finalized.
|
||||
"""
|
||||
|
||||
from .loader import GriffeLoader
|
||||
from .renderers import MkDocsRenderer
|
||||
from . import model
|
||||
|
||||
__all__ = [
|
||||
"GriffeLoader",
|
||||
"MkDocsRenderer",
|
||||
"model",
|
||||
]
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
from .loader import GriffeLoader
|
||||
from .renderers import MkDocsRenderer
|
||||
from . import model
|
||||
|
||||
__all__: list[str]
|
||||
|
||||
5
docforge/renderers/__init__.py
Normal file
5
docforge/renderers/__init__.py
Normal file
@@ -0,0 +1,5 @@
|
||||
from docforge.renderers.mkdocs import MkDocsRenderer
|
||||
|
||||
__all__ = [
|
||||
"MkDocsRenderer",
|
||||
]
|
||||
3
docforge/renderers/__init__.pyi
Normal file
3
docforge/renderers/__init__.pyi
Normal file
@@ -0,0 +1,3 @@
|
||||
from docforge.renderers.mkdocs import MkDocsRenderer
|
||||
|
||||
__all__ = ["MkDocsRenderer"]
|
||||
13
docforge/renderers/base.py
Normal file
13
docforge/renderers/base.py
Normal file
@@ -0,0 +1,13 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from docforge.model import Project
|
||||
|
||||
|
||||
class RendererConfig:
|
||||
"""Renderer configuration container."""
|
||||
|
||||
def __init__(self, out_dir: Path, project: Project) -> None:
|
||||
self.out_dir = out_dir
|
||||
self.project = project
|
||||
26
docforge/renderers/base.pyi
Normal file
26
docforge/renderers/base.pyi
Normal file
@@ -0,0 +1,26 @@
|
||||
from pathlib import Path
|
||||
from typing import Protocol
|
||||
|
||||
from docforge.model import Project
|
||||
|
||||
|
||||
class RendererConfig:
|
||||
"""Renderer configuration container."""
|
||||
|
||||
out_dir: Path
|
||||
project: Project
|
||||
|
||||
def __init__(self, out_dir: Path, project: Project) -> None: ...
|
||||
|
||||
|
||||
class DocRenderer(Protocol):
|
||||
"""Renderer interface."""
|
||||
|
||||
name: str
|
||||
|
||||
def generate_sources(
|
||||
self,
|
||||
project: Project,
|
||||
out_dir: Path,
|
||||
) -> None:
|
||||
"""Generate renderer-specific source files."""
|
||||
58
docforge/renderers/mkdocs.py
Normal file
58
docforge/renderers/mkdocs.py
Normal file
@@ -0,0 +1,58 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from docforge.model import Project
|
||||
|
||||
|
||||
class MkDocsRenderer:
|
||||
"""MkDocs source generator using mkdocstrings."""
|
||||
|
||||
name = "mkdocs"
|
||||
|
||||
def generate_sources(self, project: Project, out_dir: Path) -> None:
|
||||
"""
|
||||
Generate Markdown files with mkdocstrings directives.
|
||||
|
||||
Structure rules:
|
||||
- Each top-level package gets a directory
|
||||
- Modules become .md files
|
||||
- Packages (__init__) become index.md
|
||||
"""
|
||||
for module in project.get_all_modules():
|
||||
self._write_module(project, module, out_dir)
|
||||
|
||||
# -------------------------
|
||||
# Internal helpers
|
||||
# -------------------------
|
||||
|
||||
def _write_module(self, project: Project, module, out_dir: Path) -> None:
|
||||
parts = module.path.split(".")
|
||||
|
||||
# Root package directory
|
||||
pkg_dir = out_dir / parts[0]
|
||||
pkg_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Package (__init__.py) → index.md
|
||||
if module.path == parts[0]:
|
||||
md_path = pkg_dir / "index.md"
|
||||
title = parts[0].replace("_", " ").title()
|
||||
else:
|
||||
# Submodule → <name>.md
|
||||
md_path = pkg_dir / f"{parts[-1]}.md"
|
||||
title = parts[-1].replace("_", " ").title()
|
||||
|
||||
content = self._render_markdown(title, module.path)
|
||||
|
||||
# Idempotent write
|
||||
if md_path.exists():
|
||||
if md_path.read_text(encoding="utf-8") == content:
|
||||
return
|
||||
|
||||
md_path.write_text(content, encoding="utf-8")
|
||||
|
||||
def _render_markdown(self, title: str, module_path: str) -> str:
|
||||
return (
|
||||
f"# {title}\n\n"
|
||||
f"::: {module_path}\n"
|
||||
)
|
||||
17
docforge/renderers/mkdocs.pyi
Normal file
17
docforge/renderers/mkdocs.pyi
Normal file
@@ -0,0 +1,17 @@
|
||||
from pathlib import Path
|
||||
|
||||
from docforge.model import Project
|
||||
from docforge.renderers.base import DocRenderer
|
||||
|
||||
|
||||
class MkDocsRenderer:
|
||||
"""MkDocs source generator using mkdocstrings."""
|
||||
|
||||
name: str
|
||||
|
||||
def generate_sources(
|
||||
self,
|
||||
project: Project,
|
||||
out_dir: Path,
|
||||
) -> None:
|
||||
"""Generate Markdown files with mkdocstrings directives."""
|
||||
0
tests/renderers/__init__.py
Normal file
0
tests/renderers/__init__.py
Normal file
19
tests/renderers/test_mkdocs_content.py
Normal file
19
tests/renderers/test_mkdocs_content.py
Normal file
@@ -0,0 +1,19 @@
|
||||
from pathlib import Path
|
||||
|
||||
from docforge import MkDocsRenderer
|
||||
from docforge.model import Project, Module
|
||||
|
||||
|
||||
def test_mkdocs_file_content(tmp_path: Path):
|
||||
project = Project("testpkg")
|
||||
project.add_module(Module("testpkg.mod"))
|
||||
|
||||
out_dir = tmp_path / "docs"
|
||||
renderer = MkDocsRenderer()
|
||||
|
||||
renderer.generate_sources(project, out_dir)
|
||||
|
||||
content = (out_dir / "testpkg" / "mod.md").read_text()
|
||||
|
||||
assert "# Mod" in content
|
||||
assert "::: testpkg.mod" in content
|
||||
20
tests/renderers/test_mkdocs_idempotency.py
Normal file
20
tests/renderers/test_mkdocs_idempotency.py
Normal file
@@ -0,0 +1,20 @@
|
||||
from pathlib import Path
|
||||
|
||||
from docforge import MkDocsRenderer
|
||||
from docforge.model import Project, Module
|
||||
|
||||
|
||||
def test_mkdocs_idempotent(tmp_path: Path):
|
||||
project = Project("testpkg")
|
||||
project.add_module(Module("testpkg.mod"))
|
||||
|
||||
out_dir = tmp_path / "docs"
|
||||
renderer = MkDocsRenderer()
|
||||
|
||||
renderer.generate_sources(project, out_dir)
|
||||
first = (out_dir / "testpkg" / "mod.md").read_text()
|
||||
|
||||
renderer.generate_sources(project, out_dir)
|
||||
second = (out_dir / "testpkg" / "mod.md").read_text()
|
||||
|
||||
assert first == second
|
||||
18
tests/renderers/test_mkdocs_structure.py
Normal file
18
tests/renderers/test_mkdocs_structure.py
Normal file
@@ -0,0 +1,18 @@
|
||||
from pathlib import Path
|
||||
|
||||
from docforge import MkDocsRenderer
|
||||
from docforge.model import Project, Module
|
||||
|
||||
|
||||
def test_mkdocs_directory_structure(tmp_path: Path):
|
||||
project = Project("testpkg")
|
||||
project.add_module(Module("testpkg"))
|
||||
project.add_module(Module("testpkg.sub"))
|
||||
|
||||
out_dir = tmp_path / "docs"
|
||||
renderer = MkDocsRenderer()
|
||||
|
||||
renderer.generate_sources(project, out_dir)
|
||||
|
||||
assert (out_dir / "testpkg" / "index.md").exists()
|
||||
assert (out_dir / "testpkg" / "sub.md").exists()
|
||||
Reference in New Issue
Block a user