fix: wiki links now root-relative — handles Tolaria [[docs/path/to/page]] format
Some checks failed
Deploy Docs / deploy (push) Failing after 23s
Some checks failed
Deploy Docs / deploy (push) Failing after 23s
Strips docs/ prefix from Tolaria-generated links, collapses /index suffixes, removes .md extensions, and produces root-relative URLs (/page/path/) that work from any page regardless of depth. Images get root-relative asset paths.
This commit is contained in:
parent
fa0b34c3c2
commit
a1db08f32e
2 changed files with 59 additions and 19 deletions
|
|
@ -3,15 +3,19 @@ Python-Markdown extension: converts [[Wikilinks]] to standard Markdown links.
|
||||||
|
|
||||||
Mirrors the behavior of mkdocs-ezlinks-plugin's WikiLinkScanner, but as a
|
Mirrors the behavior of mkdocs-ezlinks-plugin's WikiLinkScanner, but as a
|
||||||
pure Markdown preprocessor (no MkDocs plugin API needed). Zensical handles
|
pure Markdown preprocessor (no MkDocs plugin API needed). Zensical handles
|
||||||
the resulting .md links natively.
|
the resulting links natively.
|
||||||
|
|
||||||
Syntax:
|
Syntax:
|
||||||
[[Page Name]] → [Page Name](page-name.md)
|
[[Page Name]] → [Page Name](/page-name/)
|
||||||
[[Page Name|Display Text]] → [Display Text](page-name.md)
|
[[Page Name|Display Text]] → [Display Text](/page-name/)
|
||||||
[[Page Name#anchor]] → [Page Name](page-name.md#anchor)
|
[[Page Name#anchor]] → [Page Name](/page-name/#anchor)
|
||||||
[[Page Name#anchor|Text]] → [Text](page-name.md#anchor)
|
[[Page Name#anchor|Text]] → [Text](/page-name/#anchor)
|
||||||
![[image.png]] → 
|
![[image.png]] → 
|
||||||
[[folder/Page Name]] → [Page Name](folder/page-name.md)
|
|
||||||
|
Tolaria-compatible: when the vault root is the project root, links like
|
||||||
|
``[[docs/contributing/index]]`` are stripped of the ``docs/`` prefix and
|
||||||
|
converted to root-relative URLs (e.g. ``/contributing/``) that work from
|
||||||
|
any page regardless of depth.
|
||||||
|
|
||||||
Code blocks (fenced and inline) are skipped — wiki links inside them pass
|
Code blocks (fenced and inline) are skipped — wiki links inside them pass
|
||||||
through unchanged.
|
through unchanged.
|
||||||
|
|
@ -44,6 +48,10 @@ _CODE_RE = re.compile(
|
||||||
re.DOTALL,
|
re.DOTALL,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Prefix to strip from Tolaria-generated links when the vault root is
|
||||||
|
# the project root (so links look like [[docs/foo/bar]]).
|
||||||
|
_DOCS_PREFIX = "docs/"
|
||||||
|
|
||||||
|
|
||||||
def _slugify(text: str) -> str:
|
def _slugify(text: str) -> str:
|
||||||
"""Convert a human page name into a filename slug.
|
"""Convert a human page name into a filename slug.
|
||||||
|
|
@ -58,8 +66,35 @@ def _slugify(text: str) -> str:
|
||||||
return slug
|
return slug
|
||||||
|
|
||||||
|
|
||||||
|
def _url_from_link(link: str) -> str:
|
||||||
|
"""Convert a wiki link target to a root-relative URL.
|
||||||
|
|
||||||
|
Handles Tolaria-style full paths: strips ``docs/`` prefix,
|
||||||
|
removes ``.md`` extension, collapses ``/index`` suffixes,
|
||||||
|
slugifies each component, and prepends ``/``.
|
||||||
|
"""
|
||||||
|
raw = link.strip()
|
||||||
|
|
||||||
|
# Strip .md extension
|
||||||
|
raw = re.sub(r"\.md$", "", raw, flags=re.IGNORECASE)
|
||||||
|
|
||||||
|
# Strip docs/ prefix (Tolaria vault root = project root)
|
||||||
|
if raw.lower().startswith(_DOCS_PREFIX):
|
||||||
|
raw = raw[len(_DOCS_PREFIX) :]
|
||||||
|
|
||||||
|
# Collapse trailing /index — section index pages resolve to the dir
|
||||||
|
raw = re.sub(r"/index$", "", raw)
|
||||||
|
|
||||||
|
if not raw:
|
||||||
|
return "/"
|
||||||
|
|
||||||
|
# Slugify each path component
|
||||||
|
parts = [_slugify(p) for p in raw.split("/") if p]
|
||||||
|
return "/" + "/".join(parts) + "/"
|
||||||
|
|
||||||
|
|
||||||
class WikilinksPreprocessor(Preprocessor):
|
class WikilinksPreprocessor(Preprocessor):
|
||||||
"""Preprocessor that converts [[wikilinks]] to [text](slug.md)."""
|
"""Preprocessor that converts [[wikilinks]] to root-relative [text](/url/)."""
|
||||||
|
|
||||||
def run(self, lines: list[str]) -> list[str]:
|
def run(self, lines: list[str]) -> list[str]:
|
||||||
text = "\n".join(lines)
|
text = "\n".join(lines)
|
||||||
|
|
@ -69,15 +104,12 @@ class WikilinksPreprocessor(Preprocessor):
|
||||||
result: list[str] = []
|
result: list[str] = []
|
||||||
cursor = 0
|
cursor = 0
|
||||||
for m in _CODE_RE.finditer(text):
|
for m in _CODE_RE.finditer(text):
|
||||||
# Transform the non-code region before this match
|
|
||||||
before = text[cursor : m.start()]
|
before = text[cursor : m.start()]
|
||||||
if before:
|
if before:
|
||||||
result.append(_WIKILINK_RE.sub(self._replace, before))
|
result.append(_WIKILINK_RE.sub(self._replace, before))
|
||||||
# Pass through the code region unchanged
|
|
||||||
result.append(m.group(0))
|
result.append(m.group(0))
|
||||||
cursor = m.end()
|
cursor = m.end()
|
||||||
|
|
||||||
# Transform any remaining text after the last code region
|
|
||||||
tail = text[cursor:]
|
tail = text[cursor:]
|
||||||
if tail:
|
if tail:
|
||||||
result.append(_WIKILINK_RE.sub(self._replace, tail))
|
result.append(_WIKILINK_RE.sub(self._replace, tail))
|
||||||
|
|
@ -94,17 +126,25 @@ class WikilinksPreprocessor(Preprocessor):
|
||||||
if not (link or text or anchor):
|
if not (link or text or anchor):
|
||||||
return match.group(0)
|
return match.group(0)
|
||||||
|
|
||||||
slug = _slugify(link) if link else ""
|
# Build the href: root-relative URL for pages; relative for images
|
||||||
href = slug
|
if is_image:
|
||||||
|
# Images: keep filename, strip docs/ prefix, make root-relative
|
||||||
|
raw = link.strip()
|
||||||
|
if raw.lower().startswith(_DOCS_PREFIX):
|
||||||
|
raw = raw[len(_DOCS_PREFIX) :]
|
||||||
|
# Non-image assets might have extensions; keep them
|
||||||
|
href = raw if "." in raw else _slugify(raw)
|
||||||
|
if not href.startswith("/"):
|
||||||
|
href = "/" + href
|
||||||
|
else:
|
||||||
|
href = _url_from_link(link)
|
||||||
|
|
||||||
|
# Append anchor if present
|
||||||
if anchor:
|
if anchor:
|
||||||
anchor_slug = _slugify(anchor)
|
anchor_slug = _slugify(anchor)
|
||||||
if anchor_slug:
|
if anchor_slug:
|
||||||
href = f"{slug}#{anchor_slug}" if slug else f"#{anchor_slug}"
|
href = href.rstrip("/") + "#" + anchor_slug
|
||||||
|
|
||||||
if not href:
|
|
||||||
return match.group(0)
|
|
||||||
|
|
||||||
# Build standard markdown link / image
|
|
||||||
if is_image:
|
if is_image:
|
||||||
return f""
|
return f""
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ icon: simple/markdown
|
||||||
|
|
||||||
## Introduction to Markdown Language
|
## Introduction to Markdown Language
|
||||||
|
|
||||||
Markdown is a simple language to build web pages. It is easier to read and write than HTML (the language web browsers read) and so we use it to write articles. You can use this reference to help you write posts and pages directly in our repository where the source documents for this wiki are held. See [[Contributing]] for more.
|
Markdown is a simple language to build web pages. It is easier to read and write than HTML (the language web browsers read) and so we use it to write articles. You can use this reference to help you write posts and pages directly in our repository where the source documents for this wiki are held. See [[docs/contributing/index]] for more.
|
||||||
|
|
||||||
## Headers
|
## Headers
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue