Skip to content

Commit

Permalink
Add support for Mermaid dependency graphs
Browse files Browse the repository at this point in the history
  • Loading branch information
0xced committed Mar 12, 2024
1 parent 8da06a8 commit 721db54
Show file tree
Hide file tree
Showing 14 changed files with 461 additions and 239 deletions.
6 changes: 6 additions & 0 deletions src/Chisel/Chisel.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -86,4 +86,10 @@
<PackageReference Include="NuGet.ProjectModel" Version="6.9.1" PrivateAssets="all" />
</ItemGroup>

<ItemGroup>
<Compile Update="GraphWriter.*.cs">
<DependentUpon>GraphWriter.cs</DependentUpon>
</Compile>
</ItemGroup>

</Project>
3 changes: 2 additions & 1 deletion src/Chisel/ChiselTask.cs
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,8 @@ public override bool Execute()
}
using var graphStream = new FileStream(graphPath, FileMode.Create);
using var writer = new StreamWriter(graphStream);
graph.Write(writer, graphDirection);
var graphWriter = Path.GetExtension(Graph) is ".mmd" or ".mermaid" ? GraphWriter.Mermaid(writer) : GraphWriter.Graphviz(writer);
graphWriter.Write(graph, graphDirection, writeIgnoredPackages: false);
GraphPath = [ new TaskItem(graphPath) ];
}
catch (Exception exception)
Expand Down
51 changes: 2 additions & 49 deletions src/Chisel/DependencyGraph.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using NuGet.ProjectModel;

Expand Down Expand Up @@ -157,53 +156,7 @@ private void Restore(Package package, HashSet<Package> excludePackages)
}
}

public void Write(TextWriter writer, GraphDirection graphDirection = GraphDirection.LeftToRight, bool writeIgnoredPackages = false)
{
bool FilterIgnored(Package package) => writeIgnoredPackages || package.State != PackageState.Ignore;

writer.WriteLine("# Generated by https://github.com/0xced/Chisel");
writer.WriteLine("digraph");
writer.WriteLine("{");

if (graphDirection == GraphDirection.LeftToRight)
writer.WriteLine(" rankdir=LR");
else if (graphDirection == GraphDirection.TopToBottom)
writer.WriteLine(" rankdir=TB");
public IEnumerable<Package> Packages => _reverseGraph.Keys;

writer.WriteLine(" node [ fontname = \"Segoe UI, sans-serif\", shape = box, style = filled, color = aquamarine ]");
writer.WriteLine();

foreach (var package in _reverseGraph.Keys.Where(FilterIgnored).OrderBy(e => e.Id))
{
writer.Write($" \"{package.Id}\"");
if (package.State == PackageState.Ignore)
{
writer.Write(" [ color = lightgray ]");
}
else if (package.State == PackageState.Remove)
{
writer.Write(" [ color = lightcoral ]");
}
else if (package.Type == PackageType.Project)
{
writer.Write(" [ color = skyblue ]");
}
else if (package.Type == PackageType.Unknown)
{
writer.Write(" [ color = khaki ]");
}
writer.WriteLine();
}
writer.WriteLine();

foreach (var (package, dependencies) in _graph.Select(e => (e.Key, e.Value)).Where(e => FilterIgnored(e.Key)).OrderBy(e => e.Key.Id))
{
foreach (var dependency in dependencies.Where(FilterIgnored).OrderBy(e => e.Id))
{
writer.WriteLine($" \"{package.Id}\" -> \"{dependency.Id}\"");
}
}

writer.WriteLine("}");
}
public IReadOnlyDictionary<Package, HashSet<Package>> Dependencies => _graph;
}
59 changes: 59 additions & 0 deletions src/Chisel/GraphWriter.Graphviz.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
using System.IO;

namespace Chisel;

internal sealed class GraphvizWriter : GraphWriter
{
public GraphvizWriter(TextWriter writer) : base(writer)
{
}

protected override void WriteHeader(GraphDirection graphDirection)
{
Writer.WriteLine("# Generated by https://github.com/0xced/Chisel");
Writer.WriteLine();
Writer.WriteLine("digraph");
Writer.WriteLine("{");

if (graphDirection == GraphDirection.LeftToRight)
Writer.WriteLine(" rankdir=LR");
else if (graphDirection == GraphDirection.TopToBottom)
Writer.WriteLine(" rankdir=TB");

Writer.WriteLine(" node [ fontname = \"Segoe UI, sans-serif\", shape = box, style = filled, color = aquamarine ]");
Writer.WriteLine();
}

protected override void WriteFooter()
{
Writer.WriteLine("}");
}

protected override void WriteNode(Package package)
{
Writer.Write($" \"{package.Id}\"");
if (package.State == PackageState.Ignore)
{
Writer.Write(" [ color = lightgray ]");
}
else if (package.State == PackageState.Remove)
{
Writer.Write(" [ color = lightcoral ]");
}
else if (package.Type == PackageType.Project)
{
Writer.Write(" [ color = skyblue ]");
}
else if (package.Type == PackageType.Unknown)
{
Writer.Write(" [ color = khaki ]");
}

Writer.WriteLine();
}

protected override void WriteEdge(Package package, Package dependency)
{
Writer.WriteLine($" \"{package.Id}\" -> \"{dependency.Id}\"");
}
}
64 changes: 64 additions & 0 deletions src/Chisel/GraphWriter.Mermaid.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
using System.IO;

namespace Chisel;

internal sealed class MermaidWriter : GraphWriter
{
public MermaidWriter(TextWriter writer) : base(writer)
{
}

protected override void WriteHeader(GraphDirection graphDirection)
{
Writer.WriteLine("%% Generated by https://github.com/0xced/Chisel");
Writer.WriteLine();
Writer.Write("graph");
if (graphDirection == GraphDirection.LeftToRight)
Writer.Write(" LR");
else if (graphDirection == GraphDirection.TopToBottom)
Writer.Write(" TB");
Writer.WriteLine();

Writer.WriteLine();
Writer.WriteLine("classDef default fill:aquamarine,stroke:aquamarine");
Writer.WriteLine("classDef project fill:skyblue,stroke:skyblue");
Writer.WriteLine("classDef unknown fill:khaki,stroke:khaki");
Writer.WriteLine("classDef ignored fill:lightgray,stroke:lightgray");
Writer.WriteLine("classDef removed fill:lightcoral,stroke:lightcoral");
Writer.WriteLine();
}

protected override void WriteFooter()
{
}

protected override void WriteNode(Package package)
{
Writer.Write($"class {package.Id} ");
if (package.State == PackageState.Ignore)
{
Writer.WriteLine("ignored");
}
else if (package.State == PackageState.Remove)
{
Writer.WriteLine("removed");
}
else if (package.Type == PackageType.Unknown)
{
Writer.WriteLine("unknown");
}
else if (package.Type == PackageType.Project)
{
Writer.WriteLine("project");
}
else
{
Writer.WriteLine("default");
}
}

protected override void WriteEdge(Package package, Package dependency)
{
Writer.WriteLine($"{package.Id} --> {dependency.Id}");
}
}
50 changes: 50 additions & 0 deletions src/Chisel/GraphWriter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
using System.IO;
using System.Linq;

namespace Chisel;

internal abstract class GraphWriter
{
public static GraphWriter Graphviz(TextWriter writer) => new GraphvizWriter(writer);

public static GraphWriter Mermaid(TextWriter writer) => new MermaidWriter(writer);

protected readonly TextWriter Writer;

protected GraphWriter(TextWriter writer) => Writer = writer;

public void Write(DependencyGraph graph, GraphDirection graphDirection, bool writeIgnoredPackages)
{
WriteHeader(graphDirection);
WriteEdges(graph, writeIgnoredPackages);
Writer.WriteLine();
WriteNodes(graph, writeIgnoredPackages);
WriteFooter();
}

protected abstract void WriteHeader(GraphDirection graphDirection);
protected abstract void WriteFooter();
protected abstract void WriteNode(Package package);
protected abstract void WriteEdge(Package package, Package dependency);

private static bool FilterIgnored(Package package, bool writeIgnoredPackages) => writeIgnoredPackages || package.State != PackageState.Ignore;

private void WriteNodes(DependencyGraph graph, bool writeIgnoredPackages)
{
foreach (var package in graph.Packages.Where(e => FilterIgnored(e, writeIgnoredPackages)).OrderBy(e => e.Id))
{
WriteNode(package);
}
}

private void WriteEdges(DependencyGraph graph, bool writeIgnoredPackages)
{
foreach (var (package, dependencies) in graph.Dependencies.Select(e => (e.Key, e.Value)).Where(e => FilterIgnored(e.Key, writeIgnoredPackages)).OrderBy(e => e.Key.Id))
{
foreach (var dependency in dependencies.Where(e => FilterIgnored(e, writeIgnoredPackages)).OrderBy(e => e.Id))
{
WriteEdge(package, dependency);
}
}
}
}
2 changes: 1 addition & 1 deletion tests/Chisel.Tests/Chisel.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
<PackageReference Include="PublicApiGenerator" Version="11.1.0" />
<PackageReference Include="System.Text.Json" Version="8.0.2" />
<PackageReference Include="Verify.Xunit" Version="23.4.0" />
<PackageReference Include="Verify.Xunit" Version="23.5.0" />
<PackageReference Include="xunit" Version="2.7.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.7" PrivateAssets="all" />
</ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,38 +1,10 @@
# Generated by https://github.com/0xced/Chisel

digraph
{
rankdir=LR
node [ fontname = "Segoe UI, sans-serif", shape = box, style = filled, color = aquamarine ]

"Azure.Core/1.35.0" [ color = lightcoral ]
"Azure.Identity/1.10.3" [ color = lightcoral ]
"Microsoft.Bcl.AsyncInterfaces/1.1.1" [ color = lightcoral ]
"Microsoft.Data.SqlClient/5.2.0"
"Microsoft.Extensions.DependencyModel/8.0.0"
"Microsoft.Identity.Client.Extensions.Msal/4.56.0" [ color = lightcoral ]
"Microsoft.Identity.Client/4.56.0"
"Microsoft.IdentityModel.Abstractions/6.35.0"
"Microsoft.IdentityModel.JsonWebTokens/6.35.0" [ color = lightcoral ]
"Microsoft.IdentityModel.Logging/6.35.0" [ color = lightcoral ]
"Microsoft.IdentityModel.Protocols.OpenIdConnect/6.35.0" [ color = lightcoral ]
"Microsoft.IdentityModel.Protocols/6.35.0" [ color = lightcoral ]
"Microsoft.IdentityModel.Tokens/6.35.0" [ color = lightcoral ]
"Microsoft.SqlServer.Server/1.0.0"
"System.Configuration.ConfigurationManager/8.0.0"
"System.Diagnostics.DiagnosticSource/6.0.1" [ color = lightcoral ]
"System.Diagnostics.EventLog/8.0.0"
"System.IdentityModel.Tokens.Jwt/6.35.0" [ color = lightcoral ]
"System.IO.FileSystem.AccessControl/5.0.0" [ color = lightcoral ]
"System.Memory.Data/1.0.2" [ color = lightcoral ]
"System.Runtime.Caching/8.0.0"
"System.Runtime.CompilerServices.Unsafe/6.0.0" [ color = lightcoral ]
"System.Security.AccessControl/5.0.0" [ color = lightcoral ]
"System.Security.Cryptography.Cng/4.5.0" [ color = lightcoral ]
"System.Security.Cryptography.ProtectedData/8.0.0"
"System.Security.Principal.Windows/5.0.0" [ color = lightcoral ]
"System.Text.Encodings.Web/8.0.0"
"System.Text.Json/8.0.0"

"Azure.Core/1.35.0" -> "Microsoft.Bcl.AsyncInterfaces/1.1.1"
"Azure.Core/1.35.0" -> "System.Diagnostics.DiagnosticSource/6.0.1"
"Azure.Core/1.35.0" -> "System.Memory.Data/1.0.2"
Expand Down Expand Up @@ -78,4 +50,32 @@ digraph
"System.Runtime.Caching/8.0.0" -> "System.Configuration.ConfigurationManager/8.0.0"
"System.Security.AccessControl/5.0.0" -> "System.Security.Principal.Windows/5.0.0"
"System.Text.Json/8.0.0" -> "System.Text.Encodings.Web/8.0.0"

"Azure.Core/1.35.0" [ color = lightcoral ]
"Azure.Identity/1.10.3" [ color = lightcoral ]
"Microsoft.Bcl.AsyncInterfaces/1.1.1" [ color = lightcoral ]
"Microsoft.Data.SqlClient/5.2.0"
"Microsoft.Extensions.DependencyModel/8.0.0"
"Microsoft.Identity.Client.Extensions.Msal/4.56.0" [ color = lightcoral ]
"Microsoft.Identity.Client/4.56.0"
"Microsoft.IdentityModel.Abstractions/6.35.0"
"Microsoft.IdentityModel.JsonWebTokens/6.35.0" [ color = lightcoral ]
"Microsoft.IdentityModel.Logging/6.35.0" [ color = lightcoral ]
"Microsoft.IdentityModel.Protocols.OpenIdConnect/6.35.0" [ color = lightcoral ]
"Microsoft.IdentityModel.Protocols/6.35.0" [ color = lightcoral ]
"Microsoft.IdentityModel.Tokens/6.35.0" [ color = lightcoral ]
"Microsoft.SqlServer.Server/1.0.0"
"System.Configuration.ConfigurationManager/8.0.0"
"System.Diagnostics.DiagnosticSource/6.0.1" [ color = lightcoral ]
"System.Diagnostics.EventLog/8.0.0"
"System.IdentityModel.Tokens.Jwt/6.35.0" [ color = lightcoral ]
"System.IO.FileSystem.AccessControl/5.0.0" [ color = lightcoral ]
"System.Memory.Data/1.0.2" [ color = lightcoral ]
"System.Runtime.Caching/8.0.0"
"System.Runtime.CompilerServices.Unsafe/6.0.0" [ color = lightcoral ]
"System.Security.AccessControl/5.0.0" [ color = lightcoral ]
"System.Security.Cryptography.Cng/4.5.0" [ color = lightcoral ]
"System.Security.Cryptography.ProtectedData/8.0.0"
"System.Security.Principal.Windows/5.0.0" [ color = lightcoral ]
"System.Text.Encodings.Web/8.0.0"
}
Loading

0 comments on commit 721db54

Please sign in to comment.