Last active
February 1, 2023 12:40
-
-
Save vbilopav/152d87c689598ace8cb28da3f61e0f4e to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
Creates TypeScript models from CSharp models | |
- TypeScript model is set by config key TsModelsPath | |
- CSharp models namespace is set by config key ModelNamespace | |
1) Add to your Program.cs: | |
if (ModelBuilder.Build(args)) | |
{ | |
return; | |
} | |
2) Call with: "dotnet run -- build-models" | |
*/ | |
using System.Reflection; | |
using System.Text; | |
namespace MyNamespace.Scripts; | |
public static class ModelBuilder | |
{ | |
private static bool IsNullable(this Type type) => type == typeof(string) || type.IsClass | |
? true | |
: type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>); | |
private static bool IsDict(this Type type) => type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Dictionary<,>); | |
private static bool IsList(this Type type) => type.IsGenericType && type.GetGenericTypeDefinition() == typeof(List<>); | |
private static string ToTsType(this Type? type) => type switch | |
{ | |
Type t when t == typeof(string) => "string", | |
Type t when t == typeof(List<string>) => "string[]", | |
Type t when t == typeof(int) || t == typeof(int?) => "number", | |
Type t when t == typeof(bool) || t == typeof(bool?) => "boolean", | |
Type t when t.IsList() => $"I{t.GenericTypeArguments?.FirstOrDefault()?.ToTsType()}[]", | |
Type t when t.IsDict() => $"Record<{t.GenericTypeArguments?.FirstOrDefault()?.ToTsType()}, {t.GenericTypeArguments?.LastOrDefault()?.ToTsType()}>", | |
Type t when t.IsClass => $"I{t.Name}", | |
_ => "any" | |
}; | |
public static bool Build(string[] args) | |
{ | |
if (!(args.Length == 1 && args[0] == "build-models")) | |
{ | |
return false; | |
} | |
var config = new ConfigurationBuilder() | |
.AddJsonFile("appsettings.json", false, false) | |
.AddJsonFile("appsettings.Development.json", false, false) | |
.Build(); | |
var filePath = config.GetValue<string>("TsModelsPath"); | |
if (filePath is null) | |
{ | |
Console.WriteLine("ERROR: ConfigModelsPath is not set."); | |
return true; | |
} | |
var modelNamespace = config.GetValue<string>("ModelNamespace"); | |
if (filePath is null) | |
{ | |
Console.WriteLine("ERROR: ModelNamespace is not set."); | |
return true; | |
} | |
StringBuilder sb = new(); | |
sb.AppendLine("/*auto generated*/"); | |
foreach (var modelClass in Assembly.Load(modelNamespace?.Split('.')[0] ?? "") | |
.GetTypes() | |
.Where(t => string.Equals(t.Namespace, modelNamespace, StringComparison.Ordinal)) ?? Enumerable.Empty<Type>()) | |
{ | |
sb.AppendLine(); | |
sb.AppendLine($"interface I{modelClass.Name} {{"); | |
foreach (var prop in modelClass.GetProperties()) | |
{ | |
var type = prop.PropertyType.ToTsType(); | |
var nullable = prop.PropertyType.IsNullable(); | |
var name = string.Concat(prop.Name[0].ToString().ToLower(), prop.Name[1..]); | |
sb.AppendLine($" {name}{(nullable ? "?" : "")}: {type};"); | |
} | |
sb.AppendLine("}"); | |
} | |
File.WriteAllText(filePath, sb.ToString()); | |
Console.WriteLine($"{filePath} written successfully"); | |
return true; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment