@@ -19,11 +19,108 @@ def escape_generics(text):
1919
2020
2121def fix_html_for_jsx (text ):
22- """Fix HTML attributes for JSX compatibility."""
22+ """Convert HTML to Markdown for MDX compatibility."""
2323 if not text :
2424 return text
25- # Replace class= with className=
26- return text .replace ('class=' , 'className=' )
25+
26+ # Convert pre/code blocks to markdown code fences first
27+ def convert_code_block (match ):
28+ lang = ''
29+ lang_match = re .search (r'class(?:Name)?="lang-(\w+)"' , match .group (0 ))
30+ if lang_match :
31+ lang = lang_match .group (1 )
32+ code_match = re .search (r'<code[^>]*>(.*?)</code>' , match .group (0 ), re .DOTALL )
33+ if code_match :
34+ content = code_match .group (1 )
35+ content = content .replace ('>' , '>' ).replace ('<' , '<' ).replace ('&' , '&' )
36+ return f'\n \n ```{ lang } \n { content } \n ```\n \n '
37+ return match .group (0 )
38+ text = re .sub (r'<pre><code[^>]*>.*?</code></pre>' , convert_code_block , text , flags = re .DOTALL )
39+
40+ # Convert inline code tags to backticks
41+ text = re .sub (r'<code>(.*?)</code>' , r'`\1`' , text )
42+
43+ # Remove XML doc tags like <param>, <returns>, <typeparam>, etc.
44+ text = re .sub (r'<param\s+name="([^"]+)">(.*?)</param>' , r'- `\1`: \2' , text )
45+ text = re .sub (r'<typeparam\s+name="([^"]+)">(.*?)</typeparam>' , r'- `\1`: \2' , text )
46+ text = re .sub (r'<returns>(.*?)</returns>' , r'Returns: \1' , text )
47+ text = re .sub (r'<see\s+cref="([^"]+)"\s*/>' , r'`\1`' , text )
48+ text = re .sub (r'<seealso\s+cref="([^"]+)"\s*/>' , r'`\1`' , text )
49+
50+ # Fix HTML entities
51+ text = text .replace ('>' , '>' ).replace ('<' , '<' ).replace ('&' , '&' )
52+
53+ # Convert HTML lists to Markdown lists
54+ if '<ul>' in text or '<li>' in text :
55+ # Process nested lists
56+ def convert_list (html ):
57+ result = []
58+ depth = 0
59+ parts = re .split (r'(</?(?:ul|li)>)' , html )
60+ current = ''
61+ for part in parts :
62+ if part == '<ul>' :
63+ depth += 1
64+ elif part == '</ul>' :
65+ depth = max (0 , depth - 1 )
66+ elif part == '<li>' :
67+ current = ''
68+ elif part == '</li>' :
69+ if current .strip ():
70+ indent = ' ' * max (0 , depth - 1 )
71+ result .append (f'{ indent } - { current .strip ()} ' )
72+ current = ''
73+ else :
74+ current += part
75+ return '\n ' .join (result )
76+ text = convert_list (text )
77+
78+ # Convert p tags to paragraphs
79+ def convert_p (match ):
80+ content = match .group (1 )
81+ # Don't collapse if contains code block
82+ if '```' in content :
83+ return f'{ content .strip ()} \n \n '
84+ # Collapse whitespace for regular text
85+ content = ' ' .join (content .split ())
86+ return f'{ content } \n \n '
87+ text = re .sub (r'<p>\s*(.*?)\s*</p>' , convert_p , text , flags = re .DOTALL )
88+
89+ # Clean up extra newlines
90+ text = re .sub (r'\n{3,}' , '\n \n ' , text )
91+
92+ return text .strip ()
93+
94+
95+ def convert_xref_tags (text ):
96+ """Convert DocFX <xref> tags to plain text or links."""
97+ if not text :
98+ return text
99+
100+ import urllib .parse
101+
102+ def replace_xref (match ):
103+ href = match .group (1 )
104+ # Parse the href to extract type and text
105+ if '?' in href :
106+ type_name , query = href .split ('?' , 1 )
107+ params = urllib .parse .parse_qs (query )
108+ display_text = params .get ('text' , [type_name ])[0 ]
109+ display_text = urllib .parse .unquote_plus (display_text )
110+ else :
111+ type_name = href
112+ display_text = type_name
113+
114+ # Create link for System types to Microsoft docs
115+ if type_name .startswith ('System.' ):
116+ url = f"https://learn.microsoft.com/dotnet/api/{ type_name .lower ()} "
117+ return f"[{ display_text } ]({ url } )"
118+
119+ return display_text
120+
121+ # Match <xref href="..." ...></xref> or <xref href="..." ... />
122+ pattern = r'<xref\s+href="([^"]+)"[^>]*(?:></xref>|/>)'
123+ return re .sub (pattern , replace_xref , text )
27124
28125
29126def escape_generics_in_link_text (text ):
@@ -155,14 +252,14 @@ def generate_markdown(yaml_data):
155252 md += f"**{ fact_name } **: { fact_value } \n \n "
156253
157254 if 'markdown' in item :
158- md += f"{ item ['markdown' ]} \n \n "
255+ md += f"{ fix_html_for_jsx ( item ['markdown' ]) } \n \n "
159256
160257 if 'h2' in item :
161258 md += f"## { item ['h2' ]} \n \n "
162259
163260 if 'h4' in item :
164261 h4_text = item ['h4' ]
165- if h4_text in ['Parameters' , 'Returns' , 'Field Value' , 'Property Value' ]:
262+ if h4_text in ['Parameters' , 'Returns' , 'Field Value' , 'Property Value' , 'Type Parameters' , 'Exceptions' , 'Remarks' , 'Event Type' ]:
166263 md += f"<ApiLabel>{ h4_text } </ApiLabel>\n \n "
167264 else :
168265 md += f"#### { h4_text } \n \n "
@@ -229,7 +326,8 @@ def generate_markdown(yaml_data):
229326 if 'api3' in item :
230327 src = item .get ('src' , '' )
231328 api3_title = str (item .get ('api3' , '' ))
232- api3_title = re .sub (r'<[^>]+>' , '' , api3_title )
329+ # Escape generics for markdown heading (use backslash)
330+ api3_title = api3_title .replace ('<' , '\\ <' ).replace ('>' , '\\ >' )
233331 api3_title = re .sub (r'\[[^\]]+\]' , '' , api3_title )
234332 md += f"### { api3_title } \n \n "
235333 if src != '' :
@@ -272,31 +370,35 @@ def convert_yaml_file(src_path, dest_path):
272370 os .makedirs (folder_path , exist_ok = True )
273371
274372 md_content = md_content .replace ('/api/shared' , '/api' ).replace ('/api/core' , '/api' )
373+ md_content = convert_xref_tags (md_content )
374+ # Escape < followed by numbers (like <1000) which MDX interprets as JSX tags
375+ md_content = re .sub (r'<(\d)' , r'\\<\1' , md_content )
275376
276377 with open (final_path , 'w' , encoding = 'utf-8' ) as f :
277378 f .write (md_content )
278379
279380
280- for root , dirs , files in os .walk (SOURCE_DIR ):
281- for file in files :
282- if file .endswith (".yml" ) or file .endswith (".yaml" ):
283- raw_base = os .path .splitext (file )[0 ]
284- for prefix in ["SwiftlyS2.Core." , "SwiftlyS2.Shared." , "SwiftlyS2." ]:
285- if raw_base .startswith (prefix ):
286- raw_base = raw_base [len (prefix ):]
287- break
288- new_base = transform_filename (raw_base )
289- dest_file = os .path .join (DEST_DIR , "/" .join (new_base .split ("." )).lower () + ".mdx" )
290- convert_yaml_file (os .path .join (root , file ), dest_file )
291-
292- script_dir = os .path .dirname (os .path .abspath (__file__ ))
293- index_source = os .path .join (script_dir , "index.mdx" )
294- index_dest = os .path .join (DEST_DIR , "index.mdx" )
295-
296- if os .path .exists (index_source ):
297- shutil .copy2 (index_source , index_dest )
298- print (f"Copied index.mdx to { index_dest } " )
299- else :
300- print (f"Warning: index.mdx not found at { index_source } " )
301-
302- print ("MDX generation complete!" )
381+ if __name__ == "__main__" :
382+ for root , dirs , files in os .walk (SOURCE_DIR ):
383+ for file in files :
384+ if file .endswith (".yml" ) or file .endswith (".yaml" ):
385+ raw_base = os .path .splitext (file )[0 ]
386+ for prefix in ["SwiftlyS2.Core." , "SwiftlyS2.Shared." , "SwiftlyS2." ]:
387+ if raw_base .startswith (prefix ):
388+ raw_base = raw_base [len (prefix ):]
389+ break
390+ new_base = transform_filename (raw_base )
391+ dest_file = os .path .join (DEST_DIR , "/" .join (new_base .split ("." )).lower () + ".mdx" )
392+ convert_yaml_file (os .path .join (root , file ), dest_file )
393+
394+ script_dir = os .path .dirname (os .path .abspath (__file__ ))
395+ index_source = os .path .join (script_dir , "index.mdx" )
396+ index_dest = os .path .join (DEST_DIR , "index.mdx" )
397+
398+ if os .path .exists (index_source ):
399+ shutil .copy2 (index_source , index_dest )
400+ print (f"Copied index.mdx to { index_dest } " )
401+ else :
402+ print (f"Warning: index.mdx not found at { index_source } " )
403+
404+ print ("MDX generation complete!" )
0 commit comments