diff --git a/src/AbpHelper.Core/AbpHelperCoreConsts.cs b/src/AbpHelper.Core/AbpHelperCoreConsts.cs new file mode 100644 index 00000000..c91d7663 --- /dev/null +++ b/src/AbpHelper.Core/AbpHelperCoreConsts.cs @@ -0,0 +1,7 @@ +namespace EasyAbp.AbpHelper.Core +{ + internal static class AbpHelperCoreConsts + { + internal static readonly string TemplateResourcePathPrefix = "/Templates/"; + } +} diff --git a/src/AbpHelper.Core/Commands/CommandOptionsBase.cs b/src/AbpHelper.Core/Commands/CommandOptionsBase.cs index 89d89233..5621b939 100644 --- a/src/AbpHelper.Core/Commands/CommandOptionsBase.cs +++ b/src/AbpHelper.Core/Commands/CommandOptionsBase.cs @@ -1,5 +1,7 @@ using System; +using System.IO; using EasyAbp.AbpHelper.Core.Attributes; +using EasyAbp.AbpHelper.Core.Extensions; namespace EasyAbp.AbpHelper.Core.Commands { @@ -14,5 +16,26 @@ public abstract class CommandOptionsBase [Option("exclude", Description = "Exclude directories when searching files, arguments can contain a combination of valid literal path and wildcard (* and ?) characters. Use double asterisk(**) to search all directories. Example: --exclude *Folder1 Folder2/Folder* **/*Folder? **/*Folder*")] public virtual string[] Exclude { get; set; } = Array.Empty(); + + [Option('t', "template-path", Description = "Prioritize loading template files in the path")] + public virtual string? TemplatePath { get; set; } + + public virtual string MapTemplatePath(string subPath) + { + string resourcePathPrefix = AbpHelperCoreConsts.TemplateResourcePathPrefix; + + if (string.IsNullOrWhiteSpace(TemplatePath)) + { + return Path.Combine(new[] { resourcePathPrefix, subPath }).NormalizePath(); + } + + if (AbpHelperCoreConsts.TemplateResourcePathPrefix.StartsWith('/') || + AbpHelperCoreConsts.TemplateResourcePathPrefix.StartsWith('\\')) + { + resourcePathPrefix = resourcePathPrefix.Substring(1); + } + + return Path.Combine(new[] { TemplatePath!, resourcePathPrefix, subPath }).NormalizePath(); + } } } \ No newline at end of file diff --git a/src/AbpHelper.Core/Commands/Generate/Controller/ControllerCommand.cs b/src/AbpHelper.Core/Commands/Generate/Controller/ControllerCommand.cs index e23f5776..541f6386 100644 --- a/src/AbpHelper.Core/Commands/Generate/Controller/ControllerCommand.cs +++ b/src/AbpHelper.Core/Commands/Generate/Controller/ControllerCommand.cs @@ -33,7 +33,7 @@ protected override IActivityBuilder ConfigureBuild(ControllerCommandOption optio step => { step.VariableName = VariableNames.TemplateDirectory; - step.ValueExpression = new LiteralExpression("/Templates/Controller"); + step.ValueExpression = new LiteralExpression(option.MapTemplatePath("Controller")); }) .Then( step => step.ConditionExpression = new JavaScriptExpression($"{OptionVariableName}.{nameof(ControllerCommandOption.SkipBuild)}"), diff --git a/src/AbpHelper.Core/Commands/Generate/Crud/CrudCommand.cs b/src/AbpHelper.Core/Commands/Generate/Crud/CrudCommand.cs index 68025ec8..2a26d1d8 100644 --- a/src/AbpHelper.Core/Commands/Generate/Crud/CrudCommand.cs +++ b/src/AbpHelper.Core/Commands/Generate/Crud/CrudCommand.cs @@ -35,7 +35,7 @@ protected override IActivityBuilder ConfigureBuild(CrudCommandOption option, IAc step => { step.VariableName = VariableNames.TemplateDirectory; - step.ValueExpression = new LiteralExpression("/Templates/Crud"); + step.ValueExpression = new LiteralExpression(option.MapTemplatePath("Crud")); }) .Then( step => { step.SearchFileName = new LiteralExpression(entityFileName); }) diff --git a/src/AbpHelper.Core/Commands/Generate/Localization/LocalizationCommand.cs b/src/AbpHelper.Core/Commands/Generate/Localization/LocalizationCommand.cs index 8f922dd8..7f17cef5 100644 --- a/src/AbpHelper.Core/Commands/Generate/Localization/LocalizationCommand.cs +++ b/src/AbpHelper.Core/Commands/Generate/Localization/LocalizationCommand.cs @@ -28,7 +28,7 @@ protected override IActivityBuilder ConfigureBuild(LocalizationCommandOption opt step => { step.VariableName = VariableNames.TemplateDirectory; - step.ValueExpression = new LiteralExpression("/Templates/Localization"); + step.ValueExpression = new LiteralExpression(option.MapTemplatePath("Localization")); }) .Then() /* Add localization */ diff --git a/src/AbpHelper.Core/Commands/Generate/Methods/MethodsCommand.cs b/src/AbpHelper.Core/Commands/Generate/Methods/MethodsCommand.cs index a732424c..5c3824ac 100644 --- a/src/AbpHelper.Core/Commands/Generate/Methods/MethodsCommand.cs +++ b/src/AbpHelper.Core/Commands/Generate/Methods/MethodsCommand.cs @@ -44,7 +44,7 @@ protected override IActivityBuilder ConfigureBuild(MethodsCommandOption option, step => { step.VariableName = VariableNames.TemplateDirectory; - step.ValueExpression = new LiteralExpression("/Templates/Methods"); + step.ValueExpression = new LiteralExpression(option.MapTemplatePath("Methods")); }) .Then( step => diff --git a/src/AbpHelper.Core/Commands/Generate/Service/ServiceCommand.cs b/src/AbpHelper.Core/Commands/Generate/Service/ServiceCommand.cs index 4b1da57b..f4f6d5fe 100644 --- a/src/AbpHelper.Core/Commands/Generate/Service/ServiceCommand.cs +++ b/src/AbpHelper.Core/Commands/Generate/Service/ServiceCommand.cs @@ -38,7 +38,7 @@ protected override IActivityBuilder ConfigureBuild(ServiceCommandOption option, step => { step.VariableName = VariableNames.TemplateDirectory; - step.ValueExpression = new LiteralExpression("/Templates/Service"); + step.ValueExpression = new LiteralExpression(option.MapTemplatePath("Service")); }) .Then() .Then( diff --git a/src/AbpHelper.Core/Commands/Module/Add/AddCommand.cs b/src/AbpHelper.Core/Commands/Module/Add/AddCommand.cs index a18d09e8..1a0d31fa 100644 --- a/src/AbpHelper.Core/Commands/Module/Add/AddCommand.cs +++ b/src/AbpHelper.Core/Commands/Module/Add/AddCommand.cs @@ -74,7 +74,7 @@ protected override IActivityBuilder ConfigureBuild(AddCommandOption option, IAct step => { step.VariableName = VariableNames.TemplateDirectory; - step.ValueExpression = new LiteralExpression("/Templates/Module"); + step.ValueExpression = new LiteralExpression(option.MapTemplatePath("Module")); }) .Then( step => diff --git a/src/AbpHelper.Core/Commands/Module/Remove/RemoveCommand.cs b/src/AbpHelper.Core/Commands/Module/Remove/RemoveCommand.cs index eba30193..bee54364 100644 --- a/src/AbpHelper.Core/Commands/Module/Remove/RemoveCommand.cs +++ b/src/AbpHelper.Core/Commands/Module/Remove/RemoveCommand.cs @@ -69,7 +69,7 @@ protected override IActivityBuilder ConfigureBuild(RemoveCommandOption option, I step => { step.VariableName = VariableNames.TemplateDirectory; - step.ValueExpression = new LiteralExpression("/Templates/Module"); + step.ValueExpression = new LiteralExpression(option.MapTemplatePath("Module")); }) .Then( step => diff --git a/src/AbpHelper.Core/Extensions/FileProviderExtensions.cs b/src/AbpHelper.Core/Extensions/FileProviderExtensions.cs index 0dbd759e..3b25bd35 100644 --- a/src/AbpHelper.Core/Extensions/FileProviderExtensions.cs +++ b/src/AbpHelper.Core/Extensions/FileProviderExtensions.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; using Microsoft.Extensions.FileProviders; +using System.Linq; +using Volo.Abp.VirtualFileSystem; namespace EasyAbp.AbpHelper.Core.Extensions { @@ -26,5 +28,56 @@ public static class FileProviderExtensions } } } + + public static IEnumerable<(string, IFileInfo)> GetFilesRecursively(this IVirtualFileProvider fileProvider, string dir) + { + var (virtualDirectory, physicalDirectory) = fileProvider.GetTemplateRootDirectoryMirror(dir); + var files = GetFilesRecursively((IFileProvider)fileProvider, virtualDirectory); + if (physicalDirectory == null) + { + return files; + } + var physicalFiles = new PhysicalFileProvider(physicalDirectory).GetFilesRecursively("").ToList(); + var virtualFiles = files.ToList(); + var physicalPaths = new HashSet(physicalFiles.Select(p => p.Item1)); + return physicalFiles.Concat(virtualFiles.Where(v => !physicalPaths.Contains(v.Item1))); + } + + /// + /// If is physical path try to get virtual directory path + /// + /// + /// (virtualDirectory, physicalDirectory) + public static (string, string?) GetTemplateRootDirectoryMirror(this IVirtualFileProvider fileProvider, string dir) + { + if (dir.StartsWith(AbpHelperCoreConsts.TemplateResourcePathPrefix)) return (dir, null); + + var index = dir.IndexOf(AbpHelperCoreConsts.TemplateResourcePathPrefix); + + if (index == -1) return (dir, null); + + var virtualDirectory = dir.Substring(index); + var physicalDirectory = dir.Substring(0, index); + + return (virtualDirectory, physicalDirectory); + } + + /// + /// If is physical path try to get virtual directory path + /// + /// + /// (virtualDirectory, physicalDirectory) + public static (string, string?) GetTemplatePathMirror(this IVirtualFileProvider fileProvider, string dir) + { + if (dir.StartsWith(AbpHelperCoreConsts.TemplateResourcePathPrefix)) return (dir, null); + + var index = dir.IndexOf(AbpHelperCoreConsts.TemplateResourcePathPrefix); + + if (index == -1) return (dir, null); + + var virtualPath = dir.Substring(index); + + return (virtualPath, dir); + } } } \ No newline at end of file diff --git a/src/AbpHelper.Core/Generator/TemplateLoader.cs b/src/AbpHelper.Core/Generator/TemplateLoader.cs index 04eda51a..3a498554 100644 --- a/src/AbpHelper.Core/Generator/TemplateLoader.cs +++ b/src/AbpHelper.Core/Generator/TemplateLoader.cs @@ -1,4 +1,6 @@ -using System.Threading.Tasks; +using System.IO; +using System.Threading.Tasks; +using EasyAbp.AbpHelper.Core.Extensions; using Microsoft.Extensions.FileProviders; using Scriban; using Scriban.Parsing; @@ -24,12 +26,22 @@ public string GetPath(TemplateContext context, SourceSpan callerSpan, string tem public string Load(TemplateContext context, SourceSpan callerSpan, string templatePath) { - return _virtualFileProvider.GetFileInfo(templatePath).ReadAsString(); + var (v, p) = _virtualFileProvider.GetTemplatePathMirror(templatePath); + if (p != null && File.Exists(p)) + { + return File.ReadAllText(p); + } + return _virtualFileProvider.GetFileInfo(v).ReadAsString(); } public async ValueTask LoadAsync(TemplateContext context, SourceSpan callerSpan, string templatePath) { - return await _virtualFileProvider.GetFileInfo(templatePath).ReadAsStringAsync(); + var (v, p) = _virtualFileProvider.GetTemplatePathMirror(templatePath); + if (p != null && File.Exists(p)) + { + return await File.ReadAllTextAsync(p); + } + return await _virtualFileProvider.GetFileInfo(v).ReadAsStringAsync(); } } } \ No newline at end of file diff --git a/src/AbpHelper.Core/Generator/TextGenerator.cs b/src/AbpHelper.Core/Generator/TextGenerator.cs index 68ad925c..c7f88644 100644 --- a/src/AbpHelper.Core/Generator/TextGenerator.cs +++ b/src/AbpHelper.Core/Generator/TextGenerator.cs @@ -26,12 +26,22 @@ public string GenerateByTemplateName(string templateDirectory, string templateNa { return GenerateByTemplateName(templateDirectory, templateName, model, out _); } - + public string GenerateByTemplateName(string templateDirectory, string templateName, object model, out TemplateContext context) { string path = Path.Combine(templateDirectory, templateName).NormalizePath(); - var templateFile = _virtualFileProvider.GetFileInfo(path); - var templateText = templateFile.ReadAsString(); + var templateText = string.Empty; + if (File.Exists(path)) + { + templateText = File.ReadAllText(path); + } + else + { + var (virtualPath, _) = _virtualFileProvider.GetTemplateRootDirectoryMirror(path); + + var templateFile = _virtualFileProvider.GetFileInfo(virtualPath); + templateText = templateFile.ReadAsString(); + } return GenerateByTemplateText(templateText, model, out context); } diff --git a/src/AbpHelper.Core/Steps/Common/GroupGenerationStep.cs b/src/AbpHelper.Core/Steps/Common/GroupGenerationStep.cs index 60d764a7..e3b2c640 100644 --- a/src/AbpHelper.Core/Steps/Common/GroupGenerationStep.cs +++ b/src/AbpHelper.Core/Steps/Common/GroupGenerationStep.cs @@ -27,7 +27,7 @@ public WorkflowExpression TemplateDirectory get => GetState>(() => new JavaScriptExpression(VariableNames.TemplateDirectory)); set => SetState(value); } - + public string GroupName { get => GetState(); @@ -80,10 +80,12 @@ protected override async Task OnExecuteAsync(WorkflowEx private async Task GenerateFile(string groupDirectory, string targetDirectory, object model, bool overwrite) { + var (virtualDirectory, physicalDirectory) = _virtualFileProvider.GetTemplateRootDirectoryMirror(groupDirectory); + foreach (var (path, file) in _virtualFileProvider.GetFilesRecursively(groupDirectory)) { Logger.LogDebug($"Generating using template file: {path}"); - var targetFilePathNameTemplate = path.Replace(groupDirectory, targetDirectory); + var targetFilePathNameTemplate = path.Replace(virtualDirectory, targetDirectory); var targetFilePathName = _textGenerator.GenerateByTemplateText(targetFilePathNameTemplate, model); if (File.Exists(targetFilePathName) && !overwrite) {