#!/usr/bin/env python3
"""Convert markdown files in spec directories to HTML.
Uses cmark-gfm for full GitHub Flavored Markdown rendering, then
post-processes the output to convert GFM alert syntax (> [!NOTE], etc.)
into styled admonition blocks.
"""
import glob
import os
import re
import subprocess
ADMONITION_TYPES = {"NOTE", "TIP", "IMPORTANT", "WARNING", "CAUTION"}
TEMPLATE = """\
{title}
{body}
"""
# cmark-gfm renders > [!NOTE]\n> text as:
# \n[!NOTE]\ntext
\n
# We convert these to styled admonition divs.
ADMONITION_RE = re.compile(
r"\n\[!(NOTE|TIP|IMPORTANT|WARNING|CAUTION)\]\n"
r"(.*?)
\n
",
re.DOTALL,
)
def convert_admonitions(html):
def replace(m):
kind = m.group(1).lower()
body = m.group(2)
title = m.group(1).capitalize()
return (
f'\n'
f'
{title}
\n'
f"
{body}
\n"
f"
"
)
return ADMONITION_RE.sub(replace, html)
def extract_title(text):
m = re.search(r"^#\s+(.+)", text, re.MULTILINE)
return m.group(1).strip() if m else "Untitled"
def render_markdown(text):
proc = subprocess.run(
["cmark-gfm", "--extension", "table", "--extension", "autolink",
"--extension", "strikethrough", "--extension", "tasklist"],
input=text, capture_output=True, encoding="utf-8",
)
if proc.returncode != 0:
raise RuntimeError(f"cmark-gfm failed: {proc.stderr}")
return convert_admonitions(proc.stdout)
def main():
for md_file in sorted(glob.glob("*/*.md")):
if md_file.startswith("."):
continue
html_file = os.path.splitext(md_file)[0] + ".html"
if os.path.exists(html_file):
continue
with open(md_file, encoding="utf-8") as f:
text = f.read()
title = extract_title(text)
body = render_markdown(text)
with open(html_file, "w", encoding="utf-8") as f:
f.write(TEMPLATE.format(title=title, body=body))
print(f" {html_file}")
if __name__ == "__main__":
main()