Skip to content

Commit f7290a1

Browse files
committed
fix(docs-gen): escape bare < in generated API markdown
docfx emits two C#-generic patterns whose opening `<` is bare while the closing `>` is markdown-escaped as `\>`: 1. heading text after the anchor close, e.g. `### <a id="…"></a> CollectShell(string, List<EnvVarInfo\>)` 2. return / parameter blocks where docfx renders an outer-generic link followed by an inner-generic link, e.g. ` [List](…)<[EnvVarInfo](…)\>` VitePress's underlying Vue template compiler sees the bare `<` as the start tag of an element that never closes (the escaped `\>` is rendered as a literal `>` rather than an end-tag token), tripping "Element is missing end tag" during `vitepress build` and failing the Docs-site CI's Build VitePress site job. Add a post-processing awk pass to generate-api-ref.sh that rewrites `<[A-Z]` and `<[` outside fenced code blocks to `&lt;[A-Z]` / `&lt;[`. The `&lt;` HTML entity survives the markdown -> HTML pipeline, whereas a backslash-escape would decode back to `<` and re-trip the same error. The awk filter tracks ```-fence state so signatures in code blocks (e.g. `public IReadOnlyList<EnvVarInfo> Collect(...)`) stay untouched, and the lowercase `<a id="…"></a>` anchor itself is unaffected because the pattern requires either an uppercase letter or `[`.
1 parent f3e44cc commit f7290a1

1 file changed

Lines changed: 44 additions & 0 deletions

File tree

docs/scripts/generate-api-ref.sh

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,4 +149,48 @@ find docs/api -mindepth 1 -maxdepth 1 -not -name 'index.md' -exec rm -rf {} +
149149
find docs/api -name '*.md' -not -name 'index.md' -print0 \
150150
| xargs -0 sed -i -E 's#<code( [^>]*)?>#<code v-pre\1>#g'
151151

152+
# docfx emits two C#-generic patterns whose opening `<` is bare while
153+
# the closing `>` is markdown-escaped as `\>`. Vue's template compiler
154+
# then treats the bare `<` as the start tag of an element that never
155+
# closes (the escaped `\>` is rendered as a literal `>` in HTML, not
156+
# as an end-tag token), tripping "Element is missing end tag" at
157+
# vitepress build time. The two patterns are:
158+
#
159+
# 1. Heading text after the anchor close, e.g.
160+
# `### <a id="…"></a> Collect\(string, List<EnvVarInfo\>\)`.
161+
# 2. Return / parameter blocks where docfx renders an outer-generic
162+
# link followed by an inner-generic link, e.g.
163+
# ` [List](url-of-list)<[EnvVarInfo](url-of-envvarinfo)\>`.
164+
#
165+
# Both forms feed the parser a `<` followed by either an uppercase
166+
# letter (heading-text type name) or a `[` (markdown link opener) and
167+
# never close it with a real `</tag>`. Rewrite both bare opens as
168+
# `&lt;` HTML entities so the markdown -> HTML pipeline can't re-emit
169+
# a literal `<` (a backslash-escape would survive markdown but the
170+
# Vue template parser sees the rendered HTML, where `\<` decodes to
171+
# `<` again and re-trips the same error). The `<a id="…"></a>` anchor
172+
# itself is intentionally not touched -- `a` is lowercase, so the
173+
# `<[A-Z]` alternative misses it, and the `<\[` alternative is
174+
# unambiguous. Fenced code blocks render via the highlighter, not
175+
# Vue's template parser, so the bare generics they contain (e.g.
176+
# `public IReadOnlyList<EnvVarInfo> Collect(...)`) do not need
177+
# escaping. A small awk pass tracks ```-fence state and skips lines
178+
# inside fences so code-block content stays untouched.
179+
while IFS= read -r -d '' md_file; do
180+
awk '
181+
BEGIN { in_fence = 0 }
182+
/^```/ { in_fence = !in_fence; print; next }
183+
in_fence { print; next }
184+
{
185+
# Replace `<X` (uppercase) with `&lt;X` by walking each match.
186+
out = ""; s = $0
187+
while (match(s, /<([A-Z]|\[)/)) {
188+
out = out substr(s, 1, RSTART - 1) "&lt;" substr(s, RSTART + 1, RLENGTH - 1)
189+
s = substr(s, RSTART + RLENGTH)
190+
}
191+
print out s
192+
}
193+
' "${md_file}" > "${md_file}.tmp" && mv "${md_file}.tmp" "${md_file}"
194+
done < <(find docs/api -name '*.md' -not -name 'index.md' -print0)
195+
152196
echo "==> done. $(find docs/api -name '*.md' -not -name 'index.md' | wc -l) pages generated."

0 commit comments

Comments
 (0)