Add referenced file handling to Content

This commit is contained in:
2026-08-25 03:08:07 +02:00
parent 015f1375ec
commit 8c6c0d318e
3 changed files with 60 additions and 1 deletions
+35
View File
@@ -1,6 +1,8 @@
import logging
from urllib.parse import urlparse
from typing import TYPE_CHECKING, Any
from omicron.ssg.output.content import Content
from omicron.ssg.output.file import File
from pathlib import Path
import omicron.ssg.markdown as md
@@ -28,8 +30,41 @@ class Article(Content):
)
self.title: str = meta["title"]
def handle_reference(self, path: Path) -> Content | File:
site = self.site
if path in site.by_source:
return site.by_source[path]
return self.copy_referenced_file(path)
def resolve_references(self, tokens: list[md.Token]) -> None:
# MarkdownIt guarantees links are always in the second level
for token in tokens:
if token.type != "inline" or token.children is None:
continue
for child in token.children:
if child.type == "link_open":
attr = "href"
elif child.type == "image":
attr = "src"
else:
continue
href = child.attrs.get(attr)
assert type(href) is str, "unexpected link/image href type"
parsed = urlparse(href)
if parsed.scheme or parsed.netloc:
continue
if not parsed.path or parsed.path.startswith("/"):
continue
target = self.locate_reference(Path(parsed.path))
output = self.handle_reference(target)
child.attrs[attr] = parsed._replace(path=output.url).geturl()
def write(self) -> None:
tokens = md.parse(self.read_body())
self.resolve_references(tokens)
if self.summary is None:
self.summary = md.render_summary(tokens)
content = md.render(tokens)
+24
View File
@@ -1,6 +1,7 @@
import re
from typing import TYPE_CHECKING
from omicron.ssg.output.output import Output, OutputError, is_normalized
from omicron.ssg.output.file import File
from pathlib import Path
from datetime import date
@@ -49,6 +50,29 @@ class Content(Output):
path = Path(f"{section}/{slug}/index.html")
return path
def locate_reference(self, path: Path) -> Path:
if path.is_absolute():
raise ContentError("Can't resolve absolute references")
base = self.site.site_path / "content"
file = (self.source.parent / path).resolve()
if not file.is_relative_to(base):
raise ContentError("Reference escapes content directory")
return file
def copy_referenced_file(self, path: Path) -> File:
if not path.is_file():
raise ContentError(f"Referenced file '{path}' does not exist")
dest = self.destination.parent / path.name
existing = self.site.by_path.get(dest)
if isinstance(existing, File) and existing.source == path:
return existing
file = File(self.site, dest, path)
self.site.add_by_path(file)
file.write()
return file
def read_body(self) -> str:
with self.source.open("r", encoding="utf-8") as f:
f.readline() # opening ---
+1 -1
View File
@@ -32,7 +32,7 @@ class ConfigError(RuntimeError):
class Site:
def __init__(self, site: Path):
self.site_path = site.absolute()
self.site_path = site.resolve()
self.content: list[Content] = []
self.directories: list[Directory] = []
self.other_outputs: list[Output] = []