improved layout handling
This commit is contained in:
parent
c46f882191
commit
35ed79490a
5 changed files with 57 additions and 27 deletions
|
@ -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():
|
||||
|
|
|
@ -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
35
src/zona/layout.py
Normal 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)
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue