Add referenced file handling to Content
This commit is contained in:
@@ -1,6 +1,8 @@
|
|||||||
import logging
|
import logging
|
||||||
|
from urllib.parse import urlparse
|
||||||
from typing import TYPE_CHECKING, Any
|
from typing import TYPE_CHECKING, Any
|
||||||
from omicron.ssg.output.content import Content
|
from omicron.ssg.output.content import Content
|
||||||
|
from omicron.ssg.output.file import File
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import omicron.ssg.markdown as md
|
import omicron.ssg.markdown as md
|
||||||
|
|
||||||
@@ -28,8 +30,41 @@ class Article(Content):
|
|||||||
)
|
)
|
||||||
self.title: str = meta["title"]
|
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:
|
def write(self) -> None:
|
||||||
tokens = md.parse(self.read_body())
|
tokens = md.parse(self.read_body())
|
||||||
|
self.resolve_references(tokens)
|
||||||
if self.summary is None:
|
if self.summary is None:
|
||||||
self.summary = md.render_summary(tokens)
|
self.summary = md.render_summary(tokens)
|
||||||
content = md.render(tokens)
|
content = md.render(tokens)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import re
|
import re
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
from omicron.ssg.output.output import Output, OutputError, is_normalized
|
from omicron.ssg.output.output import Output, OutputError, is_normalized
|
||||||
|
from omicron.ssg.output.file import File
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from datetime import date
|
from datetime import date
|
||||||
|
|
||||||
@@ -49,6 +50,29 @@ class Content(Output):
|
|||||||
path = Path(f"{section}/{slug}/index.html")
|
path = Path(f"{section}/{slug}/index.html")
|
||||||
return path
|
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:
|
def read_body(self) -> str:
|
||||||
with self.source.open("r", encoding="utf-8") as f:
|
with self.source.open("r", encoding="utf-8") as f:
|
||||||
f.readline() # opening ---
|
f.readline() # opening ---
|
||||||
|
|||||||
+1
-1
@@ -32,7 +32,7 @@ class ConfigError(RuntimeError):
|
|||||||
|
|
||||||
class Site:
|
class Site:
|
||||||
def __init__(self, site: Path):
|
def __init__(self, site: Path):
|
||||||
self.site_path = site.absolute()
|
self.site_path = site.resolve()
|
||||||
self.content: list[Content] = []
|
self.content: list[Content] = []
|
||||||
self.directories: list[Directory] = []
|
self.directories: list[Directory] = []
|
||||||
self.other_outputs: list[Output] = []
|
self.other_outputs: list[Output] = []
|
||||||
|
|||||||
Reference in New Issue
Block a user