feat: add wiki build kind with file-structure-derived navigation

- add build_wiki_nav deriving MkDocs nav from docs/wiki file structure
  (index.md -> Home, numeric prefixes stripped and title-cased, nested
  dirs become groups, natural ordering)
- add --wiki / --wiki-dir to build; wiki-only builds need no --module
- merge wiki nav before generated lib/api nav; wiki Home replaces the
  nav spec Home entry
- add mkdocs.wiki.yml template fragment and nav/cli tests
- dogfood doc-forge's own docs/wiki and regenerate site output
This commit is contained in:
2026-09-11 23:38:23 +05:30
parent bacf17b930
commit 8c6c46caf2
44 changed files with 798 additions and 17 deletions

View File

@@ -13,7 +13,12 @@ import click
import yaml
from docforge.loaders import GriffeLoader, discover_module_paths
from docforge.nav import MkDocsNavEmitter, load_nav_spec, resolve_nav
from docforge.nav import (
MkDocsNavEmitter,
build_wiki_nav,
load_nav_spec,
resolve_nav,
)
from docforge.renderers import MkDocsRenderer
@@ -78,17 +83,20 @@ def generate_config(
modes: Iterable[str] | None = None,
site_description: str | None = None,
site_author: str | None = None,
wiki_dir: Path | None = None,
) -> None:
"""
Generate an `mkdocs.yml` configuration file.
The configuration is created by combining a template configuration
with a navigation structure derived from the docforge navigation
specification.
specification (and, when a wiki directory is provided, from the wiki
file structure).
The ``docs_dir`` is always written relative to the MkDocs root and is
expected to be the shared documentation parent (for example ``docs``),
with generated sources nested under ``lib/`` or ``api/`` subdirectories.
with generated sources nested under ``lib/`` or ``api/`` subdirectories
and hand-written wiki content under a ``wiki/`` subdirectory.
Args:
docs_dir (Path):
@@ -110,8 +118,9 @@ def generate_config(
modes (Optional[Iterable[str]]):
Documentation modes to enable. Each mode contributes its own
built-in template fragment (for example ``lib`` or ``api``),
merged on top of the shared ``mkdocs.common.yml`` template.
built-in template fragment (for example ``lib``, ``api``, or
``wiki``), merged on top of the shared ``mkdocs.common.yml``
template.
site_description (Optional[str]):
Optional site description written into the configuration.
@@ -119,16 +128,34 @@ def generate_config(
site_author (Optional[str]):
Optional site author written into the configuration.
wiki_dir (Optional[Path]):
Optional path to a hand-written wiki directory (for example
``docs/wiki``). When provided, the site navigation is derived
from the wiki file structure and placed before the navigation
groups defined in ``nav_file``.
Raises:
click.FileError:
If the navigation specification or template file cannot be found.
If the navigation specification, template, or wiki directory
cannot be found.
"""
if not nav_file.exists():
if not nav_file.exists() and wiki_dir is None:
raise click.FileError(str(nav_file), hint="Nav spec not found")
spec = load_nav_spec(nav_file)
resolved = resolve_nav(spec, docs_dir)
nav_block = MkDocsNavEmitter().emit(resolved)
nav_block: list[dict] = []
if nav_file.exists():
spec = load_nav_spec(nav_file)
resolved = resolve_nav(spec, docs_dir)
nav_block = MkDocsNavEmitter().emit(resolved)
else:
spec = None
if wiki_dir is not None:
if not wiki_dir.exists():
raise click.FileError(str(wiki_dir), hint="Wiki dir not found")
wiki_nav = build_wiki_nav(wiki_dir)
if wiki_nav:
nav_block = wiki_nav + [entry for entry in nav_block if "Home" not in entry]
data = _load_template(template, modes)
@@ -140,7 +167,7 @@ def generate_config(
data["docs_dir"] = Path(os.path.relpath(docs_dir, out.parent)).as_posix()
data["nav"] = nav_block
if spec.icon:
if spec is not None and spec.icon:
theme = data.setdefault("theme", {})
if not isinstance(theme, dict):
theme = {}