11using System ;
22using System . Collections . Generic ;
3+ using System . IO ;
34using System . Linq ;
45using System . Text ;
56using System . Text . RegularExpressions ;
@@ -11,7 +12,7 @@ internal static class IntellisenseWriter
1112 {
1213 private static readonly Regex _whitespaceTrimmer = new Regex ( @"^\s+|\s+$|\s*[\r\n]+\s*" , RegexOptions . Compiled ) ;
1314
14- public static string WriteTypeScript ( IEnumerable < IntellisenseObject > objects )
15+ public static string WriteTypeScript ( IEnumerable < IntellisenseObject > objects , string sourceItemPath )
1516 {
1617 var sb = new StringBuilder ( ) ;
1718 sb . AppendLine ( "// ------------------------------------------------------------------------------" ) ;
@@ -20,57 +21,112 @@ public static string WriteTypeScript(IEnumerable<IntellisenseObject> objects)
2021 sb . AppendLine ( "// </auto-generated>" ) ;
2122 sb . AppendLine ( "// ------------------------------------------------------------------------------" ) ;
2223
24+ string export = ! Options . DeclareModule ? "export " : string . Empty ;
25+ string prefixModule = Options . DeclareModule ? "\t " : String . Empty ;
26+
27+ var sbBody = new StringBuilder ( ) ;
28+
29+ var neededImports = new List < string > ( ) ;
30+
2331 foreach ( var ns in objects . GroupBy ( o => o . Namespace ) )
2432 {
2533 if ( Options . DeclareModule )
2634 {
27- sb . AppendFormat ( "declare module {0} {{\r \n " , ns . Key ) ;
35+ sbBody . AppendFormat ( "declare module {0} {{\r \n " , ns . Key ) ;
2836 }
2937
30- string export = ! Options . DeclareModule ? "export " : string . Empty ;
31- string prefixModule = Options . DeclareModule ? "\t " : String . Empty ;
32-
3338 foreach ( IntellisenseObject io in ns )
3439 {
3540 if ( ! string . IsNullOrEmpty ( io . Summary ) )
36- sb . Append ( prefixModule ) . AppendLine ( "/** " + _whitespaceTrimmer . Replace ( io . Summary , "" ) + " */" ) ;
41+ sbBody . Append ( prefixModule ) . AppendLine ( "/** " + _whitespaceTrimmer . Replace ( io . Summary , "" ) + " */" ) ;
3742
3843 if ( io . IsEnum )
3944 {
4045 string type = "enum " ;
41- sb . Append ( prefixModule ) . Append ( export ) . Append ( type ) . Append ( Utility . CamelCaseClassName ( io . Name ) ) . Append ( " " ) ;
46+ sbBody . Append ( prefixModule ) . Append ( export ) . Append ( type ) . Append ( Utility . CamelCaseClassName ( io . Name ) ) . Append ( " " ) ;
4247
43- sb . AppendLine ( "{" ) ;
44- WriteTSEnumDefinition ( sb , prefixModule + "\t " , io . Properties ) ;
45- sb . Append ( prefixModule ) . AppendLine ( "}" ) ;
48+ sbBody . AppendLine ( "{" ) ;
49+ WriteTSEnumDefinition ( sbBody , prefixModule + "\t " , io . Properties ) ;
50+ sbBody . Append ( prefixModule ) . AppendLine ( "}" ) ;
4651 }
4752 else
4853 {
4954 string type = Options . ClassInsteadOfInterface ? "class " : "interface " ;
50- sb . Append ( prefixModule ) . Append ( export ) . Append ( type ) . Append ( Utility . CamelCaseClassName ( io . Name ) ) . Append ( " " ) ;
55+ sbBody . Append ( prefixModule ) . Append ( export ) . Append ( type ) . Append ( Utility . CamelCaseClassName ( io . Name ) ) . Append ( " " ) ;
5156
5257 if ( ! string . IsNullOrEmpty ( io . BaseName ) )
5358 {
54- sb . Append ( "extends " ) ;
59+ sbBody . Append ( "extends " ) ;
5560
5661 if ( ! string . IsNullOrEmpty ( io . BaseNamespace ) && io . BaseNamespace != io . Namespace )
57- sb . Append ( io . BaseNamespace ) . Append ( "." ) ;
62+ sbBody . Append ( io . BaseNamespace ) . Append ( "." ) ;
5863
59- sb . Append ( Utility . CamelCaseClassName ( io . BaseName ) ) . Append ( " " ) ;
64+ sbBody . Append ( Utility . CamelCaseClassName ( io . BaseName ) ) . Append ( " " ) ;
6065 }
6166
62- sb . AppendLine ( "{" ) ;
63- WriteTSInterfaceDefinition ( sb , prefixModule + "\t " , io . Properties ) ;
64- sb . Append ( prefixModule ) . AppendLine ( "}" ) ;
67+ sbBody . AppendLine ( "{" ) ;
68+ WriteTSInterfaceDefinition ( sbBody , prefixModule + "\t " , io . Properties ) ;
69+ sbBody . Append ( prefixModule ) . AppendLine ( "}" ) ;
70+ // remember client-side references for which we need imports
71+ neededImports . AddRange ( io . Properties . Where ( p => p . Type . ClientSideReferenceName != null )
72+ . Select ( p => p . Type . ClientSideReferenceName ) ) ;
6573 }
6674 }
6775
6876 if ( Options . DeclareModule )
6977 {
70- sb . AppendLine ( "}" ) ;
78+ sbBody . AppendLine ( "}" ) ;
7179 }
7280 }
7381
82+ // if interface, import external interfaces and base classes
83+ if ( ! Options . DeclareModule )
84+ {
85+ var imports = new List < string > ( ) ;
86+
87+ var references = objects . SelectMany ( o => o . References ) . Distinct ( ) ;
88+ foreach ( var reference in references )
89+ {
90+ var referencePathRelative = Utility . GetRelativePath ( sourceItemPath , reference ) ;
91+ // remove trailing ".ts" which is not expected for TS imports
92+ referencePathRelative = referencePathRelative . Substring ( 0 , referencePathRelative . Length - 3 ) ;
93+ // make sure path contains forward slashes which are expected by TS
94+ referencePathRelative = referencePathRelative . Replace ( Path . DirectorySeparatorChar , '/' ) ;
95+ var referenceName = Utility . RemoveDefaultExtension ( Path . GetFileName ( reference ) ) ;
96+
97+ // skipped indirect references
98+ if ( ! neededImports . Contains ( referenceName ) )
99+ {
100+ continue ;
101+ }
102+
103+ sb . AppendLine ( $ "import {{ { referenceName } }} from \" { referencePathRelative } \" ;") ;
104+ imports . Add ( referenceName ) ;
105+ }
106+
107+ // also import base classes if not yet imported
108+ var baseClasses = objects . Select ( o => o . BaseName ) . Where ( b => b != null && ! imports . Contains ( b ) ) . Distinct ( ) ;
109+ foreach ( var b in baseClasses )
110+ {
111+ var expectedBaseClassPath = Path . Combine ( Path . GetDirectoryName ( sourceItemPath ) , b + ".cs" ) ;
112+ if ( ! File . Exists ( expectedBaseClassPath ) )
113+ {
114+ throw new ExceptionForUser ( $ "Could not find base class for { b } . Expected path: { expectedBaseClassPath } ") ;
115+ }
116+
117+ sb . AppendLine ( $ "import {{ { b } }} from \" ./{ b } .generated\" ;") ;
118+ imports . Add ( b ) ;
119+ }
120+
121+ var notImportedNeededImports = neededImports . Except ( imports ) ;
122+ if ( notImportedNeededImports . Any ( ) )
123+ {
124+ throw new ExceptionForUser ( $ "Sorry, needed imports missing: { string . Join ( ", " , notImportedNeededImports ) } ") ;
125+ }
126+ }
127+
128+ sb . Append ( sbBody ) ;
129+
74130 if ( Options . EOLType == EOLType . LF )
75131 sb . Replace ( "\r \n " , "\n " ) ;
76132
0 commit comments