diff --git a/src/zona/builder.py b/src/zona/builder.py
index 3f72992..e4891f2 100644
--- a/src/zona/builder.py
+++ b/src/zona/builder.py
@@ -1,5 +1,6 @@
from rich import print
from zona.models import Item, ItemType
+from zona.metadata import parse_metadata
from zona import markdown as zmd
from zona.templates import Templater
from zona.layout import Layout, discover_layout
@@ -38,7 +39,7 @@ class ZonaBuilder:
if path.name.endswith(".md") and not path.is_relative_to(
layout.root / "content" / "static"
):
- item.metadata, item.content = util.parse_metadata(path)
+ item.metadata, item.content = parse_metadata(path)
item.type = ItemType.MARKDOWN
item.copy = False
name = destination.stem
diff --git a/src/zona/data/templates/base.html b/src/zona/data/templates/base.html
index e69de29..87a8392 100644
--- a/src/zona/data/templates/base.html
+++ b/src/zona/data/templates/base.html
@@ -0,0 +1,34 @@
+
+
+
+ {{ metadata.title }}
+
+
+
+
+
+
+ {% if header %}
+
+ {% endif %}
+ {% block content %}{% endblock %}
+ {% if footer %}
+
+ {% endif %}
+
+
+
+
diff --git a/src/zona/data/templates/footer.md b/src/zona/data/templates/footer.md
new file mode 100644
index 0000000..653482d
--- /dev/null
+++ b/src/zona/data/templates/footer.md
@@ -0,0 +1 @@
+The footer content.
diff --git a/src/zona/data/templates/header.md b/src/zona/data/templates/header.md
new file mode 100644
index 0000000..a0e3630
--- /dev/null
+++ b/src/zona/data/templates/header.md
@@ -0,0 +1,2 @@
+- One
+- Two
diff --git a/src/zona/data/templates/page.html b/src/zona/data/templates/page.html
new file mode 100644
index 0000000..d568de1
--- /dev/null
+++ b/src/zona/data/templates/page.html
@@ -0,0 +1,6 @@
+{% extends "base.html" %}
+
+{% block content %}
+{{ content | safe }}
+{% endblock %}
+
diff --git a/src/zona/data/templates/post.html b/src/zona/data/templates/post.html
deleted file mode 100644
index e69de29..0000000
diff --git a/src/zona/layout.py b/src/zona/layout.py
index facce4b..f309dbd 100644
--- a/src/zona/layout.py
+++ b/src/zona/layout.py
@@ -1,6 +1,7 @@
from pathlib import Path
from dataclasses import dataclass, asdict
from zona.config import ZonaConfig, find_config
+from zona import util
import yaml
@@ -54,9 +55,19 @@ def initialize_site(root: Path | None = None):
raise FileExistsError(f"Config file already exists at {config}")
# create requires layout
layout = Layout.from_input(root=root, validate=False)
- for dir in [layout.root, layout.content, layout.templates]:
+ # load template resources
+ templates = util.get_resources("templates")
+ for dir, resources in [
+ (layout.root, None),
+ (layout.content, None),
+ (layout.templates, templates),
+ ]:
if not dir.is_dir():
dir.mkdir()
+ if resources is not None:
+ for r in resources:
+ Path(r.name).write_text(r.contents)
+
config_path = layout.root / "config.yml"
config = ZonaConfig()
with open(config_path, "w") as f:
diff --git a/src/zona/markdown.py b/src/zona/markdown.py
index e9d5cb1..87ea02a 100644
--- a/src/zona/markdown.py
+++ b/src/zona/markdown.py
@@ -14,8 +14,6 @@ from pygments.formatters import HtmlFormatter
from zona import util
from zona.models import Item
-# TODO: create Ashen pygments style (separate package probably)
-
class ZonaRenderer(HTMLRenderer):
def __init__(
diff --git a/src/zona/metadata.py b/src/zona/metadata.py
new file mode 100644
index 0000000..7b722dc
--- /dev/null
+++ b/src/zona/metadata.py
@@ -0,0 +1,51 @@
+from dataclasses import dataclass
+from pathlib import Path
+from datetime import date
+
+from dacite.config import Config
+from dacite.core import from_dict
+from dacite.exceptions import DaciteError
+from yaml import YAMLError
+import zona.util
+import frontmatter
+
+
+@dataclass
+class Metadata:
+ title: str
+ date: date
+ description: str | None
+ style: str | None = "/static/style.css"
+ header: bool = True
+ footer: bool = True
+ template: str = "page.html"
+
+
+def parse_metadata(path: Path) -> tuple[Metadata, str]:
+ """
+ Parses a file and returns parsed Metadata and its content. Defaults
+ are applied for missing fields. If there is no metadata, a Metadata
+ with default values is returned.
+
+ Raises:
+ ValueError: If the metadata block is malformed in any way.
+ """
+ try:
+ post = frontmatter.load(str(path))
+ except YAMLError as e:
+ raise ValueError(f"YAML frontmatter error in {path}: {e}")
+ raw_meta = post.metadata or {}
+ defaults = {
+ "title": zona.util.filename_to_title(path),
+ "date": date.fromtimestamp(path.stat().st_mtime),
+ }
+ meta = {**defaults, **raw_meta}
+ try:
+ metadata = from_dict(
+ data_class=Metadata,
+ data=meta,
+ config=Config(check_types=True, strict=True),
+ )
+ except DaciteError as e:
+ raise ValueError(f"Malformed metadata in {path}: {e}")
+ return metadata, post.content
diff --git a/src/zona/models.py b/src/zona/models.py
index faf402d..5c6b0ab 100644
--- a/src/zona/models.py
+++ b/src/zona/models.py
@@ -1,20 +1,8 @@
from pathlib import Path
from enum import Enum
-from datetime import date
-from dataclasses import dataclass, field
+from dataclasses import dataclass
-from zona.layout import Layout
-
-
-@dataclass
-class Metadata:
- title: str
- date: date
- description: str | None
- style: str | None = "/static/style.css"
- header: bool = True
- footer: bool = True
- template: str = "page.html"
+from zona.metadata import Metadata
class ItemType(Enum):
diff --git a/src/zona/util.py b/src/zona/util.py
index c67a92a..3244249 100644
--- a/src/zona/util.py
+++ b/src/zona/util.py
@@ -1,14 +1,28 @@
-from datetime import date
+from typing import NamedTuple
+from rich import print
+from importlib import resources
import fnmatch
from pathlib import Path
from shutil import copy2
import string
-from dacite import Config, DaciteError, from_dict
-import frontmatter
-from yaml import YAMLError
-from zona.models import Metadata
+class ZonaResource(NamedTuple):
+ name: str
+ contents: str
+
+
+def get_resources(subdir: str) -> list[ZonaResource]:
+ """Load the packaged resources in data/subdir"""
+ out: list[ZonaResource] = []
+ for resource in (
+ resources.files("zona").joinpath(f"data/{subdir}").iterdir()
+ ):
+ out.append(
+ ZonaResource(f"{subdir}/{resource.name}", resource.read_text())
+ )
+ print(out)
+ return out
def ensure_parents(target: Path):
@@ -37,33 +51,3 @@ def normalize_url(url: str) -> str:
def should_ignore(path: Path, patterns: list[str], base: Path) -> bool:
rel_path = path.relative_to(base)
return any(fnmatch.fnmatch(str(rel_path), pattern) for pattern in patterns)
-
-
-def parse_metadata(path: Path) -> tuple[Metadata, str]:
- """
- Parses a file and returns parsed Metadata and its content. Defaults
- are applied for missing fields. If there is no metadata, a Metadata
- with default values is returned.
-
- Raises:
- ValueError: If the metadata block is malformed in any way.
- """
- try:
- post = frontmatter.load(str(path))
- except YAMLError as e:
- raise ValueError(f"YAML frontmatter error in {path}: {e}")
- raw_meta = post.metadata or {}
- defaults = {
- "title": filename_to_title(path),
- "date": date.fromtimestamp(path.stat().st_mtime),
- }
- meta = {**defaults, **raw_meta}
- try:
- metadata = from_dict(
- data_class=Metadata,
- data=meta,
- config=Config(check_types=True, strict=True),
- )
- except DaciteError as e:
- raise ValueError(f"Malformed metadata in {path}: {e}")
- return metadata, post.content
diff --git a/tests/test_builder.py b/tests/test_builder.py
index cce7cd7..61e148e 100644
--- a/tests/test_builder.py
+++ b/tests/test_builder.py
@@ -1,6 +1,6 @@
import pytest
from datetime import date
-from zona.models import Metadata
+from zona.metadata import Metadata
from zona.builder import split_metadata, discover, build
from pathlib import Path
diff --git a/tests/test_util.py b/tests/test_util.py
index a32e99b..2837df4 100644
--- a/tests/test_util.py
+++ b/tests/test_util.py
@@ -7,3 +7,8 @@ def test_title(tmp_path: Path):
b = tmp_path / "Writing_emails_Post.md"
assert util.filename_to_title(a) == "My First Post"
assert util.filename_to_title(b) == "Writing Emails Post"
+
+
+def test_get_resourcees():
+ util.get_resources("templates")
+ assert True