Skip to content

Commit 5ca5a33

Browse files
committed
Added support for modules.
Only public classes are exported/imported. The "EnableModules" option is a three-state value. 0 = disable. 1 = auto. If 2 or more files, then enable. 2 = enable. Default: 1
1 parent ad3dd18 commit 5ca5a33

3 files changed

Lines changed: 221 additions & 68 deletions

File tree

CSharpToJavaScript/CSTOJS.cs

Lines changed: 78 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
using Microsoft.CodeAnalysis.CSharp.Syntax;
55
using Microsoft.CodeAnalysis.CSharp;
66
using Microsoft.CodeAnalysis;
7-
using System.Linq;
87
using CSharpToJavaScript.Utils;
98
using System.Collections.Immutable;
109

@@ -45,16 +44,41 @@ public static FileData[] Translate(FileData[] files, MetadataReference[]? refere
4544

4645
for (int i = 0; i < files.Length; i++)
4746
{
48-
trees[i] = CSharpSyntaxTree.ParseText(files[i].SourceStr);
47+
trees[i] = CSharpSyntaxTree.ParseText(files[i].SourceStr, path: files[i].FileName);
4948
}
5049

51-
trees[0] = AddGlobalUsings(trees[0]);
52-
50+
trees[0] = AddGlobalUsings(trees[0]).WithFilePath(files[0].FileName);
51+
5352
CSharpCompilation compilation = CSharpCompilation
5453
.Create("HelloWorld")
5554
.AddReferences(references)
5655
.AddSyntaxTrees(trees);
5756

57+
58+
Dictionary<string, List<string>> _exportedClasses = new();
59+
60+
//Enable modules if, more than 2 files.
61+
if (files.Length >= 2)
62+
{
63+
for (int i = 0; i < files.Length; i++)
64+
{
65+
if (files[i].OptionsForFile.EnableModules == 1)
66+
files[i].OptionsForFile.EnableModules = 2;
67+
}
68+
}
69+
70+
for (int i = 0; i < files.Length; i++)
71+
{
72+
if (files[i].OptionsForFile.EnableModules >= 2)
73+
{
74+
SemanticModel _model = compilation.GetSemanticModel(trees[i]);
75+
76+
ExportClassesWalker _exportClassesWalker = new(_model, ref _exportedClasses);
77+
SyntaxNode _root = trees[i].GetRoot();
78+
_exportClassesWalker.Visit(_root);
79+
}
80+
}
81+
5882
for (int i = 0; i < files.Length; i++)
5983
{
6084
if (files[i].OptionsForFile.TranslateFile == false)
@@ -80,7 +104,7 @@ public static FileData[] Translate(FileData[] files, MetadataReference[]? refere
80104

81105
SyntaxNode _root = trees[i].GetRoot();
82106

83-
WithSemanticRewriter _withSemanticRewriter = new(_model, files[i].OptionsForFile);
107+
WithSemanticRewriter _withSemanticRewriter = new(_model, files[i].OptionsForFile, _exportedClasses);
84108
WithoutSemanticRewriter _withoutSemanticRewriter = new(files[i].OptionsForFile);
85109

86110
StringBuilderWalker _stringBuilderWalker = new();
@@ -111,7 +135,47 @@ public static FileData[] Translate(FileData[] files, MetadataReference[]? refere
111135
}
112136

113137
_stringBuilderWalker.JSSB.Append(files[i].OptionsForFile.AddSBAtTheTop);
138+
139+
//Import modules
140+
if (files[i].OptionsForFile.EnableModules >= 2)
141+
{
142+
Dictionary<string, List<string>>.KeyCollection _keys = _withSemanticRewriter.ImportClasses.Keys;
143+
144+
foreach (string _filename in _keys)
145+
{
146+
_stringBuilderWalker.JSSB.AppendLine();
147+
_stringBuilderWalker.JSSB.Append("import { ");
148+
for (int j = 0; j < _withSemanticRewriter.ImportClasses[_filename].Count; j++)
149+
{
150+
if (j == _withSemanticRewriter.ImportClasses[_filename].Count - 1)
151+
_stringBuilderWalker.JSSB.Append($"{_withSemanticRewriter.ImportClasses[_filename][j]}");
152+
else
153+
_stringBuilderWalker.JSSB.Append($"{_withSemanticRewriter.ImportClasses[_filename][j]}, ");
154+
}
155+
_stringBuilderWalker.JSSB.AppendLine($" }} from './{_filename}';");
156+
}
157+
}
158+
114159
_stringBuilderWalker.Visit(_root);
160+
161+
//Export modules
162+
if (files[i].OptionsForFile.EnableModules >= 2)
163+
{
164+
if (_exportedClasses.TryGetValue(files[i].FileName, out List<string>? _value))
165+
{
166+
_stringBuilderWalker.JSSB.AppendLine();
167+
_stringBuilderWalker.JSSB.Append("export { ");
168+
for (int j = 0; j < _value.Count; j++)
169+
{
170+
if (j == _value.Count - 1)
171+
_stringBuilderWalker.JSSB.Append($"{_value[j]}");
172+
else
173+
_stringBuilderWalker.JSSB.Append($"{_value[j]}, ");
174+
}
175+
_stringBuilderWalker.JSSB.AppendLine(" };");
176+
}
177+
}
178+
115179
_stringBuilderWalker.JSSB.Append(files[i].OptionsForFile.AddSBAtTheBottom);
116180

117181
files[i].TranslatedStr = _stringBuilderWalker.JSSB.ToString();
@@ -226,12 +290,8 @@ private static MetadataReference[] GetReferences(CSTOJSOptions options)
226290
private static SyntaxTree AddGlobalUsings(SyntaxTree tree)
227291
{
228292
CompilationUnitSyntax root = tree.GetCompilationUnitRoot();
229-
UsingDirectiveSyntax[] oldUsing = root.Usings.ToArray();
230293

231-
//https://stackoverflow.com/a/72938702
232-
root = root.WithUsings
233-
(
234-
SyntaxFactory.List<UsingDirectiveSyntax>
294+
SyntaxList<UsingDirectiveSyntax> newUsing = SyntaxFactory.List<UsingDirectiveSyntax>
235295
(
236296
new UsingDirectiveSyntax[]
237297
{
@@ -377,8 +437,10 @@ private static SyntaxTree AddGlobalUsings(SyntaxTree tree)
377437
SyntaxFactory.Token(SyntaxKind.GlobalKeyword)
378438
)
379439
}
380-
)
381-
).AddUsings(oldUsing);
440+
).AddRange(root.Usings);
441+
442+
//https://stackoverflow.com/a/72938702
443+
root = root.WithUsings(newUsing);
382444

383445
return root.SyntaxTree;
384446
}
@@ -389,6 +451,10 @@ private static SyntaxTree AddGlobalUsings(SyntaxTree tree)
389451
/// </summary>
390452
public class FileData
391453
{
454+
/// <summary>
455+
/// Full JS filename. Needed for modules.
456+
/// </summary>
457+
public string FileName { get; set; } = string.Empty;
392458
/// <summary>
393459
/// Options for a translation.
394460
/// </summary>

CSharpToJavaScript/CSTOJSOptions.cs

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,38 @@ public class CSTOJSOptions
121121
/// Default: <c>false</c>
122122
/// </value>
123123
public bool MakePropertiesEnumerable { get; set; } = false;
124-
124+
125+
/// <summary>
126+
/// Enable module export/import. Three-state value.
127+
/// </summary>
128+
/// <remarks>
129+
/// <blockquote class="NOTE"><h5>NOTE</h5>
130+
/// <para>
131+
/// cstojs_options.xml: &lt;Option EnableModules=&quot;1&quot; /&gt;
132+
/// </para>
133+
/// </blockquote>
134+
/// <blockquote class="NOTE"><h5>NOTE</h5>
135+
/// <para>
136+
/// Note, for imports a filename needs to be supplied.
137+
/// </para>
138+
/// </blockquote>
139+
/// <blockquote class="IMPORTANT"><h5>IMPORTANT</h5>
140+
/// <para>
141+
/// Only public classes are exported/imported.
142+
/// </para>
143+
/// </blockquote>
144+
/// <para>
145+
/// Possible values: <br />
146+
/// 0 = Disable modules. No exports or imports. <br />
147+
/// 1 = Automatic. If 2 or more files are supplied, enable modules. <br />
148+
/// 2 = Enable modules. <br />
149+
/// </para>
150+
/// </remarks>
151+
/// <value>
152+
/// Default: <c>1</c>
153+
/// </value>
154+
public byte EnableModules { get; set; } = 1;
155+
125156
/// <summary>
126157
/// Array of custom names to convert.
127158
/// </summary>

0 commit comments

Comments
 (0)