Skip to content

Commit 89df578

Browse files
committed
Add markdown output to doc build
Add sphinx-markdown-builder to produce .md files alongside HTML output. Agents and LLMs can consume docs at /2.0/markdown/<path>.md without HTML parsing overhead. - Add sphinx-markdown-builder dependency to pyproject.toml - Register sphinx_markdown_builder extension in conf.py - Add md1/md2/markdown/merge-markdown Makefile targets - Update llms.txt generator to point to markdown URLs - Add link tag to landing page for llms.txt discovery - Fix deprecated app.warn in redirects extension for non-HTML builders - Add custom node handlers for tip/danger/caution/admonition directives
1 parent a876699 commit 89df578

6 files changed

Lines changed: 80 additions & 7 deletions

File tree

docs/Makefile

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,21 @@ html1:
1818
html2:
1919
@source build/venv/bin/activate && $(SPHINXBUILD) -M html "source-2.0" "build/2.0" $(SPHINXOPTS) -W --keep-going -n $(O)
2020

21+
md1:
22+
@source build/venv/bin/activate && $(SPHINXBUILD) -M markdown "source-1.0" "build/1.0-md" $(SPHINXOPTS) -W --keep-going -n $(O)
23+
24+
md2:
25+
@source build/venv/bin/activate && $(SPHINXBUILD) -M markdown "source-2.0" "build/2.0-md" $(SPHINXOPTS) -W --keep-going -n $(O)
26+
2127
build-landing-page:
2228
cd landing-page && npm run build
2329

2430
llms-txt: html2
2531
python3 generate_llms_txt.py
2632

27-
html: html2 html1 build-landing-page merge-versions llms-txt
33+
markdown: md2 md1 merge-markdown
34+
35+
html: html2 html1 build-landing-page merge-versions markdown llms-txt
2836

2937
serve:
3038
npx http-server build/html
@@ -36,6 +44,11 @@ merge-versions:
3644
cp -R root/* "build/html"
3745
cp -R landing-page/dist/* "build/html/"
3846

47+
merge-markdown:
48+
mkdir -p build/html/1.0/markdown build/html/2.0/markdown
49+
cp -R "build/1.0-md/markdown/." "build/html/1.0/markdown/"
50+
cp -R "build/2.0-md/markdown/." "build/html/2.0/markdown/"
51+
3952
openhtml:
4053
open "build/html/index.html"
4154

docs/conf.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
"myst_parser",
1414
"sphinx_copybutton",
1515
"sphinx_substitution_extensions",
16+
"sphinx_markdown_builder",
1617
"smithy",
1718
]
1819
templates_path = ["../_templates", "../root"]
@@ -127,6 +128,7 @@ def __load_java_version():
127128
def setup(sphinx):
128129
sphinx.add_lexer("smithy", SmithyLexer)
129130
sphinx.connect("source-read", source_read_handler)
131+
sphinx.connect("builder-inited", _builder_inited_handler)
130132
for placeholder, replacement in replacements:
131133
print("Finding and replacing '" + placeholder + "' with '" + replacement + "'")
132134

@@ -135,3 +137,38 @@ def setup(sphinx):
135137
def source_read_handler(app, docname, source):
136138
for placeholder, replacement in replacements:
137139
source[0] = source[0].replace(placeholder, replacement)
140+
141+
142+
# -- Markdown builder: register missing admonition handlers ----------------
143+
144+
def _patch_markdown_translator(app):
145+
"""Add handlers for admonition types not supported by sphinx-markdown-builder."""
146+
try:
147+
from sphinx_markdown_builder.translator import MarkdownTranslator
148+
except ImportError:
149+
return
150+
151+
def _make_visit(label):
152+
def visit(self, node):
153+
self._push_box(label)
154+
return visit
155+
156+
def _make_depart(self, node):
157+
pass
158+
159+
for node_type, label in [
160+
("tip", "TIP"),
161+
("danger", "DANGER"),
162+
("caution", "CAUTION"),
163+
("admonition", "NOTE"),
164+
]:
165+
visit_name = f"visit_{node_type}"
166+
depart_name = f"depart_{node_type}"
167+
if not hasattr(MarkdownTranslator, visit_name):
168+
setattr(MarkdownTranslator, visit_name, _make_visit(label))
169+
setattr(MarkdownTranslator, depart_name, _make_depart)
170+
171+
172+
def _builder_inited_handler(app):
173+
if app.builder.name == "markdown":
174+
_patch_markdown_translator(app)

docs/generate_llms_txt.py

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import sys
1212

1313
BASE_URL = "https://smithy.io/2.0"
14+
MARKDOWN_BASE_URL = "https://smithy.io/2.0/markdown"
1415
SOURCE_DIR = "source-2.0"
1516

1617
# Sections in display order. Keys are directory prefixes relative to SOURCE_DIR;
@@ -49,10 +50,17 @@ def extract_title(filepath):
4950
def rst_path_to_url(rst_path):
5051
"""Convert a source-relative RST path to a smithy.io URL."""
5152
rel = rst_path.replace(os.sep, "/")
52-
html = rel.removesuffix(".rst") + ".html"
53+
html = rel[:-4] + ".html" # strip .rst, add .html
5354
return f"{BASE_URL}/{html}"
5455

5556

57+
def rst_path_to_md_url(rst_path):
58+
"""Convert a source-relative RST path to a smithy.io markdown URL."""
59+
rel = rst_path.replace(os.sep, "/")
60+
md = rel[:-4] + ".md" # strip .rst, add .md
61+
return f"{MARKDOWN_BASE_URL}/{md}"
62+
63+
5664
def collect_pages(source_dir):
5765
"""Walk source_dir and return {relative_path: title} for all RST files."""
5866
pages = {}
@@ -88,6 +96,11 @@ def generate(source_dir, output_path):
8896
" language. Smithy models define a service as a collection of resources,"
8997
" operations, and shapes.",
9098
"",
99+
"## Content Formats",
100+
"",
101+
"Each page is available in HTML and Markdown. For AI/LLM consumption,",
102+
"use the Markdown URLs listed below (more token-efficient, no HTML parsing).",
103+
"",
91104
]
92105

93106
for prefix, heading in SECTIONS:
@@ -103,8 +116,8 @@ def generate(source_dir, output_path):
103116

104117
for rel_path in sorted(section_pages):
105118
title = section_pages[rel_path]
106-
url = rst_path_to_url(rel_path)
107-
lines.append(f"- [{title}]({url})")
119+
md_url = rst_path_to_md_url(rel_path)
120+
lines.append(f"- [{title}]({md_url})")
108121

109122
lines.append("")
110123

docs/landing-page/index.html

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,12 @@
3131
/>
3232

3333
<title>Smithy</title>
34+
<link
35+
rel="alternate"
36+
type="text/plain"
37+
href="/2.0/llms.txt"
38+
title="LLM-readable documentation"
39+
/>
3440
</head>
3541
<body class="dark">
3642
<div id="root"></div>

docs/pyproject.toml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,10 @@ dependencies = [
2424
"standard-imghdr==3.13.0",
2525

2626
# Allows writing docs in markdown
27-
"myst-parser==5.0.0"
27+
"myst-parser==5.0.0",
28+
29+
# Allows building docs as markdown output
30+
"sphinx-markdown-builder==0.6.10"
2831
]
2932

3033
[project.entry-points."pygments.lexers"]

docs/smithy/redirects.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,9 @@ def generate_redirects(app):
4040
return
4141

4242
if not (type(app.builder) == StandaloneHTMLBuilder or type(app.builder) == DirectoryHTMLBuilder):
43-
app.warn("The 'sphinxcontib-redirects' plugin is only supported "
44-
"by the 'html' and 'dirhtml' builder, but you are using '%s'. Skipping..." % type(app.builder))
43+
LOGGER.info("The 'sphinxcontib-redirects' plugin is only supported "
44+
"by the 'html' and 'dirhtml' builder, but you are using '%s'. Skipping..." % type(app.builder))
45+
return
4546

4647
dirhtml = False
4748
if type(app.builder) == DirectoryHTMLBuilder:

0 commit comments

Comments
 (0)