added packaged template resources
This commit is contained in:
parent
c4505502e8
commit
ca58a0758c
13 changed files with 135 additions and 54 deletions
|
@ -1,5 +1,6 @@
|
||||||
from rich import print
|
from rich import print
|
||||||
from zona.models import Item, ItemType
|
from zona.models import Item, ItemType
|
||||||
|
from zona.metadata import parse_metadata
|
||||||
from zona import markdown as zmd
|
from zona import markdown as zmd
|
||||||
from zona.templates import Templater
|
from zona.templates import Templater
|
||||||
from zona.layout import Layout, discover_layout
|
from zona.layout import Layout, discover_layout
|
||||||
|
@ -38,7 +39,7 @@ class ZonaBuilder:
|
||||||
if path.name.endswith(".md") and not path.is_relative_to(
|
if path.name.endswith(".md") and not path.is_relative_to(
|
||||||
layout.root / "content" / "static"
|
layout.root / "content" / "static"
|
||||||
):
|
):
|
||||||
item.metadata, item.content = util.parse_metadata(path)
|
item.metadata, item.content = parse_metadata(path)
|
||||||
item.type = ItemType.MARKDOWN
|
item.type = ItemType.MARKDOWN
|
||||||
item.copy = False
|
item.copy = False
|
||||||
name = destination.stem
|
name = destination.stem
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>{{ metadata.title }}</title>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<link
|
||||||
|
href="{{ metadata.style }}"
|
||||||
|
rel="stylesheet"
|
||||||
|
type="text/css"
|
||||||
|
media="all"
|
||||||
|
/>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="container">
|
||||||
|
{% if header %}
|
||||||
|
<header id="header">
|
||||||
|
<center>
|
||||||
|
{{ header | safe }}
|
||||||
|
</center>
|
||||||
|
<hr>
|
||||||
|
</header>
|
||||||
|
{% endif %}
|
||||||
|
{% block content %}{% endblock %}
|
||||||
|
{% if footer %}
|
||||||
|
<footer id="footer">
|
||||||
|
<hr>
|
||||||
|
{{ footer | safe }}
|
||||||
|
</footer>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
1
src/zona/data/templates/footer.md
Normal file
1
src/zona/data/templates/footer.md
Normal file
|
@ -0,0 +1 @@
|
||||||
|
The footer content.
|
2
src/zona/data/templates/header.md
Normal file
2
src/zona/data/templates/header.md
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
- One
|
||||||
|
- Two
|
6
src/zona/data/templates/page.html
Normal file
6
src/zona/data/templates/page.html
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<article>{{ content | safe }}</article>
|
||||||
|
{% endblock %}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from dataclasses import dataclass, asdict
|
from dataclasses import dataclass, asdict
|
||||||
from zona.config import ZonaConfig, find_config
|
from zona.config import ZonaConfig, find_config
|
||||||
|
from zona import util
|
||||||
import yaml
|
import yaml
|
||||||
|
|
||||||
|
|
||||||
|
@ -54,9 +55,19 @@ def initialize_site(root: Path | None = None):
|
||||||
raise FileExistsError(f"Config file already exists at {config}")
|
raise FileExistsError(f"Config file already exists at {config}")
|
||||||
# create requires layout
|
# create requires layout
|
||||||
layout = Layout.from_input(root=root, validate=False)
|
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():
|
if not dir.is_dir():
|
||||||
dir.mkdir()
|
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_path = layout.root / "config.yml"
|
||||||
config = ZonaConfig()
|
config = ZonaConfig()
|
||||||
with open(config_path, "w") as f:
|
with open(config_path, "w") as f:
|
||||||
|
|
|
@ -14,8 +14,6 @@ from pygments.formatters import HtmlFormatter
|
||||||
from zona import util
|
from zona import util
|
||||||
from zona.models import Item
|
from zona.models import Item
|
||||||
|
|
||||||
# TODO: create Ashen pygments style (separate package probably)
|
|
||||||
|
|
||||||
|
|
||||||
class ZonaRenderer(HTMLRenderer):
|
class ZonaRenderer(HTMLRenderer):
|
||||||
def __init__(
|
def __init__(
|
||||||
|
|
51
src/zona/metadata.py
Normal file
51
src/zona/metadata.py
Normal file
|
@ -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
|
|
@ -1,20 +1,8 @@
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from datetime import date
|
from dataclasses import dataclass
|
||||||
from dataclasses import dataclass, field
|
|
||||||
|
|
||||||
from zona.layout import Layout
|
from zona.metadata import Metadata
|
||||||
|
|
||||||
|
|
||||||
@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"
|
|
||||||
|
|
||||||
|
|
||||||
class ItemType(Enum):
|
class ItemType(Enum):
|
||||||
|
|
|
@ -1,14 +1,28 @@
|
||||||
from datetime import date
|
from typing import NamedTuple
|
||||||
|
from rich import print
|
||||||
|
from importlib import resources
|
||||||
import fnmatch
|
import fnmatch
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from shutil import copy2
|
from shutil import copy2
|
||||||
import string
|
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):
|
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:
|
def should_ignore(path: Path, patterns: list[str], base: Path) -> bool:
|
||||||
rel_path = path.relative_to(base)
|
rel_path = path.relative_to(base)
|
||||||
return any(fnmatch.fnmatch(str(rel_path), pattern) for pattern in patterns)
|
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
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import pytest
|
import pytest
|
||||||
from datetime import date
|
from datetime import date
|
||||||
from zona.models import Metadata
|
from zona.metadata import Metadata
|
||||||
from zona.builder import split_metadata, discover, build
|
from zona.builder import split_metadata, discover, build
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
|
@ -7,3 +7,8 @@ def test_title(tmp_path: Path):
|
||||||
b = tmp_path / "Writing_emails_Post.md"
|
b = tmp_path / "Writing_emails_Post.md"
|
||||||
assert util.filename_to_title(a) == "My First Post"
|
assert util.filename_to_title(a) == "My First Post"
|
||||||
assert util.filename_to_title(b) == "Writing Emails Post"
|
assert util.filename_to_title(b) == "Writing Emails Post"
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_resourcees():
|
||||||
|
util.get_resources("templates")
|
||||||
|
assert True
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue