Fix rewrite_html_links for sites whose URL isn't a bare {slug}.html

It derived the slug to match against by stripping ".html" off a
hardcoded link's basename, which only equals the real slug on a site
whose ARTICLE_URL/PAGE_URL *is* "{slug}.html" -- true for randogoth.com,
where this was first written and verified, false for sublunar.space's
ARTICLE_URL ("{date:%Y}-{date:%m}-{slug}.html": "chaxiraxi"'s real link
target is "2018-06-chaxiraxi.html", whose basename-minus-extension is
"2018-06-chaxiraxi", never equal to the slug "chaxiraxi"). Every such
article-to-article link passed smolnet's own "no match" branch and
reached the capsule unrewritten, pointing at a path itsybitsy has no
document for.

Matches against each document's own rendered .url instead, which is
correct for any ARTICLE_URL/PAGE_URL shape rather than one specific
one. Verified against sublunar.space's full content: the reported
broken link and 160+ others like it across the corpus now resolve;
every remaining unrewritten .html link left in the capsule traces to a
target genuinely absent from it (a tag-index page, a draft, a hidden
page) or to a pre-existing dead link already broken on the live HTML
site, unrelated to this fix.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
randogoth 2026-10-10 15:39:26 +03:00
parent bfb54d3ffa
commit e025f32f61

View file

@ -187,20 +187,30 @@ def rewrite_internal(text, by_source, warn):
return INTERNAL.sub(sub, text)
def rewrite_html_links(text, slugs, warn):
def rewrite_html_links(text, urls, warn):
"""Point links written against the HTML build at the capsule instead.
Matched against each document's own rendered URL (Pelican's ARTICLE_URL /
PAGE_URL settings resolved per document), not by stripping ".html" off the
link's basename and hoping that equals the slug. That shortcut only holds
for a site whose URL *is* a bare "{slug}.html" -- true for randogoth.com,
where this was first written, false for a site like sublunar.space whose
ARTICLE_URL prefixes the date ("2018-06-chaxiraxi.html" for slug
"chaxiraxi"). There the stripped string never matched any slug, and every
article-to-article link passed through unrewritten, pointing the capsule
at a path itsybitsy has no document for.
`/pages/glossary.html` and `/go-west.html` both become `/<slug>`, since the
capsule is flat. A target whose slug is not among the documents built is
capsule is flat. A target whose rendered URL matches no document built is
reported and left alone rather than silently rewritten into a second broken
link.
"""
def sub(match):
target, rest = match.group(1), match.group(2)
slug = os.path.basename(target)[: -len(".html")]
if slug in slugs:
return "](/%s%s)" % (slug, rest)
key = target.lstrip("/")
if key in urls:
return "](/%s%s)" % (urls[key], rest)
warn("smolnet: link to %s matches no document", target)
return match.group(0)
@ -391,10 +401,10 @@ class CapsuleGenerator(Generator):
# Map source basename -> slug, so {filename} can be resolved. Built from
# everything published, pages included, since an article may link one.
by_source = {}
slugs = set()
urls = {}
for doc in articles + pages:
by_source[os.path.basename(doc.source_path)] = doc.slug
slugs.add(doc.slug)
urls[doc.url] = doc.slug
os.makedirs(out, exist_ok=True)
root_titles = {}
@ -403,7 +413,7 @@ class CapsuleGenerator(Generator):
body = strip_metadata(self._read(doc))
body = body.replace(SUMMARY_MARKER, "")
body = rewrite_internal(body, by_source, self._warn)
body = rewrite_html_links(body, slugs, self._warn)
body = rewrite_html_links(body, urls, self._warn)
body = reveal_rot13(body, rot_attr)
body = drop_dead_images(body, dead_hosts)
if footnotes: