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" }
];