Files
C4IT-F4SD-M42WebApi/F4SDM42WebApi/M42ExtensionScaffolder/Solutions/Matrix42.Extensions.Scaffolder.Vsix/Wizard/ExtensionProjectsWizard.cs
2026-06-26 11:31:03 +02:00

231 lines
5.9 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using EnvDTE;
using EnvDTE80;
using Microsoft.VisualStudio.TemplateWizard;
namespace Matrix42.Extensions.Scaffolder.Wizard
{
public class ExtensionProjectsWizard : IWizard
{
private DTE2 _dte;
private Project _placeholderProject;
private string _extensionNamespace = string.Empty;
private List<ProjectDefinition> _projects = new List<ProjectDefinition>();
private string _solutionDirectory = string.Empty;
public void RunStarted(
object automationObject,
Dictionary<string, string> replacementsDictionary,
WizardRunKind runKind,
object[] customParams)
{
_dte = automationObject as DTE2
?? throw new InvalidOperationException("DTE2 not available.");
using (var form = new WizardForm())
{
var result = form.ShowDialog();
if (result != System.Windows.Forms.DialogResult.OK)
throw new WizardBackoutException();
_extensionNamespace = form.ExtensionNamespace;
_projects = new List<ProjectDefinition>(form.Projects);
}
}
// 🔹 This is the method that was missing
public void BeforeOpeningFile(ProjectItem projectItem)
{
// We don't need to do anything before a file is opened.
// Leave empty on purpose.
}
public void ProjectFinishedGenerating(Project project)
{
_placeholderProject = project;
}
public void ProjectItemFinishedGenerating(ProjectItem projectItem)
{
}
public void RunFinished()
{
var solution = _dte.Solution;
_solutionDirectory = Path.GetDirectoryName(solution.FullName);
if (string.IsNullOrEmpty(_solutionDirectory))
return;
// Remove placeholder project from solution and disk
if (_placeholderProject != null)
{
var placeholderPath = _placeholderProject.FullName;
solution.Remove(_placeholderProject);
TryDeleteDirectorySafe(Path.GetDirectoryName(placeholderPath));
}
// Drop Directory.Build.props
var propsTemplatePath = Path.Combine(
GetExtensionInstallPath(),
"ProjectTemplates",
"Directory.Build.props.template");
if (File.Exists(propsTemplatePath))
{
var destProps = Path.Combine(_solutionDirectory, "Directory.Build.props");
if (!File.Exists(destProps))
File.Copy(propsTemplatePath, destProps, overwrite: false);
}
foreach (var proj in _projects)
{
CreateProjectFromDefinition(solution, proj);
}
}
public bool ShouldAddProjectItem(string filePath) => true;
private void CreateProjectFromDefinition(Solution solution, ProjectDefinition def)
{
var projectName = def.GetProjectName(_extensionNamespace);
var projectNamespace = def.GetNamespace(_extensionNamespace);
var projectDir = Path.Combine(_solutionDirectory, projectName);
Directory.CreateDirectory(projectDir);
var csprojPath = Path.Combine(projectDir, projectName + ".csproj");
var runtimeIds = def.GetRuntimeIdentifiers();
var tfm = "net8.0"; // default target framework
var csprojContent = GenerateCsproj(def.Type, tfm, runtimeIds, projectNamespace);
File.WriteAllText(csprojPath, csprojContent);
var codeFilePath = Path.Combine(
projectDir,
def.Type == ProjectType.WebApi ? "Program.cs" : "Class1.cs");
var codeContent = GenerateCodeFile(def.Type, projectNamespace);
File.WriteAllText(codeFilePath, codeContent);
solution.AddFromFile(csprojPath);
}
private string GenerateCsproj(
ProjectType type,
string tfm,
string runtimeIds,
string rootNamespace)
{
var outputType = type == ProjectType.WebApi ? "Exe" : "Library";
var sdk = type == ProjectType.WebApi ? "Microsoft.NET.Sdk.Web" : "Microsoft.NET.Sdk";
var ridBlock = string.IsNullOrWhiteSpace(runtimeIds)
? string.Empty
: $" <RuntimeIdentifiers>{runtimeIds}</RuntimeIdentifiers>{Environment.NewLine}";
return
$@"<Project Sdk=""{sdk}"">
<PropertyGroup>
<TargetFramework>{tfm}</TargetFramework>
<OutputType>{outputType}</OutputType>
<RootNamespace>{rootNamespace}</RootNamespace>
</PropertyGroup>
<PropertyGroup>
{ridBlock} </PropertyGroup>
<ItemGroup>
<PackageReference Include=""Matrix42.Extensions.SDK"" Version=""1.0.0"" />
</ItemGroup>
</Project>
";
}
private string GenerateCodeFile(ProjectType type, string ns)
{
switch (type)
{
case ProjectType.WebApi:
return
$@"using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Matrix42.Extensions.SDK;
namespace {ns}
{{
public class Program
{{
public static void Main(string[] args)
{{
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
// TODO: configure Matrix42.Extensions.SDK services
var app = builder.Build();
app.MapControllers();
app.Run();
}}
}}
}}";
case ProjectType.Contracts:
return
$@"namespace {ns}
{{
// Place your contracts (DTOs, interfaces) here
public class SampleContract
{{
public string Id {{ get; set; }} = string.Empty;
}}
}}";
case ProjectType.BizLogic:
return
$@"using Matrix42.Extensions.SDK;
namespace {ns}
{{
// Place your business logic services here
public class SampleService
{{
public string SayHello() => ""Hello from {ns}"";
}}
}}";
case ProjectType.Custom:
default:
return
$@"namespace {ns}
{{
// Custom project for your extension
public class CustomEntry
{{
public string Name {{ get; set; }} = ""Custom"";
}}
}}";
}
}
private string GetExtensionInstallPath()
{
var asmPath = typeof(ExtensionProjectsWizard).Assembly.Location;
return Path.GetDirectoryName(asmPath);
}
private void TryDeleteDirectorySafe(string path)
{
try
{
if (!string.IsNullOrWhiteSpace(path) && Directory.Exists(path))
Directory.Delete(path, recursive: true);
}
catch
{
// ignore
}
}
}
}