diff --git a/src/zona/builder.py b/src/zona/builder.py index e8d0d58..e99c4b0 100644 --- a/src/zona/builder.py +++ b/src/zona/builder.py @@ -1,4 +1,6 @@ -from zona.models import Item, Metadata +from zona.models import Item, Metadata, ItemType +from zona import markdown as zmd +from zona import util from pathlib import Path import frontmatter from dacite import from_dict @@ -29,20 +31,35 @@ def discover(root: Path, out_dir: Path) -> list[Item]: for path in base.rglob("*"): if path.is_file(): print(f"{subdir}: {path.relative_to(root)}") - if path.name.endswith(".md") and not path.is_relative_to( - root / "content" / "static" - ): - meta, content = split_metadata(path) - else: - meta = None - content = path.read_bytes() + # we only parse markdown files not in static/ destination = out_dir / path.relative_to(base) item = Item( source=path, destination=destination, url=str(destination.relative_to(out_dir)), - metadata=meta, - content=content, ) + if path.name.endswith(".md") and not path.is_relative_to( + root / "content" / "static" + ): + item.metadata, item.content = split_metadata(path) + item.type = ItemType.MARKDOWN + item.copy = False + item.destination = destination.with_suffix(".html") items.append(item) return items + + +def build(items: list[Item]): + for item in items: + dst = item.destination + # create parent dirs if needed + if item.type == ItemType.MARKDOWN: + assert item.content is not None + # parse markdown and render as html + raw_html = zmd.md_to_html(item.content) + # TODO: apply to template here + util.ensure_parents(dst) + dst.write_text(raw_html, encoding="utf-8") + else: + if item.copy: + util.copy_static_file(item.source, dst) diff --git a/src/zona/models.py b/src/zona/models.py index 7f4d5bb..2351abd 100644 --- a/src/zona/models.py +++ b/src/zona/models.py @@ -1,6 +1,8 @@ from pathlib import Path +from enum import Enum from datetime import date from dataclasses import dataclass +from typing import Literal @dataclass @@ -11,10 +13,18 @@ class Metadata: # add more options later... +class ItemType(Enum): + MARKDOWN = "markdown" + HTML = "html" + IMAGE = "image" + + @dataclass class Item: source: Path destination: Path url: str # relative to site root - metadata: Metadata | None # frontmatter - content: str | bytes + metadata: Metadata | None = None # frontmatter + content: str | None = None + type: ItemType | None = None + copy: bool = True diff --git a/src/zona/util.py b/src/zona/util.py new file mode 100644 index 0000000..cb3427b --- /dev/null +++ b/src/zona/util.py @@ -0,0 +1,13 @@ +from pathlib import Path +from shutil import copy2 + + +def ensure_parents(target: Path): + """Ensure the target's parent directories exist.""" + target.parent.mkdir(parents=True, exist_ok=True) + + +def copy_static_file(src: Path, dst: Path): + """Copy a static file from one location to another.""" + ensure_parents(dst) + copy2(src, dst) diff --git a/tests/test_metadata.py b/tests/test_builder.py similarity index 63% rename from tests/test_metadata.py rename to tests/test_builder.py index 1c73f37..c75e36b 100644 --- a/tests/test_metadata.py +++ b/tests/test_builder.py @@ -1,6 +1,6 @@ from datetime import date from zona.models import Metadata -from zona.builder import split_metadata, discover +from zona.builder import split_metadata, discover, build from pathlib import Path @@ -43,12 +43,13 @@ description: This is a test. """) style = staticd / "style.css" - style.write_text(""" + style_content = """ p { color: red; text-align: center; } -""") +""" + style.write_text(style_content) items = discover(tmp_path, outd) @@ -61,6 +62,7 @@ p { assert md_item.url == "post.md" assert isinstance(md_item.metadata, Metadata) assert md_item.metadata.title == "Test Post" + assert md_item.content is not None assert md_item.content.strip() == "# Hello World" st_item = items[1] @@ -69,3 +71,39 @@ p { assert st_item.destination.is_relative_to(outd) assert st_item.url == "static/style.css" assert st_item.metadata is None + + +def test_build(tmp_path: Path): + contentd = tmp_path / "content" + staticd = contentd / "static" + templatesd = tmp_path / "templates" + outd = tmp_path / "out" + + for d in [contentd, staticd, templatesd, outd]: + d.mkdir() + md_file = contentd / "post.md" + md_content = """--- +title: Test Post +date: 2025-06-03 +description: This is a test. +--- + +# Hello World + """ + md_file.write_text(md_content) + + style = staticd / "style.css" + style_content = """ +p { + color: red; + text-align: center; +} +""" + style.write_text(style_content) + + items = discover(tmp_path, outd) + build(items) + html = (outd / "post.html").read_text() + assert html.strip() == "

Hello World

" + s = (outd / "static" / "style.css").read_text() + assert s.strip() == style_content.strip()