fix?
This commit is contained in:
1 parent
d4221f1455
commit
0703c7a1fc
4 files changed
+132
-15
No files matched your search
@@ -22,3 +22,4 @@ secrets/*
|
|||||||
!secrets/README.md
|
!secrets/README.md
|
||||||
scripts/twitter/__pycache__
|
scripts/twitter/__pycache__
|
||||||
scripts/twitter/tests/__pycache__
|
scripts/twitter/tests/__pycache__
|
||||||
|
publish/quartz-patches/__pycache__
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
# Threadwell\
|
# Threadwell
|
||||||
|
|
||||||
Recovered threads and submerged records.
|
Recovered threads and submerged records.
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
"""Single source of truth for the Threadwell site base.
|
"""Keep in-site links under the /Threadwell/ path.
|
||||||
|
|
||||||
Two stages:
|
Two stages:
|
||||||
|
|
||||||
@@ -6,13 +6,11 @@ Two stages:
|
|||||||
relative URLs in the SPA click handler resolve from a directory base.
|
relative URLs in the SPA click handler resolve from a directory base.
|
||||||
|
|
||||||
2. After `npx quartz build`, rewrite every built HTML file:
|
2. After `npx quartz build`, rewrite every built HTML file:
|
||||||
- add `<base href="<abs>">` so relative URL resolution works even when
|
- set `<base href>` to that page's directory under the site path so
|
||||||
the URL bar lacks a trailing slash
|
Quartz page-relative `../` links do not resolve against the domain root
|
||||||
- rewrite the sidebar `<a class="page-title">` href to the absolute site
|
- rewrite the sidebar `<a class="page-title">` href to the site path
|
||||||
base, not the directory the page is in
|
|
||||||
|
|
||||||
The base URL is read from `publish/quartz.config.yaml`'s `baseUrl`. Change
|
`baseUrl` in `publish/quartz.config.yaml` is the single source of truth.
|
||||||
that line; the next deploy uses the new value everywhere.
|
|
||||||
|
|
||||||
Idempotent. Safe to re-run.
|
Idempotent. Safe to re-run.
|
||||||
"""
|
"""
|
||||||
@@ -64,7 +62,9 @@ def read_base_url() -> tuple[str, str]:
|
|||||||
|
|
||||||
ABS_BASE, REL_BASE = read_base_url()
|
ABS_BASE, REL_BASE = read_base_url()
|
||||||
HEAD_RE: re.Pattern[str] = re.compile(r"(<head[^>]*>)", re.IGNORECASE)
|
HEAD_RE: re.Pattern[str] = re.compile(r"(<head[^>]*>)", re.IGNORECASE)
|
||||||
BASE_TAG = f'<base href="{ABS_BASE}">\n'
|
BASE_RE: re.Pattern[str] = re.compile(
|
||||||
|
r"<base\s+href=\"[^\"]*\"\s*/?>\s*", re.IGNORECASE
|
||||||
|
)
|
||||||
TITLE_LINK: re.Pattern[str] = re.compile(
|
TITLE_LINK: re.Pattern[str] = re.compile(
|
||||||
r'(<h2[^>]*class="page-title"><a\s+href=")[^"]*(">)'
|
r'(<h2[^>]*class="page-title"><a\s+href=")[^"]*(">)'
|
||||||
)
|
)
|
||||||
@@ -131,18 +131,38 @@ def patch_spa() -> bool:
|
|||||||
# --- post-build HTML rewrites ----------------------------------------------
|
# --- post-build HTML rewrites ----------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
def page_dir_href(html_path: Path, public: Path, rel_base: str) -> str:
|
||||||
|
"""Return the path-absolute directory of a built HTML file.
|
||||||
|
|
||||||
|
`public/archive/threads/h/slug/index.html` with rel_base `/Threadwell/`
|
||||||
|
becomes `/Threadwell/archive/threads/h/slug/`.
|
||||||
|
"""
|
||||||
|
rel = html_path.relative_to(public).as_posix()
|
||||||
|
if rel.endswith("/index.html"):
|
||||||
|
dir_rel = rel[: -len("index.html")]
|
||||||
|
elif rel == "index.html":
|
||||||
|
dir_rel = ""
|
||||||
|
else:
|
||||||
|
cut = rel.rfind("/")
|
||||||
|
dir_rel = rel[: cut + 1] if cut >= 0 else ""
|
||||||
|
return f"{rel_base.rstrip('/')}/{dir_rel}"
|
||||||
|
|
||||||
|
|
||||||
def rewrite_html(path: Path) -> str | None:
|
def rewrite_html(path: Path) -> str | None:
|
||||||
"""Return 'base' / 'title' / None for what changed."""
|
"""Return 'base' / 'title' / None for what changed."""
|
||||||
text = path.read_text(encoding="utf-8", errors="ignore")
|
text = path.read_text(encoding="utf-8", errors="ignore")
|
||||||
out = text
|
out = text
|
||||||
actions = []
|
actions: list[str] = []
|
||||||
|
expected_base = f'<base href="{page_dir_href(path, PUBLIC, REL_BASE)}">'
|
||||||
|
|
||||||
m = HEAD_RE.search(out)
|
if out.count("<base ") != 1 or expected_base not in out:
|
||||||
if m and "quartz-base" not in out:
|
out = BASE_RE.sub("", out)
|
||||||
out = out[: m.end()] + "\n" + BASE_TAG + out[m.end():]
|
m = HEAD_RE.search(out)
|
||||||
actions.append("base")
|
if m is not None:
|
||||||
|
out = out[: m.end()] + "\n" + expected_base + "\n" + out[m.end() :].lstrip()
|
||||||
|
actions.append("base")
|
||||||
if 'class="page-title"' in out:
|
if 'class="page-title"' in out:
|
||||||
out2 = TITLE_LINK.sub(rf"\g<1>{ABS_BASE}\g<2>", out)
|
out2 = TITLE_LINK.sub(rf"\g<1>{REL_BASE}\g<2>", out)
|
||||||
if out2 != out:
|
if out2 != out:
|
||||||
out = out2
|
out = out2
|
||||||
actions.append("title")
|
actions.append("title")
|
||||||
|
|||||||
@@ -0,0 +1,96 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import re
|
||||||
|
import tempfile
|
||||||
|
import unittest
|
||||||
|
from pathlib import Path
|
||||||
|
from urllib.parse import urljoin
|
||||||
|
|
||||||
|
import apply
|
||||||
|
|
||||||
|
|
||||||
|
DEEP_REL: str = "../../../../archive/threads/notimothylottes/"
|
||||||
|
DEEP_SLUG: str = (
|
||||||
|
"archive/threads/notimothylottes/2025-02-04-gelatinous-pixel-soup/index.html"
|
||||||
|
)
|
||||||
|
PAGE_BASE: str = (
|
||||||
|
"/Threadwell/archive/threads/notimothylottes/2025-02-04-gelatinous-pixel-soup/"
|
||||||
|
)
|
||||||
|
SAMPLE: str = (
|
||||||
|
"<html><head></head><body>"
|
||||||
|
'<h2 class="page-title"><a href="./">Threadwell</a></h2>'
|
||||||
|
f'<a class="internal" href="{DEEP_REL}">NOTimothyLottes</a>'
|
||||||
|
"</body></html>"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _write_public(root: Path, rel: str, html: str) -> Path:
|
||||||
|
path = root / rel
|
||||||
|
path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
path.write_text(html, encoding="utf-8")
|
||||||
|
return path
|
||||||
|
|
||||||
|
|
||||||
|
class ApplyBaseTests(unittest.TestCase):
|
||||||
|
def setUp(self) -> None:
|
||||||
|
self._public = apply.PUBLIC
|
||||||
|
self._tmp = tempfile.TemporaryDirectory()
|
||||||
|
self.public = Path(self._tmp.name)
|
||||||
|
apply.PUBLIC = self.public
|
||||||
|
|
||||||
|
def tearDown(self) -> None:
|
||||||
|
apply.PUBLIC = self._public
|
||||||
|
self._tmp.cleanup()
|
||||||
|
|
||||||
|
def test_deep_page_base_is_page_directory(self) -> None:
|
||||||
|
path = _write_public(self.public, DEEP_SLUG, SAMPLE)
|
||||||
|
apply.rewrite_html(path)
|
||||||
|
text = path.read_text(encoding="utf-8")
|
||||||
|
self.assertIn(f'<base href="{PAGE_BASE}">', text)
|
||||||
|
self.assertNotIn('<base href="https://edsabode.dev/Threadwell/">', text)
|
||||||
|
self.assertNotIn('<base href="/Threadwell/">', text)
|
||||||
|
|
||||||
|
def test_deep_page_relative_link_keeps_threadwell_prefix(self) -> None:
|
||||||
|
path = _write_public(self.public, DEEP_SLUG, SAMPLE)
|
||||||
|
apply.rewrite_html(path)
|
||||||
|
text = path.read_text(encoding="utf-8")
|
||||||
|
match = re.search(r'<base href="([^"]+)">', text)
|
||||||
|
self.assertIsNotNone(match)
|
||||||
|
assert match is not None
|
||||||
|
resolved = urljoin("https://edsabode.dev" + match.group(1), DEEP_REL)
|
||||||
|
self.assertEqual(
|
||||||
|
resolved,
|
||||||
|
"https://edsabode.dev/Threadwell/archive/threads/notimothylottes/",
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_homepage_base_is_site_path(self) -> None:
|
||||||
|
path = _write_public(self.public, "index.html", SAMPLE)
|
||||||
|
apply.rewrite_html(path)
|
||||||
|
text = path.read_text(encoding="utf-8")
|
||||||
|
self.assertIn('<base href="/Threadwell/">', text)
|
||||||
|
|
||||||
|
def test_title_link_is_site_path_not_domain_root(self) -> None:
|
||||||
|
path = _write_public(self.public, DEEP_SLUG, SAMPLE)
|
||||||
|
apply.rewrite_html(path)
|
||||||
|
text = path.read_text(encoding="utf-8")
|
||||||
|
self.assertIn(
|
||||||
|
'<h2 class="page-title"><a href="/Threadwell/">',
|
||||||
|
text,
|
||||||
|
)
|
||||||
|
self.assertNotIn('href="https://edsabode.dev/Threadwell/"', text)
|
||||||
|
|
||||||
|
def test_rewrite_replaces_site_root_base_and_stays_idempotent(self) -> None:
|
||||||
|
html = SAMPLE.replace(
|
||||||
|
"<head></head>",
|
||||||
|
'<head><base href="https://edsabode.dev/Threadwell/"></head>',
|
||||||
|
)
|
||||||
|
path = _write_public(self.public, DEEP_SLUG, html)
|
||||||
|
apply.rewrite_html(path)
|
||||||
|
apply.rewrite_html(path)
|
||||||
|
text = path.read_text(encoding="utf-8")
|
||||||
|
self.assertEqual(text.count("<base "), 1)
|
||||||
|
self.assertIn(f'<base href="{PAGE_BASE}">', text)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
Reference in new issue
Block a user