frhagn / Typewriter

Automatic TypeScript template generation from C# source files
http://frhagn.github.io/Typewriter
Apache License 2.0
536 stars 132 forks source link

temporary brute force alternative till vNext #293

Open Beej126 opened 5 years ago

Beej126 commented 5 years ago

i've been hesitant to mention a rough temporary alternative at the risk of fracturing from the noble efforts of current and next gen Typewriter... and possibly underestimating the work involved to fully apply this alternative to each situation... yet it has given us a way to relieve different kinds of pressures that i've seen come through many other requests (e.g. CLI, for VSCode, externals DLLs, DLL lock, sharing template helper code, etc, etc) and i humbly hope it could provide similar relief to others in the interim of grander solutions... i assume it's already been thought of and admit it's not doing anything earth shattering, largely just re-purposing something else already doing the hard work...

TL;DR;

leverage a library that makes it trivial to dynamically load an assembly, and then write your own code to enumerate that assembly however you like to generate your desired output... and sure go ahead and query any other inputs that you need for your output, e.g. project files, build settings files, etc

as annoyingly DIY as this might sound at first blush, when i looked at our ultimate class generator typewriter "template" built up over time, it is not much real template left vs a ton of helper methods that enumerate all our usual assembly suspects (Classes, Properties, Attributes) and spit out the corresponding typescript chunks

McMaster DotNetCorePlugins is the assembly loader nuget library that's worked very nicely for us so far... this is .net core based... i have to imagine there would be something equivalent for traditional .net framework but we don't need that ourselves and haven't explored

here is a quick & dirty console program.cs to give the idea

using McMaster.NETCore.Plugins;
using System;
using System.Linq;
using System.Reflection;

namespace TypeRighterCore {
 class Program {
   static void Main(string[] args) {
     var argstring = string.Join(", ", args.Select((arg, index) => $"{index}:{arg}"));
     Console.WriteLine($"Hello World! - args: {argstring}");

     var assemblyPath = args[0];
     //@"C:\Users\banderson\source\repos\Disposal\bin\Debug\netcoreapp2.2\Disposal.dll";
     //var assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(assemblyPath);
     var loader = PluginLoader.CreateFromAssemblyFile(assemblyPath, null);
     var assembly = loader.LoadDefaultAssembly();
     EnumerateAssembly(assembly);

     Console.WriteLine("press any key to end");
     Console.ReadKey();
   }

   static void EnumerateAssembly(Assembly assembly) {

     var types = assembly.GetTypes();
     foreach (var type in types) {
       Console.WriteLine($"Type Name: {type.FullName}");

       if (type.IsGenericType) {
         Console.WriteLine("  Generic Parms: " + string.Join(", ", type.GetGenericArguments().Select(t => t.FullName)).ToArray());
       }

       var props = type.GetProperties();
       if (props.Length > 0) {
         Console.WriteLine("  Properties:");
         foreach (var prop in type.GetProperties()) {
           Console.WriteLine($"    { prop.Name }: { prop.PropertyType.Name }");
         }
       }

     }
   }
 }
}

example run command line

λ dotnet ThisConsole.dll "{path to your assembly}"

sample output

Type Name: Disposal.Models.ApiResponse`1
 Generic Parms: System.Char[]
 Properties:
   Data: T
   Success: Boolean
   ErrorMessages: String[]
Type Name: Disposal.Models.DisposalItemBase
 Properties:
   RuleId: String
   MatrixClass: String
   LoginNum: String
   SampleNum: String
   SampleCount: Int32
   ProjectNum: String
   DeptNum: String
   DeptNums: Int32[]
   DoneDate: Nullable`1
   DueDate: Nullable`1
   ProjDispDays: Int32
   DisposalDate: Nullable`1
   Comments: String
   LastDate: Nullable`1
   LastUser: String
   IsDisposed: Boolean
   IsSelected: Boolean

once we whipped that up, the potential was clear... we iterated on a few basic needs, e.g.:

go ahead and throw stones at the lack of robust integration, i do hope it sparks ideas.