11using System . Globalization ;
2+ using System . Text ;
3+ using System . Xml . Linq ;
24using NJsonSchema ;
35using NSwag ;
46using NSwag . CodeGeneration . CSharp ;
@@ -18,6 +20,270 @@ static string FindRepoRoot()
1820 return Directory . GetCurrentDirectory ( ) ;
1921}
2022
23+ static void GenerateLlmsTxt ( string repoRoot )
24+ {
25+ var xmlPath = Path . Combine ( repoRoot , "Thirdweb" , "bin" , "Release" , "netstandard2.1" , "Thirdweb.xml" ) ;
26+ var outputPath = Path . Combine ( repoRoot , "llms.txt" ) ;
27+
28+ if ( ! File . Exists ( xmlPath ) )
29+ {
30+ Console . WriteLine ( $ "XML documentation not found at { xmlPath } ") ;
31+ Console . WriteLine ( "Please build the project in Release mode first." ) ;
32+ Environment . Exit ( 1 ) ;
33+ }
34+
35+ Console . WriteLine ( $ "Reading XML documentation from { xmlPath } ...") ;
36+ var doc = XDocument . Load ( xmlPath ) ;
37+ var sb = new StringBuilder ( ) ;
38+
39+ _ = sb . AppendLine ( "THIRDWEB .NET SDK - API DOCUMENTATION" ) ;
40+ _ = sb . AppendLine ( "=====================================" ) ;
41+ _ = sb . AppendLine ( ) ;
42+
43+ var assembly = doc . Root ? . Element ( "assembly" ) ? . Element ( "name" ) ? . Value ;
44+ if ( assembly != null )
45+ {
46+ _ = sb . AppendLine ( $ "Assembly: { assembly } ") ;
47+ _ = sb . AppendLine ( ) ;
48+ }
49+
50+ var members = doc . Root ? . Element ( "members" ) ? . Elements ( "member" ) ;
51+ if ( members != null )
52+ {
53+ foreach ( var member in members )
54+ {
55+ var name = member . Attribute ( "name" ) ? . Value ;
56+ if ( string . IsNullOrEmpty ( name ) )
57+ {
58+ continue ;
59+ }
60+
61+ _ = sb . AppendLine ( new string ( '-' , 80 ) ) ;
62+ _ = sb . AppendLine ( name ) ;
63+ _ = sb . AppendLine ( new string ( '-' , 80 ) ) ;
64+
65+ // Parse signature for better display
66+ var signature = ParseMemberSignature ( name ) ;
67+ if ( signature != null )
68+ {
69+ _ = sb . AppendLine ( ) ;
70+ _ = sb . AppendLine ( $ "KIND: { signature . Kind } ") ;
71+ if ( ! string . IsNullOrEmpty ( signature . ReturnType ) )
72+ {
73+ _ = sb . AppendLine ( $ "RETURN TYPE: { signature . ReturnType } ") ;
74+ }
75+ }
76+
77+ // Group param elements by name attribute
78+ var paramDocs = member . Elements ( "param" ) . Select ( p => new { Name = p . Attribute ( "name" ) ? . Value , Description = p . Value . Trim ( ) } ) . Where ( p => ! string . IsNullOrEmpty ( p . Name ) ) . ToList ( ) ;
79+
80+ // Display parameters with their names and types
81+ if ( signature ? . Parameters != null && signature . Parameters . Count > 0 )
82+ {
83+ _ = sb . AppendLine ( ) ;
84+ _ = sb . AppendLine ( "PARAMETERS:" ) ;
85+ for ( var i = 0 ; i < signature . Parameters . Count ; i ++ )
86+ {
87+ var param = signature . Parameters [ i ] ;
88+ // Try to get the actual parameter name from documentation
89+ var paramDoc = i < paramDocs . Count ? paramDocs [ i ] : null ;
90+ var paramName = paramDoc ? . Name ?? param . Name ;
91+
92+ _ = sb . AppendLine ( $ " - { paramName } ({ param . Type } )") ;
93+ if ( paramDoc != null && ! string . IsNullOrEmpty ( paramDoc . Description ) )
94+ {
95+ _ = sb . AppendLine ( $ " { NormalizeXmlText ( paramDoc . Description ) . Replace ( "\n " , "\n " ) } ") ;
96+ }
97+ }
98+ }
99+
100+ // Display other elements (summary, remarks, returns, exception, etc.)
101+ foreach ( var element in member . Elements ( ) )
102+ {
103+ if ( element . Name . LocalName == "param" )
104+ {
105+ continue ; // Already handled above
106+ }
107+
108+ var content = element . Value . Trim ( ) ;
109+ if ( string . IsNullOrEmpty ( content ) )
110+ {
111+ continue ;
112+ }
113+
114+ _ = sb . AppendLine ( ) ;
115+ var elementName = element . Name . LocalName . ToUpper ( ) ;
116+
117+ // Add attribute info for exceptions and type params
118+ var nameAttr = element . Attribute ( "name" ) ? . Value ;
119+ var crefAttr = element . Attribute ( "cref" ) ? . Value ;
120+ if ( ! string . IsNullOrEmpty ( nameAttr ) )
121+ {
122+ elementName += $ " ({ nameAttr } )";
123+ }
124+ else if ( ! string . IsNullOrEmpty ( crefAttr ) )
125+ {
126+ elementName += $ " ({ crefAttr } )";
127+ }
128+
129+ _ = sb . AppendLine ( $ "{ elementName } :") ;
130+ _ = sb . AppendLine ( NormalizeXmlText ( content ) ) ;
131+ }
132+
133+ _ = sb . AppendLine ( ) ;
134+ }
135+ }
136+
137+ File . WriteAllText ( outputPath , sb . ToString ( ) ) ;
138+ Console . WriteLine ( $ "Generated llms.txt at { outputPath } ") ;
139+ }
140+
141+ static MemberSignature ? ParseMemberSignature ( string memberName )
142+ {
143+ if ( string . IsNullOrEmpty ( memberName ) || memberName . Length < 2 )
144+ {
145+ return null ;
146+ }
147+
148+ var kind = memberName [ 0 ] switch
149+ {
150+ 'M' => "Method" ,
151+ 'P' => "Property" ,
152+ 'T' => "Type" ,
153+ 'F' => "Field" ,
154+ 'E' => "Event" ,
155+ _ => "Unknown" ,
156+ } ;
157+
158+ var fullSignature = memberName [ 2 ..] ; // Remove "M:", "P:", etc.
159+
160+ // For methods, parse parameters and return type
161+ if ( memberName [ 0 ] == 'M' )
162+ {
163+ var parameters = new List < ParameterInfo > ( ) ;
164+ var methodName = fullSignature ;
165+ var returnType = "System.Threading.Tasks.Task" ; // Default for async methods
166+
167+ // Extract parameters from signature
168+ var paramStart = fullSignature . IndexOf ( '(' ) ;
169+ if ( paramStart >= 0 )
170+ {
171+ methodName = fullSignature [ ..paramStart ] ;
172+ var paramEnd = fullSignature . LastIndexOf ( ')' ) ;
173+ if ( paramEnd > paramStart )
174+ {
175+ var paramString = fullSignature [ ( paramStart + 1 ) ..paramEnd ] ;
176+ if ( ! string . IsNullOrEmpty ( paramString ) )
177+ {
178+ var paramParts = SplitParameters ( paramString ) ;
179+ for ( var i = 0 ; i < paramParts . Count ; i ++ )
180+ {
181+ var paramType = SimplifyTypeName ( paramParts [ i ] ) ;
182+ parameters . Add ( new ParameterInfo { Name = $ "param{ i + 1 } ", Type = paramType } ) ;
183+ }
184+ }
185+ }
186+
187+ // Check if there's a return type after the parameters
188+ if ( paramEnd + 1 < fullSignature . Length && fullSignature [ paramEnd + 1 ] == '~' )
189+ {
190+ returnType = SimplifyTypeName ( fullSignature [ ( paramEnd + 2 ) ..] ) ;
191+ }
192+ }
193+
194+ return new MemberSignature
195+ {
196+ Kind = kind ,
197+ Parameters = parameters ,
198+ ReturnType = parameters . Count > 0 || fullSignature . Contains ( "Async" ) ? returnType : null ,
199+ } ;
200+ }
201+
202+ return new MemberSignature { Kind = kind } ;
203+ }
204+
205+ static List < string > SplitParameters ( string paramString )
206+ {
207+ var parameters = new List < string > ( ) ;
208+ var current = new StringBuilder ( ) ;
209+ var depth = 0 ;
210+
211+ foreach ( var c in paramString )
212+ {
213+ if ( c is '{' or '<' )
214+ {
215+ depth ++ ;
216+ }
217+ else if ( c is '}' or '>' )
218+ {
219+ depth -- ;
220+ }
221+ else if ( c == ',' && depth == 0 )
222+ {
223+ parameters . Add ( current . ToString ( ) ) ;
224+ _ = current . Clear ( ) ;
225+ continue ;
226+ }
227+
228+ _ = current . Append ( c ) ;
229+ }
230+
231+ if ( current . Length > 0 )
232+ {
233+ parameters . Add ( current . ToString ( ) ) ;
234+ }
235+
236+ return parameters ;
237+ }
238+
239+ static string SimplifyTypeName ( string fullTypeName )
240+ {
241+ // Remove assembly information
242+ var typeName = fullTypeName . Split ( ',' ) [ 0 ] ;
243+
244+ // Simplify common generic types
245+ typeName = typeName
246+ . Replace ( "System.Threading.Tasks.Task{" , "Task<" )
247+ . Replace ( "System.Collections.Generic.List{" , "List<" )
248+ . Replace ( "System.Collections.Generic.Dictionary{" , "Dictionary<" )
249+ . Replace ( "System.Nullable{" , "Nullable<" )
250+ . Replace ( "System.String" , "string" )
251+ . Replace ( "System.Int32" , "int" )
252+ . Replace ( "System.Int64" , "long" )
253+ . Replace ( "System.Boolean" , "bool" )
254+ . Replace ( "System.Byte" , "byte" )
255+ . Replace ( "System.Object" , "object" )
256+ . Replace ( '{' , '<' )
257+ . Replace ( '}' , '>' ) ;
258+
259+ return typeName ;
260+ }
261+
262+ static string NormalizeXmlText ( string text )
263+ {
264+ var lines = text . Split ( '\n ' ) ;
265+ var normalized = new StringBuilder ( ) ;
266+
267+ foreach ( var line in lines )
268+ {
269+ var trimmed = line . Trim ( ) ;
270+ if ( ! string . IsNullOrEmpty ( trimmed ) )
271+ {
272+ _ = normalized . AppendLine ( trimmed ) ;
273+ }
274+ }
275+
276+ return normalized . ToString ( ) . TrimEnd ( ) ;
277+ }
278+
279+ var cmdArgs = Environment . GetCommandLineArgs ( ) ;
280+ if ( cmdArgs . Length > 1 && cmdArgs [ 1 ] == "--llms" )
281+ {
282+ var root = FindRepoRoot ( ) ;
283+ GenerateLlmsTxt ( root ) ;
284+ return ;
285+ }
286+
21287var specUrl = "https://api.thirdweb.com/openapi.json" ;
22288var repoRoot = FindRepoRoot ( ) ;
23289var outputPath = Path . Combine ( repoRoot , "Thirdweb" , "Thirdweb.Api" , "ThirdwebApi.cs" ) ;
@@ -177,3 +443,16 @@ void DedupeEnumOnSchema(JsonSchema schema, string? debugPath)
177443Directory . CreateDirectory ( Path . GetDirectoryName ( outputPath ) ! ) ;
178444await File . WriteAllTextAsync ( outputPath , code ) ;
179445Console . WriteLine ( $ "Wrote generated client to { outputPath } ") ;
446+
447+ internal class MemberSignature
448+ {
449+ public string Kind { get ; set ; } = "" ;
450+ public List < ParameterInfo > ? Parameters { get ; set ; }
451+ public string ? ReturnType { get ; set ; }
452+ }
453+
454+ internal class ParameterInfo
455+ {
456+ public string Name { get ; set ; } = "" ;
457+ public string Type { get ; set ; } = "" ;
458+ }
0 commit comments