Skip to content

Commit e295194

Browse files
committed
fix(docs-gen): escape bare < in generated API heading lines
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 escapes `<[A-Z]` and `<[` outside fenced code blocks, so the Vue parser treats the type-parameter brackets as literal text. 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 e295194

1 file changed

Lines changed: 33 additions & 0 deletions

File tree

docs/scripts/generate-api-ref.sh

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,4 +149,37 @@ 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>`. Escape both so Vue treats the
168+
# brackets as literal text. The `<a id="…"></a>` anchor itself is
169+
# intentionally not touched -- `a` is lowercase, so the `<[A-Z]`
170+
# alternative misses it, and the `<\[` alternative is unambiguous.
171+
# Fenced code blocks render via the highlighter, not Vue's template
172+
# parser, so the bare generics they contain (e.g.
173+
# `public IReadOnlyList<EnvVarInfo> Collect(...)`) do not need
174+
# escaping. A small awk pass tracks ```-fence state and skips lines
175+
# inside fences so code-block content stays untouched.
176+
while IFS= read -r -d '' md_file; do
177+
awk '
178+
BEGIN { in_fence = 0 }
179+
/^```/ { in_fence = !in_fence; print; next }
180+
in_fence { print; next }
181+
{ gsub(/<\[/, "\\\\&"); gsub(/<[A-Z]/, "\\\\&"); print }
182+
' "${md_file}" > "${md_file}.tmp" && mv "${md_file}.tmp" "${md_file}"
183+
done < <(find docs/api -name '*.md' -not -name 'index.md' -print0)
184+
152185
echo "==> done. $(find docs/api -name '*.md' -not -name 'index.md' | wc -l) pages generated."

0 commit comments

Comments
 (0)