mirror of https://github.com/abpframework/abp.git
committed by
GitHub
8 changed files with 404 additions and 0 deletions
@ -0,0 +1,78 @@ |
|||||
|
using System; |
||||
|
using System.IO; |
||||
|
using System.Text; |
||||
|
using System.Threading.Tasks; |
||||
|
using Microsoft.Extensions.Logging; |
||||
|
using Microsoft.Extensions.Logging.Abstractions; |
||||
|
using Volo.Abp.Cli.Args; |
||||
|
using Volo.Abp.Cli.LIbs; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
|
||||
|
namespace Volo.Abp.Cli.Commands |
||||
|
{ |
||||
|
public class InstallLibsCommand : IConsoleCommand, ITransientDependency |
||||
|
{ |
||||
|
public ILogger<InstallLibsCommand> Logger { get; set; } |
||||
|
|
||||
|
protected IInstallLibsService InstallLibsService { get; } |
||||
|
|
||||
|
public InstallLibsCommand(IInstallLibsService installLibsService) |
||||
|
{ |
||||
|
InstallLibsService = installLibsService; |
||||
|
Logger = NullLogger<InstallLibsCommand>.Instance; |
||||
|
} |
||||
|
|
||||
|
public async Task ExecuteAsync(CommandLineArgs commandLineArgs) |
||||
|
{ |
||||
|
var workingDirectoryArg = commandLineArgs.Options.GetOrNull( |
||||
|
Options.WorkingDirectory.Short, |
||||
|
Options.WorkingDirectory.Long |
||||
|
); |
||||
|
|
||||
|
var workingDirectory = workingDirectoryArg ?? Directory.GetCurrentDirectory(); |
||||
|
|
||||
|
if (!Directory.Exists(workingDirectory)) |
||||
|
{ |
||||
|
throw new CliUsageException( |
||||
|
"Specified directory does not exist." + |
||||
|
Environment.NewLine + Environment.NewLine + |
||||
|
GetUsageInfo() |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
await InstallLibsService.InstallLibs(workingDirectory); |
||||
|
} |
||||
|
|
||||
|
public string GetUsageInfo() |
||||
|
{ |
||||
|
var sb = new StringBuilder(); |
||||
|
|
||||
|
sb.AppendLine(""); |
||||
|
sb.AppendLine("Usage:"); |
||||
|
sb.AppendLine(""); |
||||
|
sb.AppendLine(" abp install-libs"); |
||||
|
sb.AppendLine(""); |
||||
|
sb.AppendLine("Options:"); |
||||
|
sb.AppendLine(""); |
||||
|
sb.AppendLine("-wd|--working-directory <directory-path> (default: empty)"); |
||||
|
sb.AppendLine(""); |
||||
|
sb.AppendLine("See the documentation for more info: https://docs.abp.io/en/abp/latest/CLI"); |
||||
|
|
||||
|
return sb.ToString(); |
||||
|
} |
||||
|
|
||||
|
public string GetShortDescription() |
||||
|
{ |
||||
|
return "Install NPM Packages for MVC / Razor Pages and Blazor Server UI types."; |
||||
|
} |
||||
|
|
||||
|
public static class Options |
||||
|
{ |
||||
|
public static class WorkingDirectory |
||||
|
{ |
||||
|
public const string Short = "wd"; |
||||
|
public const string Long = "working-directory"; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,9 @@ |
|||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
namespace Volo.Abp.Cli.LIbs |
||||
|
{ |
||||
|
public interface IInstallLibsService |
||||
|
{ |
||||
|
Task InstallLibs(string directory); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,222 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.IO; |
||||
|
using System.Linq; |
||||
|
using System.Threading.Tasks; |
||||
|
using Microsoft.Extensions.FileSystemGlobbing; |
||||
|
using Microsoft.Extensions.FileSystemGlobbing.Abstractions; |
||||
|
using Microsoft.Extensions.Logging; |
||||
|
using Microsoft.Extensions.Logging.Abstractions; |
||||
|
using NuGet.Versioning; |
||||
|
using Volo.Abp.Cli.Utils; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
using Volo.Abp.Json; |
||||
|
|
||||
|
namespace Volo.Abp.Cli.LIbs |
||||
|
{ |
||||
|
public class InstallLibsService : IInstallLibsService, ITransientDependency |
||||
|
{ |
||||
|
public ILogger<InstallLibsService> Logger { get; set; } |
||||
|
|
||||
|
private readonly IJsonSerializer _jsonSerializer; |
||||
|
|
||||
|
public InstallLibsService(IJsonSerializer jsonSerializer) |
||||
|
{ |
||||
|
_jsonSerializer = jsonSerializer; |
||||
|
Logger = NullLogger<InstallLibsService>.Instance; |
||||
|
} |
||||
|
|
||||
|
public async Task InstallLibs(string directory) |
||||
|
{ |
||||
|
var projectFiles = Directory.GetFiles(directory, "*.csproj"); |
||||
|
if (!projectFiles.Any()) |
||||
|
{ |
||||
|
Logger.LogError("No project file found in the directory."); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
if (!await CanInstallLibs(directory)) |
||||
|
{ |
||||
|
Logger.LogWarning( |
||||
|
"abp install-libs command is available for MVC, Razor Page, and Blazor-Server UI types"); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
if (!IsNpmInstalled()) |
||||
|
{ |
||||
|
Logger.LogWarning("NPM is not installed, visit https://nodejs.org/en/download/ and install NPM"); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
if (IsYarnAvailable()) |
||||
|
{ |
||||
|
RunYarn(directory); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
RunNpmInstall(directory); |
||||
|
} |
||||
|
|
||||
|
await CleanAndCopyResources(directory); |
||||
|
} |
||||
|
|
||||
|
private async Task CleanAndCopyResources(string fileDirectory) |
||||
|
{ |
||||
|
var mappingFiles = Directory.GetFiles(fileDirectory, "abp.resourcemapping.js", SearchOption.AllDirectories); |
||||
|
var resourceMapping = new ResourceMapping |
||||
|
{ |
||||
|
Clean = new List<string> {"./wwwroot/libs"} |
||||
|
}; |
||||
|
|
||||
|
foreach (var mappingFile in mappingFiles) |
||||
|
{ |
||||
|
using (var reader = File.OpenText(mappingFile)) |
||||
|
{ |
||||
|
var mappingFileContent = await reader.ReadToEndAsync(); |
||||
|
|
||||
|
var mapping = _jsonSerializer.Deserialize<ResourceMapping>(mappingFileContent |
||||
|
.Replace("module.exports", string.Empty) |
||||
|
.Replace("=", string.Empty).Trim().TrimEnd(';')); |
||||
|
|
||||
|
mapping.ReplaceAliases(); |
||||
|
|
||||
|
mapping.Clean.ForEach(c => resourceMapping.Clean.AddIfNotContains(c)); |
||||
|
mapping.Aliases.ToList().ForEach(x => |
||||
|
{ |
||||
|
resourceMapping.Aliases.AddIfNotContains(new KeyValuePair<string, string>(x.Key, x.Value)); |
||||
|
}); |
||||
|
mapping.Mappings.ToList().ForEach(x => |
||||
|
{ |
||||
|
resourceMapping.Mappings.AddIfNotContains(new KeyValuePair<string, string>(x.Key, x.Value)); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
CleanDirsAndFiles(fileDirectory, resourceMapping); |
||||
|
CopyResourcesToLibs(fileDirectory, resourceMapping); |
||||
|
} |
||||
|
|
||||
|
private void CopyResourcesToLibs(string fileDirectory, ResourceMapping resourceMapping) |
||||
|
{ |
||||
|
foreach (var mapping in resourceMapping.Mappings) |
||||
|
{ |
||||
|
var destPath = Path.Combine(fileDirectory, mapping.Value); |
||||
|
|
||||
|
var files = FindFiles(fileDirectory, mapping.Key); |
||||
|
|
||||
|
foreach (var file in files) |
||||
|
{ |
||||
|
var destFilePath = Path.Combine(destPath, Path.GetFileName(file)); |
||||
|
if (File.Exists(destFilePath)) |
||||
|
{ |
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
Directory.CreateDirectory(Path.GetFullPath(destPath)); |
||||
|
File.Copy(file, destFilePath); |
||||
|
|
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private async Task<bool> CanInstallLibs(string fileDirectory) |
||||
|
{ |
||||
|
var projectFiles = Directory.GetFiles(fileDirectory, "*.csproj"); |
||||
|
|
||||
|
using (var reader = File.OpenText(projectFiles[0])) |
||||
|
{ |
||||
|
var projectFileContent = await reader.ReadToEndAsync(); |
||||
|
|
||||
|
return projectFileContent.Contains("Microsoft.NET.Sdk.Web"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void CleanDirsAndFiles(string directory, ResourceMapping resourceMapping) |
||||
|
{ |
||||
|
var files = FindFiles(directory, resourceMapping.Clean.ToArray()); |
||||
|
|
||||
|
foreach (var file in files) |
||||
|
{ |
||||
|
if (File.Exists(file)) |
||||
|
{ |
||||
|
File.Delete(file); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
foreach (var directoryInfo in Directory.GetDirectories(Path.Combine(directory, resourceMapping.Clean.First()),"*", SearchOption.AllDirectories).Reverse()) |
||||
|
{ |
||||
|
if (!Directory.EnumerateFileSystemEntries(directoryInfo).Any()) |
||||
|
{ |
||||
|
Directory.Delete(directoryInfo); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private string[] FindFiles(string directory, params string[] patterns) |
||||
|
{ |
||||
|
var matcher = new Matcher(); |
||||
|
|
||||
|
foreach (var pattern in patterns) |
||||
|
{ |
||||
|
if (pattern.StartsWith("!")) |
||||
|
{ |
||||
|
matcher.AddExclude(NormalizeGlob(pattern).TrimStart('!')); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
matcher.AddInclude(NormalizeGlob(pattern)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
var result = matcher.Execute(new DirectoryInfoWrapper(new DirectoryInfo(directory))); |
||||
|
|
||||
|
return result.Files.Select(x => Path.Combine(directory, x.Path)).ToArray(); |
||||
|
} |
||||
|
|
||||
|
private string NormalizeGlob(string pattern) |
||||
|
{ |
||||
|
pattern = pattern.Replace("//", "/"); |
||||
|
|
||||
|
if (!Path.HasExtension(pattern) && !pattern.EndsWith("*")) |
||||
|
{ |
||||
|
return pattern.EnsureEndsWith('/') + "**"; |
||||
|
} |
||||
|
|
||||
|
return pattern; |
||||
|
} |
||||
|
|
||||
|
private void RunNpmInstall(string directory) |
||||
|
{ |
||||
|
Logger.LogInformation($"Running npm install on {directory}"); |
||||
|
CmdHelper.RunCmd($"cd {directory} && npm install"); |
||||
|
} |
||||
|
|
||||
|
private void RunYarn(string directory) |
||||
|
{ |
||||
|
Logger.LogInformation($"Running Yarn on {directory}"); |
||||
|
CmdHelper.RunCmd($"cd {directory} && yarn"); |
||||
|
} |
||||
|
|
||||
|
private bool IsNpmInstalled() |
||||
|
{ |
||||
|
var output = CmdHelper.RunCmdAndGetOutput("npm -v").Trim(); |
||||
|
return SemanticVersion.TryParse(output, out _); |
||||
|
} |
||||
|
|
||||
|
private bool IsYarnAvailable() |
||||
|
{ |
||||
|
var output = CmdHelper.RunCmdAndGetOutput("npm list yarn -g").Trim(); |
||||
|
if (output.Contains("empty")) |
||||
|
{ |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
if (!SemanticVersion.TryParse(output.Substring(output.IndexOf('@') + 1), out var version)) |
||||
|
{ |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
return version > SemanticVersion.Parse("1.20.0"); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,53 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
|
||||
|
namespace Volo.Abp.Cli.LIbs |
||||
|
{ |
||||
|
public class ResourceMapping |
||||
|
{ |
||||
|
public Dictionary<string, string> Aliases { get; set; } |
||||
|
|
||||
|
public List<string> Clean { get; set; } |
||||
|
|
||||
|
public Dictionary<string, string> Mappings { get; set; } |
||||
|
|
||||
|
public ResourceMapping() |
||||
|
{ |
||||
|
Aliases = new Dictionary<string, string> |
||||
|
{ |
||||
|
{"@node_modules", "./node_modules"}, |
||||
|
{"@libs", "./wwwroot/libs"}, |
||||
|
}; |
||||
|
Clean = new List<string>(); |
||||
|
Mappings = new Dictionary<string, string>(); |
||||
|
} |
||||
|
|
||||
|
public void ReplaceAliases() |
||||
|
{ |
||||
|
for (var i = 0; i < Mappings.Count; i++) |
||||
|
{ |
||||
|
var mapping = Mappings.ElementAt(i); |
||||
|
Mappings.Remove(mapping.Key); |
||||
|
|
||||
|
var key = mapping.Key; |
||||
|
var value = mapping.Value; |
||||
|
|
||||
|
foreach (var alias in Aliases) |
||||
|
{ |
||||
|
key = key.Replace(alias.Key, alias.Value); |
||||
|
value = value.Replace(alias.Key, alias.Value); |
||||
|
} |
||||
|
|
||||
|
Mappings[key] = value; |
||||
|
} |
||||
|
|
||||
|
for (var i = 0; i < Clean.Count; i++) |
||||
|
{ |
||||
|
foreach (var alias in Aliases) |
||||
|
{ |
||||
|
Clean[i] = Clean[i].Replace(alias.Key, alias.Value); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue