static void Main() { var sourcesPath = Path.Combine(Environment.CurrentDirectory, "Sources"); Console.WriteLine($"Running from: {Environment.CurrentDirectory}"); Console.WriteLine($"Sources from: {sourcesPath}"); Console.WriteLine("Modify the sources to compile and run it!"); var compiler = new Compiler(); var runner = new Runner(); using (var watcher = new ObservableFileSystemWatcher(c => { c.Path = @".\Sources"; })) { var changes = watcher.Changed.Throttle(TimeSpan.FromSeconds(.5)).Where(c => c.FullPath.EndsWith(@"DynamicProgram.cs")).Select(c => c.FullPath); changes.Subscribe(filepath => runner.Execute(compiler.Compile(filepath), new[] { "France" })); watcher.Start(); Console.WriteLine("Press any key to exit!"); Console.ReadLine(); } }
public static async Task Live(LiveConfiguration configuration = null) { //Extract LiveBlazor.zip to a temporary folter //var stream = typeof(LiveBlazor).Assembly.GetManifestResourceStream("LivingThing.LiveBlazor.LiveBlazor.zip"); //var workingDirectory = Path.GetDirectoryName(Path.Combine(Environment.CurrentDirectory, "..", "LiveBlazor")); //if (!Directory.Exists(workingDirectory)) //{ // Directory.CreateDirectory(workingDirectory); //} //var zipPath = Path.Combine(workingDirectory, "LiveBlazor.zip"); //FileStream fs = new FileStream(zipPath, FileMode.Create); //stream.CopyTo(fs); //fs.Close(); //stream.Close(); //ZipFile.ExtractToDirectory(zipPath, workingDirectory, true); ////prebuild project enabling restore, so we dont have to restor anymore, which is faster //$"cd {workingDirectory} & dotnet build".Bash(); harmony = new Harmony("com.liveblazor.livingthing"); var compiler = new Compiler(); var invokeAsync = typeof(ComponentBase).GetMethod("InvokeAsync", bindingAttr: BindingFlags.NonPublic | BindingFlags.Instance, types: new Type[] { typeof(Action) }, binder: null, modifiers: null); var stateHasChanged = typeof(ComponentBase).GetMethod("StateHasChanged", BindingFlags.NonPublic | BindingFlags.Instance); //find all components in all assemblies var componentTypes = AppDomain.CurrentDomain.GetAssemblies().SelectMany(a => a.GetTypes()).Where(t => !t.IsAbstract && t.MemberType == MemberTypes.TypeInfo && typeof(ComponentBase).IsAssignableFrom(t)).ToArray(); var prefix = typeof(Blazor).GetMethod(nameof(Prefix)); foreach (var _type in componentTypes) { var types = configuration?.Filter?.Invoke(_type) ?? new Type[] { _type }; foreach (var type in types) { if (!typeof(ComponentBase).IsAssignableFrom(type)) { throw new InvalidOperationException($"Type {type} is not a Component"); } if (!type.ContainsGenericParameters) { var renderTree = type.GetMethod("BuildRenderTree", BindingFlags.NonPublic | BindingFlags.Instance); if (renderTree.DeclaringType == type) { if (type.Name == "DeviceListItem") { } var context = new LiveComponentContext() { OriginalMethod = type.GetMethod("BuildRenderTree", BindingFlags.NonPublic | BindingFlags.Instance), OriginalTypeInference = type.Assembly.GetType("__Blazor." + type.FullName + ".TypeInference"), }; liveContexts[type] = context; harmony.Patch(renderTree, new HarmonyMethod(prefix)); } } } } string watchPath = configuration?.WatchDirectory ?? Environment.CurrentDirectory; if (configuration?.WatchDirectory == null) { var solutionPath = Path.GetFullPath(Path.Combine(watchPath, "../")); if (Directory.EnumerateFiles(solutionPath).Any(f => f.EndsWith(".sln"))) { watchPath = solutionPath; } } watcher = new ObservableFileSystemWatcher(c => { c.Path = watchPath; c.IncludeSubdirectories = true; c.Filter = "*.razor"; c.NotifyFilter = NotifyFilters.Attributes | NotifyFilters.CreationTime | NotifyFilters.FileName | NotifyFilters.LastAccess | NotifyFilters.LastWrite | NotifyFilters.Size | NotifyFilters.Security; //c.Filters.Add("*.razor"); }); var changes = watcher.Changed.Throttle(TimeSpan.FromSeconds(.5)); string dotnetPath = (await "where dotnet".CLI()).StdOut.Trim(); string dotnetVersion = (await "dotnet --version".CLI()).StdOut.Trim(); var dotnetFolder = Path.GetDirectoryName(dotnetPath) + "\\"; var buildRenderTreeMethodPatcher = typeof(Blazor).GetMethod(nameof(ReplaceBuildRenderTree)); var typeInferenceMethodsPatcher = typeof(Blazor).GetMethod(nameof(ReplaceTypeInference)); changes.Subscribe(async filepath => { string razorGeneratePath = configuration?.RazoGeneratorPath ?? @$ "{dotnetFolder}sdk\{dotnetVersion}\Sdks\Microsoft.NET.Sdk.Razor\tools\netcoreapp3.0\rzc.dll"; var project = GetProjectPath(Path.GetDirectoryName(filepath.FullPath)); string projectName = Path.GetFileNameWithoutExtension(project.FileName); var workspace = $"obj\\Debug\\{project.Type}\\"; var workingDirectory = $"{project.Path}{workspace}"; var outputPath = $"{workingDirectory}{Path.GetFileName(filepath.FullPath)}.g.cs"; string filePathInProject = filepath.FullPath.Replace(project.Path, ""); var @namespace = projectName; string compile = $"dotnet exec \"{razorGeneratePath}\" generate -s \"{filepath.FullPath}\" -r \"{filePathInProject}\" -o \"{outputPath}\" -k component -p {project.Path} -v 3.0 -c {configuration?.ProjectConfiguration??"Default"} --root-namespace {@namespace} -t \"{workspace}{projectName}.TagHelpers.output.cache\""; await $"cd {project.Path} & {compile}".CLI(); var file = File.ReadAllText(outputPath); List <string> sourceCodes = new List <string>() { file }; var csFile = Path.ChangeExtension(filepath.FullPath, ".razor.cs"); if (File.Exists(csFile)) { var csFileContent = File.ReadAllText(csFile); sourceCodes.Add(csFileContent); } var code = compiler.Compile(sourceCodes.ToArray()); using (var asm = new MemoryStream(code)) { var assemblyLoadContext = new UnloadableAssemblyLoadContext(); var assembly = assemblyLoadContext.LoadFromStream(asm); // var assembly = Assembly.Load(code);//.LoadFromStream(asm); Type newType = assembly.ExportedTypes.First(t => t.Name == Path.GetFileNameWithoutExtension(filepath.Name)); //Type newType = AppDomain.CurrentDomain.GetAssemblies().SelectMany(a => a.GetTypes()).First(t => t.Name == Path.GetFileNameWithoutExtension(filepath.Name)); assemblyLoadContext.Unload(); Type originalType = componentTypes.FirstOrDefault(t => t.FullName == newType.FullName); if (originalType != null) { LiveComponentContext context = null; liveContexts.TryGetValue(originalType, out context); if (context != null) { context.NewType = newType; currentType = originalType; try { harmony.Patch(context.OriginalMethod, transpiler: new HarmonyMethod(buildRenderTreeMethodPatcher)); //patch all anonymous method of this type //var anonymousMethods = //pathch typeInference class Type inferenceType = assembly.DefinedTypes.FirstOrDefault(t => t.Name == "TypeInference"); if (inferenceType != null && context.OriginalTypeInference != null) { currentTypeInference = inferenceType; var methods = inferenceType.GetMethods(); foreach (var method in methods) { var originalMethod = context.OriginalTypeInference.GetMethod(method.Name); harmony.Patch(originalMethod, transpiler: new HarmonyMethod(typeInferenceMethodsPatcher)); } } } catch (Exception e) { Console.WriteLine(e.Message); Console.WriteLine(e.StackTrace); return; } context.Components.ForEach(c => { Action rerender = () => stateHasChanged.Invoke(c, new object[] { }); invokeAsync.Invoke(c, new object[] { rerender }); }); } } } });
public FilesystemInjectorClient() { WatchMask = "*.dll, *.exe"; _fileSystemWatcher = new ObservableFileSystemWatcher(fsw => fsw.IncludeSubdirectories = true); }