improved layout handling

This commit is contained in:
Daniel Fichtinger 2025-06-22 15:04:10 -04:00
parent c46f882191
commit 35ed79490a
5 changed files with 57 additions and 27 deletions

View file

@ -2,6 +2,7 @@ from rich import print
from zona.models import Item, Metadata, ItemType, BuildCtx
from zona import markdown as zmd
from zona.templates import Templater
from zona.layout import Layout
from zona import util
from pathlib import Path
import frontmatter
@ -40,28 +41,21 @@ def split_metadata(path: Path) -> tuple[Metadata, str]:
return metadata, post.content
def discover(root: Path, out_dir: Path) -> list[Item]:
required_dirs = {"content", "templates"}
found_dirs = {p.name for p in root.iterdir() if p.is_dir()}
missing = required_dirs - found_dirs
assert not missing, f"Missing required directories: {', '.join(missing)}"
def discover(layout: Layout) -> list[Item]:
items: list[Item] = []
subdir = "content"
base = root / subdir
base = layout.root / layout.content
for path in base.rglob("*"):
if path.is_file():
print(f"{subdir}: {path.relative_to(root)}")
# we only parse markdown files not in static/
destination = out_dir / path.relative_to(base)
destination = layout.output / path.relative_to(base)
item = Item(
source=path,
destination=destination,
url=str(destination.relative_to(out_dir)),
url=str(destination.relative_to(layout.output)),
)
if path.name.endswith(".md") and not path.is_relative_to(
root / "content" / "static"
layout.root / "content" / "static"
):
item.metadata, item.content = split_metadata(path)
item.type = ItemType.MARKDOWN
@ -73,17 +67,17 @@ def discover(root: Path, out_dir: Path) -> list[Item]:
relative = path.relative_to(base).with_suffix("")
name = relative.stem
item.destination = (
out_dir / relative.parent / name / "index.html"
layout.output / relative.parent / name / "index.html"
)
rel_url = item.destination.parent.relative_to(out_dir)
rel_url = item.destination.parent.relative_to(layout.output)
item.url = "" if rel_url == Path(".") else rel_url.as_posix()
items.append(item)
return items
def build(root: Path, items: list[Item]):
ctx = BuildCtx(root=root, base=root / "content")
templater = Templater(root / "templates")
def build(layout: Layout, items: list[Item]):
ctx = BuildCtx(layout)
templater = Templater(layout.templates)
ctx.item_map = {item.source.resolve(): item for item in items}
# print(item_map)
for item in ctx.item_map.values():

View file

@ -1,18 +1,16 @@
import typer
from pathlib import Path
from zona import builder, server
from zona.layout import Layout, discover_layout
app = typer.Typer()
@app.command()
def build(in_dir: str | None = None, out_dir: str | None = None):
if in_dir is None:
in_dir = "."
if out_dir is None:
out_dir = "public"
items = builder.discover(Path(in_dir), Path(out_dir))
builder.build(Path(in_dir), items)
def build(root: Path | None = None, output: Path | None = None):
layout: Layout = discover_layout(root, output)
items = builder.discover(layout)
builder.build(layout, items)
@app.command()

35
src/zona/layout.py Normal file
View file

@ -0,0 +1,35 @@
from pathlib import Path
from dataclasses import dataclass
@dataclass
class Layout:
root: Path
content: Path
templates: Path
output: Path
@classmethod
def from_input(cls, root: Path, output: Path | None = None) -> "Layout":
layout = cls(
root=root.resolve(),
content=(root / "content").resolve(),
templates=(root / "templates").resolve(),
output=(root / "public").resolve() if not output else output,
)
for path in [layout.content, layout.templates]:
if not path.is_dir():
raise FileNotFoundError(f"Missing required directory: {path}")
return layout
def discover_layout(
cli_root: Path | None = None, cli_output: Path | None = None
) -> Layout:
if cli_root:
root = cli_root
else:
# TODO: config based discovery
# We just set cwd for now
root = Path(".")
return Layout.from_input(root, cli_output)

View file

@ -35,7 +35,9 @@ class ZonaRenderer(HTMLRenderer):
_href = href
if href.startswith("/"):
# resolve relative to content root
resolved = (self.ctx.base / cur.relative_to("/")).resolve()
resolved = (
self.ctx.layout.content / cur.relative_to("/")
).resolve()
else:
# treat as relative link and try to resolve
resolved = (self.source.parent / cur).resolve()

View file

@ -3,6 +3,8 @@ from enum import Enum
from datetime import date
from dataclasses import dataclass, field
from zona.layout import Layout
@dataclass
class Metadata:
@ -34,6 +36,5 @@ class Item:
@dataclass
class BuildCtx:
root: Path
base: Path
layout: Layout
item_map: dict[Path, Item] = field(default_factory=dict)