diff --git a/src/zona/builder.py b/src/zona/builder.py index 4a64e25..78300f9 100644 --- a/src/zona/builder.py +++ b/src/zona/builder.py @@ -1,5 +1,5 @@ from rich import print -from zona.models import Item, Metadata, ItemType +from zona.models import Item, Metadata, ItemType, BuildCtx from zona import markdown as zmd from zona.templates import Templater from zona import util @@ -82,17 +82,23 @@ def discover(root: Path, out_dir: Path) -> list[Item]: def build(root: Path, items: list[Item]): + ctx = BuildCtx(root=root) templater = Templater(root / "templates") - item_map = {item.source.resolve(): item for item in items} + ctx.item_map = {item.source.resolve(): item for item in items} # print(item_map) - for item in item_map.values(): + for item in ctx.item_map.values(): dst = item.destination # print(item) # 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) + raw_html = zmd.md_to_html( + content=item.content, + resolve_links=True, + source=item.source, + ctx=ctx, + ) # TODO: test this rendered = templater.render_item(item, raw_html) util.ensure_parents(dst) diff --git a/src/zona/markdown.py b/src/zona/markdown.py index e2a8d8d..580c33f 100644 --- a/src/zona/markdown.py +++ b/src/zona/markdown.py @@ -1,11 +1,12 @@ +from rich import print from typing import Any, override from pathlib import Path -from marko import Markdown from marko.inline import Link, Image from marko.html_renderer import HTMLRenderer from marko.parser import Parser -from zona.models import Item +from zona.models import BuildCtx, Item +from zona import util class ZonaRenderer(HTMLRenderer): @@ -13,28 +14,31 @@ class ZonaRenderer(HTMLRenderer): self, resolve: bool = False, source: Path | None = None, - item_map: dict[Path, Item] | None = None, + ctx: BuildCtx | None = None, ): + # print("Zona renderer initializing...") super().__init__() self.resolve: bool = resolve if self.resolve: + # print("Resolve is set") assert source is not None - assert item_map is not None + assert ctx is not None self.source: Path = source.resolve() - self.map: dict[Path, Item] = item_map + self.ctx: BuildCtx = ctx # TODO: resolve relative links and replace with url? @override def render_link(self, element: Link): href = element.dest + print(f"Rendering link, original: {href}") assert isinstance(href, str) if self.resolve: cur = Path(href) - # TODO: fix relative path issue - if not href.startswith(("http", "https")) and cur.is_relative_to( - self.source - ): - href = self.map[self.source].url + _href = href + resolved = (self.source.parent / cur).resolve() + if resolved.exists(): + href = util.normalize_url(self.ctx.item_map[resolved].url) + print(f"Link in file {self.source}: {_href} resolved to {href}") body: Any = self.render_children(element) return f'{body}' @@ -56,9 +60,13 @@ def md_to_html( content: str, resolve_links: bool = False, source: Path | None = None, - item_map: dict[Path, Item] | None = None, + ctx: BuildCtx | None = None, ) -> str: + if resolve_links and (source is None or ctx is None): + raise TypeError( + "md_to_html() missing source and ctx when resolve_links is true" + ) parser = Parser() ast = parser.parse(content) - renderer = ZonaRenderer(resolve_links, source, item_map) + renderer = ZonaRenderer(resolve_links, source, ctx) return renderer.render(ast) diff --git a/src/zona/models.py b/src/zona/models.py index c3be79c..aff3530 100644 --- a/src/zona/models.py +++ b/src/zona/models.py @@ -1,7 +1,7 @@ from pathlib import Path from enum import Enum from datetime import date -from dataclasses import dataclass +from dataclasses import dataclass, field @dataclass @@ -30,3 +30,9 @@ class Item: content: str | None = None type: ItemType | None = None copy: bool = True + + +@dataclass +class BuildCtx: + root: Path + item_map: dict[Path, Item] = field(default_factory=dict) diff --git a/src/zona/util.py b/src/zona/util.py index 6c96d87..d610ed3 100644 --- a/src/zona/util.py +++ b/src/zona/util.py @@ -18,3 +18,9 @@ def filename_to_title(path: Path) -> str: name = path.stem words = name.replace("-", " ").replace("_", " ") return string.capwords(words) + + +def normalize_url(url: str) -> str: + if not url.startswith("/"): + url = "/" + url + return url