From 8be90fcee595222daac055050ab206887f145a62 Mon Sep 17 00:00:00 2001 From: Nan Yu Date: Tue, 27 Jan 2026 23:56:02 +0000 Subject: [PATCH] docs: simplify convert_docs.py to uni-directional conversion The bi-directional support was initially implemented to migrate existing *.md files from MkDocs admonitions to GitHub-flavored markdown. With the migration complete, the reverse transformation is no longer needed. A uni-directional flow (GitHub -> MkDocs) is cleaner and sufficient for the build pipeline. It also adds a README.md file to explain what the scripts do. --- .github/workflows/docs.yml | 2 +- docs/scripts/README.md | 39 ++++++++++++++ docs/scripts/convert_docs.py | 47 +++-------------- docs/scripts/test_convert_docs.py | 86 ++++++++++++------------------- 4 files changed, 80 insertions(+), 94 deletions(-) create mode 100644 docs/scripts/README.md diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 8ca88f3dd..4a559b74f 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -73,7 +73,7 @@ jobs: run: pip install pytest && pytest docs/scripts/test_convert_docs.py - name: Convert Admonitions in Documentation - run: python docs/scripts/convert_docs.py --mode github-to-mkdocs + run: python docs/scripts/convert_docs.py - name: Build Documentation (PR Check) if: github.event_name == 'pull_request' diff --git a/docs/scripts/README.md b/docs/scripts/README.md new file mode 100644 index 000000000..5ff9ff1e8 --- /dev/null +++ b/docs/scripts/README.md @@ -0,0 +1,39 @@ +# Documentation Transformation Scripts + +This directory contains utility scripts to prepare our documentation for the **MkDocs** build process. + +## Purpose + +To ensure a great reading experience both on GitHub and the hosted site, we use **GitHub-flavored Markdown** as our primary source of truth. This script transforms GitHub's native syntax into **MkDocs-compatible syntax** (specifically for the `pymdown-extensions`) during the build pipeline. + +## Supported Conversions (Uni-directional) + +The script performs a uni-directional transformation: **GitHub Markdown → MkDocs Syntax**. + +### Alert/Admonition Conversion + +- GitHub uses a blockquote-based syntax for alerts. +- MkDocs requires the `!!!` or `???` syntax to render colored callout boxes. + +## Running the Conversion + +The conversion is run as part of the build pipeline. No additional steps are required. If you need to run the conversion manually, you can run the `convert_docs.py` script in the repository root. + +```bash +python docs/scripts/convert_docs.py +``` + +### Example + +- **Source (GitHub-flavored Markdown):** + ```markdown + > âš ī¸ **Attention** + > + > This is an alert. + ``` + +- **Target (MkDocs Syntax):** + ```markdown + !!! warning "Attention" + This is an alert. + ``` diff --git a/docs/scripts/convert_docs.py b/docs/scripts/convert_docs.py index d117521c9..df0d9cf9d 100644 --- a/docs/scripts/convert_docs.py +++ b/docs/scripts/convert_docs.py @@ -88,57 +88,24 @@ def alert_replacer(match): content = re.sub(GITHUB_ALERT_PATTERN, alert_replacer, content, flags=re.MULTILINE) return content -def to_github(content): - """Converts MkDocs style to GitHub style.""" - - def mkdocs_replacer(match): - adm_type, title, body = match.groups() - # Safely retrieve the emoji, default to 'note' (📝) for unknown/unmapped types - emoji = MAPPING.get(adm_type, {"emoji": "📝"})["emoji"] - - # Strip trailing whitespace from captured body to prevent hanging '>' - clean_body = body.rstrip() - raw_lines = clean_body.split('\n') - content_lines = [re.sub(r'^\s{4}', '', line).rstrip() for line in raw_lines] - - # Header line logic - github_lines = [f"> {emoji} **{title}**"] if title.strip() else [f"> {emoji}"] - github_lines.append(">") # Spacer line - - for line in content_lines: - github_lines.append(f"> {line}" if line else ">") - - return "\n".join(github_lines) + "\n" - - return re.sub(MKDOCS_PATTERN, mkdocs_replacer, content) - -def process_file(path, mode): - mode_map = { - "github-to-mkdocs": to_mkdocs, - "mkdocs-to-github": to_github - } - if mode not in mode_map: - raise ValueError(f"Unsupported mode: {mode}. Choose from {list(mode_map.keys())}.") - target_func = mode_map[mode] - +def process_file(path): with open(path, 'r', encoding='utf-8') as f: content = f.read() - new_content = target_func(content) + new_content = to_mkdocs(content) if new_content != content: with open(path, 'w', encoding='utf-8') as f: f.write(new_content) - print(f"[{mode.upper()}] Converted: {path}") + print(f"[CONVERTED] {path}") -def run_conversion(mode): +def run_conversion(): for root, dirs, files in os.walk('docs'): if any(x in root for x in ['scripts', 'assets', '__pycache__']): continue for file in files: if file.endswith('.md'): - process_file(os.path.join(root, file), mode) + process_file(os.path.join(root, file)) if __name__ == "__main__": - parser = argparse.ArgumentParser(description="Bidirectional Markdown Admonition Converter") - parser.add_argument("--mode", choices=["github-to-mkdocs", "mkdocs-to-github"], required=True, help="Target format") + parser = argparse.ArgumentParser(description="GitHub to MkDocs Markdown Admonition Converter") args = parser.parse_args() - run_conversion(args.mode) + run_conversion() diff --git a/docs/scripts/test_convert_docs.py b/docs/scripts/test_convert_docs.py index 51b97694f..2cfcd1fb8 100644 --- a/docs/scripts/test_convert_docs.py +++ b/docs/scripts/test_convert_docs.py @@ -1,47 +1,39 @@ import pytest -from convert_docs import to_mkdocs, to_github +from convert_docs import to_mkdocs # --- 1. A2UI Specific Header Cases --- ADMONITION_CASES = [ - ('!!! info "Coming soon..."', "Coming soon..."), - ('!!! warning "Status: Early Stage Public Preview"', "Status: Early Stage Public Preview"), - ('!!! success "Stable Release"', "Stable Release"), - ('!!! note "Version Compatibility"', "Version Compatibility"), - ('!!! warning "Attention"', "Attention"), - ('!!! tip "It\'s Just JSON"', "It's Just JSON"), + ('!!! info "Coming soon..."', "Coming soon...", "â„šī¸"), + ('!!! warning "Status: Early Stage Public Preview"', "Status: Early Stage Public Preview", "âš ī¸"), + ('!!! success "Stable Release"', "Stable Release", "✅"), + ('!!! note "Version Compatibility"', "Version Compatibility", "📝"), + ('!!! warning "Attention"', "Attention", "âš ī¸"), + ('!!! tip "It\'s Just JSON"', "It's Just JSON", "💡"), ] -@pytest.mark.parametrize("header, expected_title", ADMONITION_CASES) -def test_standard_a2ui_round_trip(header, expected_title): - """Verifies that all standard A2UI headers survive a round-trip conversion.""" +@pytest.mark.parametrize("expected_header, title, emoji", ADMONITION_CASES) +def test_standard_a2ui_conversion(expected_header, title, emoji): + """Verifies that GitHub style converts to expected A2UI headers.""" body = " Line 1\n Line 2" - original = f"{header}\n{body}\n" + github_input = f"> {emoji} **{title}**\n>\n> Line 1\n> Line 2\n" - # MkDocs -> GitHub - github = to_github(original) - assert f"**{expected_title}**" in github + expected = f"{expected_header}\n{body}\n" # GitHub -> MkDocs - back = to_mkdocs(github) - assert back.strip() == original.strip() + result = to_mkdocs(github_input) + assert result.strip() == expected.strip() # --- 2. Empty Title Edge Case --- def test_empty_title_case(): """ - Verifies !!! tip "" converts to '> 💡' exactly. - - No trailing spaces - - No bold markers (****) + Verifies '> 💡' converts to !!! tip "". """ - original = '!!! tip ""\n Content.\n' - github = to_github(original) + github_input = "> 💡\n>\n> Content.\n" + expected = '!!! tip ""\n Content.\n' - lines = github.splitlines() - assert lines[0] == "> 💡" # Strictly no space or bold markers - assert lines[1] == ">" # Spacer line - - back = to_mkdocs(github) - assert back == original + result = to_mkdocs(github_input) + assert result == expected # --- 3. Spacing & Internal Paragraph Preservation --- @@ -73,49 +65,37 @@ def test_paragraph_spacing_and_trailing_lines(): assert result == expected -# --- 4. Unmapped/Unknown Type Fallback --- -def test_unknown_type_fallback(): - """ - Verifies that an unknown admonition type defaults to the 'note' emoji (📝). - """ - original = '!!! mystery "Secret"\n Content.\n' - github = to_github(original) - - assert "> 📝 **Secret**" in github - - # Note: Round trip will convert it back to '!!! note' - # because the source type 'mystery' wasn't in the map. - back = to_mkdocs(github) - assert '!!! note "Secret"' in back - - -# --- 5. Multiple Blocks & Isolation --- +# --- 4. Multiple Blocks & Isolation --- def test_multiple_blocks_in_one_file(): """Ensures multiple blocks are processed without bleeding into each other.""" - original = ( + github_input = ( + '> ✅ **Block 1**\n' + '> Content 1\n' + '\n' + '> â„šī¸ **Block 2**\n' + '> Content 2\n' + ) + + expected = ( '!!! success "Block 1"\n' ' Content 1\n' '\n' '!!! info "Block 2"\n' ' Content 2\n' ) - github = to_github(original) - assert "> ✅ **Block 1**" in github - assert "> â„šī¸ **Block 2**" in github - back = to_mkdocs(github) - assert back == original + result = to_mkdocs(github_input) + assert result == expected -# --- 6. False Positive Prevention --- +# --- 5. False Positive Prevention --- def test_regular_blockquote_ignored(): """Ensures regular quotes are not touched.""" source = "> This is just a quote, not an admonition." assert to_mkdocs(source) == source - assert to_github(source) == source -# --- 7. GitHub Official Alert Syntax Support --- +# --- 6. GitHub Official Alert Syntax Support --- def test_github_alert_to_mkdocs(): """Verifies official [!TYPE] syntax conversion.""" source = "> [!WARNING]\n> **Security Notice**\n> Do not share keys."