From b73c33532e693bf54290a1e305b37d766f00b8aa Mon Sep 17 00:00:00 2001 From: Adam Kauffman <26984068+A9G-Data-Droid@users.noreply.github.com> Date: Sun, 27 Apr 2025 21:17:35 -0700 Subject: [PATCH 1/5] Refactor Getting ready for multiple render types --- mermaid-graph/Commands.cs | 113 ++----------------------- mermaid-graph/Diagrams/ClassDiagram.cs | 110 ++++++++++++++++++++++++ mermaid-graph/Diagrams/Diagram.cs | 33 ++++++++ mermaid-graphTests/CommandsTests.cs | 13 +-- 4 files changed, 156 insertions(+), 113 deletions(-) create mode 100644 mermaid-graph/Diagrams/ClassDiagram.cs create mode 100644 mermaid-graph/Diagrams/Diagram.cs diff --git a/mermaid-graph/Commands.cs b/mermaid-graph/Commands.cs index 890246d..60369ca 100644 --- a/mermaid-graph/Commands.cs +++ b/mermaid-graph/Commands.cs @@ -1,6 +1,4 @@ -using System.Text; -using Microsoft.Build.Construction; -using Microsoft.Build.Evaluation; +using MermaidGraph.Diagrams; using Microsoft.Build.Locator; namespace MermaidGraph; @@ -10,18 +8,9 @@ namespace MermaidGraph; /// public class Commands { - public const string MermaidBegin = Fence + "mermaid"; - public const string Fence = "```"; - private readonly StringBuilder _graph; - - /// - /// Initialize the graph output - /// public Commands() { - _graph = new StringBuilder(); - // Ensure MSBuild is registered if (!MSBuildLocator.IsRegistered) { @@ -35,18 +24,9 @@ public Commands() /// `.csproj` file. public string Project(FileInfo file) { - Header(file.Name); - using var projectCollection = new ProjectCollection(); - var project = projectCollection.LoadProject(file.FullName); - GraphProject(project); - _graph.AppendLine(Fence); - var graph = _graph.ToString(); - - // Cleanup - _graph.Clear(); - projectCollection.UnloadAllProjects(); - - return graph; + var graph = new ClassDiagram(); + + return graph.Project(file); } /// @@ -55,89 +35,8 @@ public string Project(FileInfo file) /// `.sln` file. public string Solution(FileInfo file) { - Header(file.Name); - var solutionFile = SolutionFile.Parse(file.FullName); - var solutionName = Path.GetFileNameWithoutExtension(file.Name); - var solutionId = $"{solutionName}"; - _graph.AppendLine($$""" - class {{solutionName}}{ - type solution - } - """); - - using var projectCollection = new ProjectCollection(); + var graph = new ClassDiagram(); - foreach (var project in solutionFile.ProjectsInOrder) - { - if (project.ProjectType != SolutionProjectType.KnownToBeMSBuildFormat) continue; - - var projectPath = project.AbsolutePath; - var projectName = Path.GetFileNameWithoutExtension(projectPath); - _graph.AppendLine($" {solutionId} --> {projectName}"); - var projectFile = new FileInfo(projectPath); - if (projectFile.Exists) - { - var referenceProject = projectCollection.LoadProject(projectFile.FullName); - GraphProject(referenceProject); - } - } - - _graph.AppendLine(Fence); - var graph = _graph.ToString(); - - // Cleanup - _graph.Clear(); - projectCollection.UnloadAllProjects(); - - return graph; - } - - private void Header(string title) - { - _graph.AppendLine(MermaidBegin); - _graph.AppendLine($""" - --- - title: {title} - config: - class: - hideEmptyMembersBox: true - --- - """); - - _graph.AppendLine("classDiagram"); - } - - private void GraphProject(Project project) - { - var projectName = Path.GetFileNameWithoutExtension(project.FullPath); - var type = project.GetPropertyValue("OutputType"); - var targetFramework = project.GetPropertyValue("TargetFramework") ?? project.GetPropertyValue("TargetFrameworks"); - _graph.AppendLine($$""" - class {{projectName}}{ - type {{type}} - target {{targetFramework}} - } - """); - - foreach (var item in project.GetItems("ProjectReference")) - { - var refPath = item.EvaluatedInclude; - var refName = Path.GetFileNameWithoutExtension(refPath); - _graph.AppendLine($" {projectName} ..> {refName}"); - } - - foreach (var item in project.GetItems("PackageReference")) - { - var packageName = item.EvaluatedInclude; - var version = item.GetMetadataValue("Version"); - _graph.AppendLine($$""" - class {{packageName}}{ - type NuGet - version {{version}} - } - """); - - _graph.AppendLine($" {projectName} ..> {packageName}"); - } + return graph.Solution(file); } } \ No newline at end of file diff --git a/mermaid-graph/Diagrams/ClassDiagram.cs b/mermaid-graph/Diagrams/ClassDiagram.cs new file mode 100644 index 0000000..033e83f --- /dev/null +++ b/mermaid-graph/Diagrams/ClassDiagram.cs @@ -0,0 +1,110 @@ +using Microsoft.Build.Construction; +using Microsoft.Build.Evaluation; + +namespace MermaidGraph.Diagrams; + +internal class ClassDiagram : Diagram +{ + public override void Header(string title) + { + base.Header(title); + Graph.AppendLine("classDiagram"); + } + + /// + /// Generate the dependency graph of a Visual Studio Project. + /// + /// `.csproj` file. + public string Project(FileInfo file) + { + Header(file.Name); + using var projectCollection = new ProjectCollection(); + var project = projectCollection.LoadProject(file.FullName); + GraphProject(project); + Graph.AppendLine(Fence); + var graph = Graph.ToString(); + + // Cleanup + Graph.Clear(); + projectCollection.UnloadAllProjects(); + + return graph; + } + + /// + /// Generate the dependency graph of a Visual Studio Solution. + /// + /// `.sln` file. + public string Solution(FileInfo file) + { + Header(file.Name); + var solutionFile = SolutionFile.Parse(file.FullName); + var solutionName = Path.GetFileNameWithoutExtension(file.Name); + var solutionId = $"{solutionName}"; + Graph.AppendLine($$""" + class {{solutionName}}{ + type solution + } + """); + + using var projectCollection = new ProjectCollection(); + + foreach (var project in solutionFile.ProjectsInOrder) + { + if (project.ProjectType != SolutionProjectType.KnownToBeMSBuildFormat) continue; + + var projectPath = project.AbsolutePath; + var projectName = Path.GetFileNameWithoutExtension(projectPath); + Graph.AppendLine($" {solutionId} --> {projectName}"); + var projectFile = new FileInfo(projectPath); + if (projectFile.Exists) + { + var referenceProject = projectCollection.LoadProject(projectFile.FullName); + GraphProject(referenceProject); + } + } + + Graph.AppendLine(Fence); + var graph = Graph.ToString(); + + // Cleanup + Graph.Clear(); + projectCollection.UnloadAllProjects(); + + return graph; + } + + private void GraphProject(Project project) + { + var projectName = Path.GetFileNameWithoutExtension(project.FullPath); + var type = project.GetPropertyValue("OutputType"); + var targetFramework = project.GetPropertyValue("TargetFramework") ?? project.GetPropertyValue("TargetFrameworks"); + Graph.AppendLine($$""" + class {{projectName}}{ + type {{type}} + target {{targetFramework}} + } + """); + + foreach (var item in project.GetItems("ProjectReference")) + { + var refPath = item.EvaluatedInclude; + var refName = Path.GetFileNameWithoutExtension(refPath); + Graph.AppendLine($" {projectName} ..> {refName}"); + } + + foreach (var item in project.GetItems("PackageReference")) + { + var packageName = item.EvaluatedInclude; + var version = item.GetMetadataValue("Version"); + Graph.AppendLine($$""" + class {{packageName}}{ + type NuGet + version {{version}} + } + """); + + Graph.AppendLine($" {projectName} ..> {packageName}"); + } + } +} \ No newline at end of file diff --git a/mermaid-graph/Diagrams/Diagram.cs b/mermaid-graph/Diagrams/Diagram.cs new file mode 100644 index 0000000..0b830ed --- /dev/null +++ b/mermaid-graph/Diagrams/Diagram.cs @@ -0,0 +1,33 @@ +using System.Text; + +namespace MermaidGraph.Diagrams; + +public class Diagram +{ + public const string Fence = "```"; + public const string MermaidBegin = Fence + "mermaid"; + + internal readonly StringBuilder Graph = new(); + + /// + /// Initialize the graph output + /// + public virtual void Header(string title) + { + Graph.AppendLine(MermaidBegin); + Graph.AppendLine($""" + --- + title: {title} + config: + class: + hideEmptyMembersBox: true + --- + """); + } + + /// + /// Get the mermaid diagram markdown text. + /// + /// The contents of the graph buffer. + public override string ToString() => Graph.ToString(); +} \ No newline at end of file diff --git a/mermaid-graphTests/CommandsTests.cs b/mermaid-graphTests/CommandsTests.cs index 7adb517..0e9d34e 100644 --- a/mermaid-graphTests/CommandsTests.cs +++ b/mermaid-graphTests/CommandsTests.cs @@ -1,4 +1,5 @@ -using Microsoft.ClearScript.V8; +using MermaidGraph.Diagrams; +using Microsoft.ClearScript.V8; using NUnit.Framework; using Assert = NUnit.Framework.Assert; @@ -88,13 +89,13 @@ public void CommandLineFailTests(string? file, int hResult) Assert.That(Program.Main(file), Is.EqualTo(hResult)); } - private static string ExtractMermaid(string markup) + private static string ExtractMermaid(string? markup) { - Assert.That(markup, Does.StartWith(Commands.MermaidBegin)); - markup = markup.Substring(Commands.MermaidBegin.Length + Environment.NewLine.Length); + Assert.That(markup, Does.StartWith(Diagram.MermaidBegin)); + markup = markup.Substring(Diagram.MermaidBegin.Length + Environment.NewLine.Length); - Assert.That(markup, Does.EndWith(Commands.Fence + Environment.NewLine)); - return markup.Substring(0, markup.Length - Commands.MermaidBegin.Length + Environment.NewLine.Length); + Assert.That(markup, Does.EndWith(Diagram.Fence + Environment.NewLine)); + return markup.Substring(0, markup.Length - Diagram.MermaidBegin.Length + Environment.NewLine.Length); } private string? DetectType(string markup) From a4d25b1196293f71b062fc9c5edd5f67c05e7129 Mon Sep 17 00:00:00 2001 From: Adam Kauffman <26984068+A9G-Data-Droid@users.noreply.github.com> Date: Mon, 28 Apr 2025 18:37:43 -0700 Subject: [PATCH 2/5] Add GraphDiagram option - Refactor for polymorphic architecture - Test both diagram types --- mermaid-graph/Commands.cs | 47 +++++---- mermaid-graph/Diagrams/ClassDiagram.cs | 5 +- mermaid-graph/Diagrams/Diagram.cs | 35 ++++++- mermaid-graph/Diagrams/GraphDiagram.cs | 111 ++++++++++++++++++++++ mermaid-graph/Diagrams/IMermaidDiagram.cs | 23 +++++ mermaid-graph/Program.cs | 17 ++-- mermaid-graphTests/CommandsTests.cs | 21 ++-- 7 files changed, 224 insertions(+), 35 deletions(-) create mode 100644 mermaid-graph/Diagrams/GraphDiagram.cs create mode 100644 mermaid-graph/Diagrams/IMermaidDiagram.cs diff --git a/mermaid-graph/Commands.cs b/mermaid-graph/Commands.cs index 60369ca..d9ab0d4 100644 --- a/mermaid-graph/Commands.cs +++ b/mermaid-graph/Commands.cs @@ -1,30 +1,37 @@ -using MermaidGraph.Diagrams; -using Microsoft.Build.Locator; +using System.Diagnostics; +using MermaidGraph.Diagrams; namespace MermaidGraph; /// -/// The commands that can be run by `mermaid-graph` +/// Specifies the type of mermaid diagram to generate. /// -public class Commands +public enum DiagramType { + /// + /// Represents a general dependency graph. + /// + Graph, - public Commands() - { - // Ensure MSBuild is registered - if (!MSBuildLocator.IsRegistered) - { - MSBuildLocator.RegisterDefaults(); - } - } + /// + /// Represents a class diagram. + /// + Class +} +/// +/// The commands that can be run by `mermaid-graph` +/// +public class Commands +{ /// /// Generate the dependency graph of a Visual Studio Project. /// /// `.csproj` file. - public string Project(FileInfo file) + /// + public static string Project(FileInfo file, DiagramType diagramType = DiagramType.Graph) { - var graph = new ClassDiagram(); + var graph = GetGraphType(diagramType); return graph.Project(file); } @@ -33,10 +40,18 @@ public string Project(FileInfo file) /// Generate the dependency graph of a Visual Studio Solution. /// /// `.sln` file. - public string Solution(FileInfo file) + /// + public static string Solution(FileInfo file, DiagramType diagramType = DiagramType.Graph) { - var graph = new ClassDiagram(); + var graph = GetGraphType(diagramType); return graph.Solution(file); } + + private static Diagram GetGraphType(DiagramType diagramType) => diagramType switch + { + DiagramType.Class => new ClassDiagram(), + DiagramType.Graph => new GraphDiagram(), + _ => throw new NotImplementedException($"Option not supported: {diagramType}"), + }; } \ No newline at end of file diff --git a/mermaid-graph/Diagrams/ClassDiagram.cs b/mermaid-graph/Diagrams/ClassDiagram.cs index 033e83f..83079f6 100644 --- a/mermaid-graph/Diagrams/ClassDiagram.cs +++ b/mermaid-graph/Diagrams/ClassDiagram.cs @@ -5,6 +5,7 @@ namespace MermaidGraph.Diagrams; internal class ClassDiagram : Diagram { + /// public override void Header(string title) { base.Header(title); @@ -15,7 +16,7 @@ public override void Header(string title) /// Generate the dependency graph of a Visual Studio Project. /// /// `.csproj` file. - public string Project(FileInfo file) + public override string Project(FileInfo file) { Header(file.Name); using var projectCollection = new ProjectCollection(); @@ -35,7 +36,7 @@ public string Project(FileInfo file) /// Generate the dependency graph of a Visual Studio Solution. /// /// `.sln` file. - public string Solution(FileInfo file) + public override string Solution(FileInfo file) { Header(file.Name); var solutionFile = SolutionFile.Parse(file.FullName); diff --git a/mermaid-graph/Diagrams/Diagram.cs b/mermaid-graph/Diagrams/Diagram.cs index 0b830ed..aab57c8 100644 --- a/mermaid-graph/Diagrams/Diagram.cs +++ b/mermaid-graph/Diagrams/Diagram.cs @@ -1,14 +1,37 @@ using System.Text; +using Microsoft.Build.Locator; namespace MermaidGraph.Diagrams; -public class Diagram +/// +/// The Diagram abstract class implements shared functionality for Mermaid diagram generation, +/// including initializing the graph output and managing the graph buffer. +/// +public abstract class Diagram : IMermaidDiagram { + /// + /// Code block fence + /// public const string Fence = "```"; + + /// + /// Mermaid code block + /// public const string MermaidBegin = Fence + "mermaid"; internal readonly StringBuilder Graph = new(); + /// + /// Initialize the Diagram class and ensure MSBuild is registered. + /// + protected Diagram() + { + if (!MSBuildLocator.IsRegistered) + { + MSBuildLocator.RegisterDefaults(); + } + } + /// /// Initialize the graph output /// @@ -26,8 +49,14 @@ public virtual void Header(string title) } /// - /// Get the mermaid diagram markdown text. + /// Get the mermaid diagram Markdown text. /// /// The contents of the graph buffer. public override string ToString() => Graph.ToString(); -} \ No newline at end of file + + /// + public abstract string Project(FileInfo file); + + /// + public abstract string Solution(FileInfo file); +} diff --git a/mermaid-graph/Diagrams/GraphDiagram.cs b/mermaid-graph/Diagrams/GraphDiagram.cs new file mode 100644 index 0000000..fe8f43b --- /dev/null +++ b/mermaid-graph/Diagrams/GraphDiagram.cs @@ -0,0 +1,111 @@ +using Microsoft.Build.Construction; +using Microsoft.Build.Evaluation; + +namespace MermaidGraph.Diagrams; + +internal class GraphDiagram : Diagram +{ + /// + public override void Header(string title) + { + base.Header(title); + Graph.AppendLine("graph TD"); + } + + /// + /// Generate the dependency graph of a Visual Studio Project. + /// + /// `.csproj` file. + public override string Project(FileInfo file) + { + Header(file.Name); + using var projectCollection = new ProjectCollection(); + var project = projectCollection.LoadProject(file.FullName); + GraphProject(project); + Graph.AppendLine(Fence); + var graph = Graph.ToString(); + + // Cleanup + Graph.Clear(); + projectCollection.UnloadAllProjects(); + + return graph; + } + + /// + /// Generate the dependency graph of a Visual Studio Solution. + /// + /// `.sln` file. + public override string Solution(FileInfo file) + { + Header(file.Name); + var solutionFile = SolutionFile.Parse(file.FullName); + var solutionName = Path.GetFileNameWithoutExtension(file.Name); + var solutionId = $"{solutionName}"; + Graph.AppendLine($$""" + class {{solutionName}}{ + type solution + } + """); + + using var projectCollection = new ProjectCollection(); + + foreach (var project in solutionFile.ProjectsInOrder) + { + if (project.ProjectType != SolutionProjectType.KnownToBeMSBuildFormat) continue; + + var projectPath = project.AbsolutePath; + var projectName = Path.GetFileNameWithoutExtension(projectPath); + Graph.AppendLine($" {solutionId} --> {projectName}"); + var projectFile = new FileInfo(projectPath); + if (projectFile.Exists) + { + var referenceProject = projectCollection.LoadProject(projectFile.FullName); + GraphProject(referenceProject); + } + } + + Graph.AppendLine(Fence); + var graph = Graph.ToString(); + + // Cleanup + Graph.Clear(); + projectCollection.UnloadAllProjects(); + + return graph; + } + + private void GraphProject(Project project) + { + var projectName = Path.GetFileNameWithoutExtension(project.FullPath); + var type = project.GetPropertyValue("OutputType"); + var targetFramework = project.GetPropertyValue("TargetFramework") ?? project.GetPropertyValue("TargetFrameworks"); + Graph.AppendLine($$""" + class {{projectName}}{ + type {{type}} + target {{targetFramework}} + } + """); + + foreach (var item in project.GetItems("ProjectReference")) + { + var refPath = item.EvaluatedInclude; + var refName = Path.GetFileNameWithoutExtension(refPath); + Graph.AppendLine($" {projectName} ..> {refName}"); + } + + foreach (var item in project.GetItems("PackageReference")) + { + var packageName = item.EvaluatedInclude; + var version = item.GetMetadataValue("Version"); + Graph.AppendLine($$""" + class {{packageName}}{ + type NuGet + version {{version}} + } + """); + + Graph.AppendLine($" {projectName} ..> {packageName}"); + } + } +} \ No newline at end of file diff --git a/mermaid-graph/Diagrams/IMermaidDiagram.cs b/mermaid-graph/Diagrams/IMermaidDiagram.cs new file mode 100644 index 0000000..4a1bd47 --- /dev/null +++ b/mermaid-graph/Diagrams/IMermaidDiagram.cs @@ -0,0 +1,23 @@ +namespace MermaidGraph.Diagrams; + +/// +/// This file defines the IMermaidDiagram interface and the Diagram abstract class. +/// The IMermaidDiagram interface provides methods for generating Mermaid diagrams +/// from Visual Studio project (*.csproj) and solution (*.sln) files. +/// +public interface IMermaidDiagram +{ + /// + /// Generate the diagram from a visual studio project file (*.csproj) + /// + /// The project file + /// Mermaid Markdown + public string Project(FileInfo file); + + /// + /// Generate the diagram from a visual studio solution file (*.sln) + /// + /// The solution file. + /// Mermaid Markdown + public string Solution(FileInfo file); +} \ No newline at end of file diff --git a/mermaid-graph/Program.cs b/mermaid-graph/Program.cs index 4726c6a..d94a14e 100644 --- a/mermaid-graph/Program.cs +++ b/mermaid-graph/Program.cs @@ -1,7 +1,5 @@ namespace MermaidGraph; -// ReSharper disable UnusedMember.Global - /// /// mermaid-graph.exe /// @@ -11,15 +9,18 @@ public sealed class Program /// Outputs a mermaid graph of the dependency diagram for a project, or whole solution. /// /// Full path to the solution (*.sln) or project (*.csproj) file that will be mapped. + /// The type of diagram to generate (e.g., Graph or Class). /// HResult - public static int Main(string? path) + public static int Main(string? path, DiagramType diagramType = DiagramType.Graph) { if (path is null) { - System.CommandLine.DragonFruit.CommandLine.ExecuteAssembly(typeof(AutoGeneratedProgram).Assembly, ["--help"], ""); + System.CommandLine.DragonFruit.CommandLine + .ExecuteAssembly(typeof(AutoGeneratedProgram).Assembly, ["--help"], ""); + return 1; } - + var file = new FileInfo(path); if (!file.Exists) { @@ -31,13 +32,13 @@ public static int Main(string? path) { if (path.EndsWith(".csproj")) { - Console.WriteLine(new Commands().Project(file)); + Console.WriteLine(Commands.Project(file, diagramType)); return 0; } if (path.EndsWith(".sln")) { - Console.WriteLine(new Commands().Solution(file)); + Console.WriteLine(Commands.Solution(file, diagramType)); return 0; } } @@ -50,4 +51,4 @@ public static int Main(string? path) Console.WriteLine($"Error: Unsupported file type - {path}"); return 3; } -} \ No newline at end of file +} diff --git a/mermaid-graphTests/CommandsTests.cs b/mermaid-graphTests/CommandsTests.cs index 0e9d34e..e12c95e 100644 --- a/mermaid-graphTests/CommandsTests.cs +++ b/mermaid-graphTests/CommandsTests.cs @@ -23,6 +23,13 @@ private V8ScriptEngine Js { } } + internal static readonly object[] DiagramTypeTestCases = + [ + new object[] { DiagramType.Class, "class" }, + new object[] { DiagramType.Class, "class" }, + new object[] { DiagramType.Graph, "flowchart" } + ]; + [OneTimeTearDown] public void Disposal() { @@ -30,34 +37,36 @@ public void Disposal() } [Test] - public void DogFoodSolutionTest() + [TestCaseSource(nameof(DiagramTypeTestCases))] + public void DogFoodSolutionTest(DiagramType type, string typeName) { var solutionPath = FindFileDownTree("*.sln"); Assert.That(solutionPath, Is.Not.Null); var info = new FileInfo(solutionPath!); Assert.That(info.Exists); - var graph = new Commands().Solution(info); + var graph = Commands.Solution(info, type); Console.WriteLine(graph); var graphType = DetectType(ExtractMermaid(graph)); - Assert.That(graphType, Is.EqualTo("class")); + Assert.That(graphType, Is.EqualTo(typeName)); Console.WriteLine(graphType); } [Test] - public void DogFoodProjectTestAsync() + [TestCaseSource(nameof(DiagramTypeTestCases))] + public void DogFoodProjectTestAsync(DiagramType type, string typeName) { var filePath = FindFileDownTree("*.csproj"); Assert.That(filePath, Is.Not.Null); var info = new FileInfo(filePath!); Assert.That(info.Exists); - var graph = new Commands().Project(info); + var graph = Commands.Project(info, type); Console.WriteLine(graph); var graphType = DetectType(ExtractMermaid(graph)); - Assert.That(graphType, Is.EqualTo("class")); + Assert.That(graphType, Is.EqualTo(typeName)); Console.WriteLine(graphType); } From 3c98c5cd7b10477a7497cac41d226ed54145ff03 Mon Sep 17 00:00:00 2001 From: Adam Kauffman <26984068+A9G-Data-Droid@users.noreply.github.com> Date: Mon, 28 Apr 2025 19:54:32 -0700 Subject: [PATCH 3/5] Version 1.0.0 - Keep the graph in the buffer until next header or scope end - Update README - Refactor, cleanup --- README.md | 27 ++++++++++++++++--- mermaid-graph/Commands.cs | 23 +++------------- mermaid-graph/Diagrams/ClassDiagram.cs | 14 +++------- mermaid-graph/Diagrams/DiagramType.cs | 17 ++++++++++++ mermaid-graph/Diagrams/GraphDiagram.cs | 14 +++------- mermaid-graph/Diagrams/IMermaidDiagram.cs | 2 +- .../{Diagram.cs => MermaidDiagram.cs} | 15 ++++++----- mermaid-graph/Program.cs | 12 +++++---- mermaid-graphTests/CommandsTests.cs | 10 +++---- 9 files changed, 72 insertions(+), 62 deletions(-) create mode 100644 mermaid-graph/Diagrams/DiagramType.cs rename mermaid-graph/Diagrams/{Diagram.cs => MermaidDiagram.cs} (78%) diff --git a/README.md b/README.md index 64f11b2..4afac30 100644 --- a/README.md +++ b/README.md @@ -19,13 +19,22 @@ Usage: mermaid-graph [options] Options: - --path Full path to the solution (*.sln) or project (*.csproj) file that will be mapped. - --version Show version information - -?, -h, --help Show help and usage information - ``` + --path Full path to the solution (*.sln) or project (*.csproj) file that will be mapped. + --type The type of diagram to generate (e.g., Graph or Class). [default: Graph] + --version Show version information + -?, -h, --help Show help and usage information +``` ## Example output from this solution +You can run the following command to generate a class diagram for this solution: + +```powershell +.\mermaid-graph.exe --path "MermaidGraph.NET.sln" --type Class +``` + +This will generate a mermaid graph in the console output, which can be piped to a file and used in a markdown document. + ```mermaid --- title: MermaidGraph.NET.sln @@ -78,6 +87,16 @@ classDiagram version 6.0.4 } MermaidGraphTests ..> coverlet.msbuild + class Microsoft.ClearScript.V8{ + type NuGet + version 7.4.5 + } + MermaidGraphTests ..> Microsoft.ClearScript.V8 + class Microsoft.ClearScript.V8.Native.win-x64{ + type NuGet + version 7.4.5 + } + MermaidGraphTests ..> Microsoft.ClearScript.V8.Native.win-x64 class Microsoft.NET.Test.Sdk{ type NuGet version 17.13.0 diff --git a/mermaid-graph/Commands.cs b/mermaid-graph/Commands.cs index d9ab0d4..32e3547 100644 --- a/mermaid-graph/Commands.cs +++ b/mermaid-graph/Commands.cs @@ -1,26 +1,9 @@ -using System.Diagnostics; -using MermaidGraph.Diagrams; +using MermaidGraph.Diagrams; namespace MermaidGraph; /// -/// Specifies the type of mermaid diagram to generate. -/// -public enum DiagramType -{ - /// - /// Represents a general dependency graph. - /// - Graph, - - /// - /// Represents a class diagram. - /// - Class -} - -/// -/// The commands that can be run by `mermaid-graph` +/// The commands that can be run by `mermaid-graph`. /// public class Commands { @@ -48,7 +31,7 @@ public static string Solution(FileInfo file, DiagramType diagramType = DiagramTy return graph.Solution(file); } - private static Diagram GetGraphType(DiagramType diagramType) => diagramType switch + private static IMermaidDiagram GetGraphType(DiagramType diagramType) => diagramType switch { DiagramType.Class => new ClassDiagram(), DiagramType.Graph => new GraphDiagram(), diff --git a/mermaid-graph/Diagrams/ClassDiagram.cs b/mermaid-graph/Diagrams/ClassDiagram.cs index 83079f6..f69e09f 100644 --- a/mermaid-graph/Diagrams/ClassDiagram.cs +++ b/mermaid-graph/Diagrams/ClassDiagram.cs @@ -3,7 +3,7 @@ namespace MermaidGraph.Diagrams; -internal class ClassDiagram : Diagram +internal class ClassDiagram : MermaidDiagram { /// public override void Header(string title) @@ -23,13 +23,10 @@ public override string Project(FileInfo file) var project = projectCollection.LoadProject(file.FullName); GraphProject(project); Graph.AppendLine(Fence); - var graph = Graph.ToString(); - // Cleanup - Graph.Clear(); projectCollection.UnloadAllProjects(); - return graph; + return Graph.ToString(); } /// @@ -66,13 +63,10 @@ type solution } Graph.AppendLine(Fence); - var graph = Graph.ToString(); - - // Cleanup - Graph.Clear(); + projectCollection.UnloadAllProjects(); - return graph; + return Graph.ToString(); } private void GraphProject(Project project) diff --git a/mermaid-graph/Diagrams/DiagramType.cs b/mermaid-graph/Diagrams/DiagramType.cs new file mode 100644 index 0000000..d580ec6 --- /dev/null +++ b/mermaid-graph/Diagrams/DiagramType.cs @@ -0,0 +1,17 @@ +namespace MermaidGraph.Diagrams; + +/// +/// Specifies the type of mermaid diagram to generate. +/// +public enum DiagramType +{ + /// + /// Represents a general dependency graph. + /// + Graph, + + /// + /// Represents a class diagram. + /// + Class +} \ No newline at end of file diff --git a/mermaid-graph/Diagrams/GraphDiagram.cs b/mermaid-graph/Diagrams/GraphDiagram.cs index fe8f43b..44f0a49 100644 --- a/mermaid-graph/Diagrams/GraphDiagram.cs +++ b/mermaid-graph/Diagrams/GraphDiagram.cs @@ -3,7 +3,7 @@ namespace MermaidGraph.Diagrams; -internal class GraphDiagram : Diagram +internal class GraphDiagram : MermaidDiagram { /// public override void Header(string title) @@ -23,13 +23,10 @@ public override string Project(FileInfo file) var project = projectCollection.LoadProject(file.FullName); GraphProject(project); Graph.AppendLine(Fence); - var graph = Graph.ToString(); - // Cleanup - Graph.Clear(); projectCollection.UnloadAllProjects(); - return graph; + return Graph.ToString(); } /// @@ -66,13 +63,10 @@ type solution } Graph.AppendLine(Fence); - var graph = Graph.ToString(); - - // Cleanup - Graph.Clear(); + projectCollection.UnloadAllProjects(); - return graph; + return Graph.ToString(); } private void GraphProject(Project project) diff --git a/mermaid-graph/Diagrams/IMermaidDiagram.cs b/mermaid-graph/Diagrams/IMermaidDiagram.cs index 4a1bd47..d9c6221 100644 --- a/mermaid-graph/Diagrams/IMermaidDiagram.cs +++ b/mermaid-graph/Diagrams/IMermaidDiagram.cs @@ -1,7 +1,7 @@ namespace MermaidGraph.Diagrams; /// -/// This file defines the IMermaidDiagram interface and the Diagram abstract class. +/// This file defines the IMermaidDiagram interface and the MermaidDiagram abstract class. /// The IMermaidDiagram interface provides methods for generating Mermaid diagrams /// from Visual Studio project (*.csproj) and solution (*.sln) files. /// diff --git a/mermaid-graph/Diagrams/Diagram.cs b/mermaid-graph/Diagrams/MermaidDiagram.cs similarity index 78% rename from mermaid-graph/Diagrams/Diagram.cs rename to mermaid-graph/Diagrams/MermaidDiagram.cs index aab57c8..8aeebdc 100644 --- a/mermaid-graph/Diagrams/Diagram.cs +++ b/mermaid-graph/Diagrams/MermaidDiagram.cs @@ -4,27 +4,27 @@ namespace MermaidGraph.Diagrams; /// -/// The Diagram abstract class implements shared functionality for Mermaid diagram generation, +/// The MermaidDiagram abstract class implements shared functionality for Mermaid diagram generation, /// including initializing the graph output and managing the graph buffer. /// -public abstract class Diagram : IMermaidDiagram +public abstract class MermaidDiagram : IMermaidDiagram { /// - /// Code block fence + /// Code block fence. /// public const string Fence = "```"; /// - /// Mermaid code block + /// Mermaid code block. /// public const string MermaidBegin = Fence + "mermaid"; internal readonly StringBuilder Graph = new(); /// - /// Initialize the Diagram class and ensure MSBuild is registered. + /// Initialize the MermaidDiagram class and ensure MSBuild is registered. /// - protected Diagram() + protected MermaidDiagram() { if (!MSBuildLocator.IsRegistered) { @@ -33,10 +33,11 @@ protected Diagram() } /// - /// Initialize the graph output + /// Initialize the graph output. /// public virtual void Header(string title) { + Graph.Clear(); Graph.AppendLine(MermaidBegin); Graph.AppendLine($""" --- diff --git a/mermaid-graph/Program.cs b/mermaid-graph/Program.cs index d94a14e..4a52f4d 100644 --- a/mermaid-graph/Program.cs +++ b/mermaid-graph/Program.cs @@ -1,4 +1,6 @@ -namespace MermaidGraph; +using MermaidGraph.Diagrams; + +namespace MermaidGraph; /// /// mermaid-graph.exe @@ -9,9 +11,9 @@ public sealed class Program /// Outputs a mermaid graph of the dependency diagram for a project, or whole solution. /// /// Full path to the solution (*.sln) or project (*.csproj) file that will be mapped. - /// The type of diagram to generate (e.g., Graph or Class). + /// The type of diagram to generate (e.g., Graph or Class). /// HResult - public static int Main(string? path, DiagramType diagramType = DiagramType.Graph) + public static int Main(string? path, DiagramType type = DiagramType.Graph) { if (path is null) { @@ -32,13 +34,13 @@ public static int Main(string? path, DiagramType diagramType = DiagramType.Graph { if (path.EndsWith(".csproj")) { - Console.WriteLine(Commands.Project(file, diagramType)); + Console.WriteLine(Commands.Project(file, type)); return 0; } if (path.EndsWith(".sln")) { - Console.WriteLine(Commands.Solution(file, diagramType)); + Console.WriteLine(Commands.Solution(file, type)); return 0; } } diff --git a/mermaid-graphTests/CommandsTests.cs b/mermaid-graphTests/CommandsTests.cs index e12c95e..b1d16fc 100644 --- a/mermaid-graphTests/CommandsTests.cs +++ b/mermaid-graphTests/CommandsTests.cs @@ -55,7 +55,7 @@ public void DogFoodSolutionTest(DiagramType type, string typeName) [Test] [TestCaseSource(nameof(DiagramTypeTestCases))] - public void DogFoodProjectTestAsync(DiagramType type, string typeName) + public void DogFoodProjectTest(DiagramType type, string typeName) { var filePath = FindFileDownTree("*.csproj"); Assert.That(filePath, Is.Not.Null); @@ -100,11 +100,11 @@ public void CommandLineFailTests(string? file, int hResult) private static string ExtractMermaid(string? markup) { - Assert.That(markup, Does.StartWith(Diagram.MermaidBegin)); - markup = markup.Substring(Diagram.MermaidBegin.Length + Environment.NewLine.Length); + Assert.That(markup, Does.StartWith(MermaidDiagram.MermaidBegin)); + markup = markup.Substring(MermaidDiagram.MermaidBegin.Length + Environment.NewLine.Length); - Assert.That(markup, Does.EndWith(Diagram.Fence + Environment.NewLine)); - return markup.Substring(0, markup.Length - Diagram.MermaidBegin.Length + Environment.NewLine.Length); + Assert.That(markup, Does.EndWith(MermaidDiagram.Fence + Environment.NewLine)); + return markup.Substring(0, markup.Length - MermaidDiagram.MermaidBegin.Length + Environment.NewLine.Length); } private string? DetectType(string markup) From 095e14ddaf5174048f75221c2b26171e81da0aca Mon Sep 17 00:00:00 2001 From: Adam Kauffman <26984068+A9G-Data-Droid@users.noreply.github.com> Date: Mon, 28 Apr 2025 19:55:05 -0700 Subject: [PATCH 4/5] Version bump, 1.0.0 RC --- mermaid-graph/mermaid-graph.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mermaid-graph/mermaid-graph.csproj b/mermaid-graph/mermaid-graph.csproj index 2e7fa9a..aead51d 100644 --- a/mermaid-graph/mermaid-graph.csproj +++ b/mermaid-graph/mermaid-graph.csproj @@ -26,7 +26,7 @@ True mermaid-graph.png mermaid-graph.ico - 1.0.0-beta + 1.0.0 LICENSE true From 9f4babca15cbf73c4fa4ff4c0d04dfdf037cd3a3 Mon Sep 17 00:00:00 2001 From: Adam Kauffman <26984068+A9G-Data-Droid@users.noreply.github.com> Date: Mon, 28 Apr 2025 21:43:46 -0700 Subject: [PATCH 5/5] GraphDiagram creates correct flowchart --- mermaid-graph/Diagrams/GraphDiagram.cs | 29 ++++---------------------- mermaid-graphTests/CommandsTests.cs | 1 - 2 files changed, 4 insertions(+), 26 deletions(-) diff --git a/mermaid-graph/Diagrams/GraphDiagram.cs b/mermaid-graph/Diagrams/GraphDiagram.cs index 44f0a49..809a36a 100644 --- a/mermaid-graph/Diagrams/GraphDiagram.cs +++ b/mermaid-graph/Diagrams/GraphDiagram.cs @@ -38,15 +38,10 @@ public override string Solution(FileInfo file) Header(file.Name); var solutionFile = SolutionFile.Parse(file.FullName); var solutionName = Path.GetFileNameWithoutExtension(file.Name); - var solutionId = $"{solutionName}"; - Graph.AppendLine($$""" - class {{solutionName}}{ - type solution - } - """); + var solutionId = $"s{solutionFile.GetHashCode()}({solutionName})"; using var projectCollection = new ProjectCollection(); - + foreach (var project in solutionFile.ProjectsInOrder) { if (project.ProjectType != SolutionProjectType.KnownToBeMSBuildFormat) continue; @@ -72,34 +67,18 @@ type solution private void GraphProject(Project project) { var projectName = Path.GetFileNameWithoutExtension(project.FullPath); - var type = project.GetPropertyValue("OutputType"); - var targetFramework = project.GetPropertyValue("TargetFramework") ?? project.GetPropertyValue("TargetFrameworks"); - Graph.AppendLine($$""" - class {{projectName}}{ - type {{type}} - target {{targetFramework}} - } - """); foreach (var item in project.GetItems("ProjectReference")) { var refPath = item.EvaluatedInclude; var refName = Path.GetFileNameWithoutExtension(refPath); - Graph.AppendLine($" {projectName} ..> {refName}"); + Graph.AppendLine($" {projectName} --> {refName}"); } foreach (var item in project.GetItems("PackageReference")) { var packageName = item.EvaluatedInclude; - var version = item.GetMetadataValue("Version"); - Graph.AppendLine($$""" - class {{packageName}}{ - type NuGet - version {{version}} - } - """); - - Graph.AppendLine($" {projectName} ..> {packageName}"); + Graph.AppendLine($" {projectName} -->|NuGet| {packageName}"); } } } \ No newline at end of file diff --git a/mermaid-graphTests/CommandsTests.cs b/mermaid-graphTests/CommandsTests.cs index b1d16fc..1ec7da0 100644 --- a/mermaid-graphTests/CommandsTests.cs +++ b/mermaid-graphTests/CommandsTests.cs @@ -25,7 +25,6 @@ private V8ScriptEngine Js { internal static readonly object[] DiagramTypeTestCases = [ - new object[] { DiagramType.Class, "class" }, new object[] { DiagramType.Class, "class" }, new object[] { DiagramType.Graph, "flowchart" } ];