mirror of https://github.com/abpframework/abp.git
12 changed files with 437 additions and 0 deletions
@ -0,0 +1,18 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
using Volo.Abp.Bundling; |
||||
|
|
||||
|
namespace Volo.Abp.AspNetCore.Components.WebAssembly.BasicTheme |
||||
|
{ |
||||
|
public class BundleContributer : IBundleContributer |
||||
|
{ |
||||
|
public void AddScripts(List<string> scripts) |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
|
||||
|
public void AddStyles(List<string> styles) |
||||
|
{ |
||||
|
styles.AddIfNotContains("_content/Volo.Abp.AspNetCore.Components.WebAssembly.BasicTheme/libs/abp/css/theme.css"); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,20 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
using Volo.Abp.Bundling; |
||||
|
|
||||
|
namespace Volo.Abp.AspNetCore.Components.WebAssembly.Theming |
||||
|
{ |
||||
|
public class BundleContributer : IBundleContributer |
||||
|
{ |
||||
|
public void AddScripts(List<string> scripts) |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
|
||||
|
public void AddStyles(List<string> styles) |
||||
|
{ |
||||
|
styles.AddIfNotContains("_content/Volo.Abp.AspNetCore.Components.WebAssembly.Theming/libs/bootstrap/css/bootstrap.css"); |
||||
|
styles.AddIfNotContains("_content/Volo.Abp.AspNetCore.Components.WebAssembly.Theming/libs/fontawesome/css/all.css"); |
||||
|
styles.AddIfNotContains("_content/Volo.Abp.AspNetCore.Components.WebAssembly.Theming/libs/flag-icon/css/flag-icon.css"); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,18 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
using Volo.Abp.Bundling; |
||||
|
|
||||
|
namespace Volo.Abp.AspNetCore.Components.WebAssembly |
||||
|
{ |
||||
|
public class BundleContributer : IBundleContributer |
||||
|
{ |
||||
|
public void AddScripts(List<string> scripts) |
||||
|
{ |
||||
|
scripts.AddIfNotContains("_content/Volo.Abp.AspNetCore.Components.WebAssembly/libs/abp/js/abp.js"); |
||||
|
} |
||||
|
|
||||
|
public void AddStyles(List<string> styles) |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,21 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
using Volo.Abp.Bundling; |
||||
|
|
||||
|
namespace Volo.Abp.BlazoriseUI |
||||
|
{ |
||||
|
public class BundleContributer : IBundleContributer |
||||
|
{ |
||||
|
public void AddScripts(List<string> scripts) |
||||
|
{ |
||||
|
scripts.AddIfNotContains("_content/Blazorise/blazorise.js"); |
||||
|
scripts.AddIfNotContains("_content/Blazorise.Bootstrap/blazorise.bootstrap.js"); |
||||
|
} |
||||
|
|
||||
|
public void AddStyles(List<string> styles) |
||||
|
{ |
||||
|
styles.AddIfNotContains("_content/Blazorise/blazorise.css"); |
||||
|
styles.AddIfNotContains("_content/Blazorise.Bootstrap/blazorise.bootstrap.css"); |
||||
|
styles.AddIfNotContains("_content/Blazorise.Snackbar/blazorise.snackbar.css"); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,10 @@ |
|||||
|
using System; |
||||
|
|
||||
|
namespace Volo.Abp.Cli.Bundling |
||||
|
{ |
||||
|
internal class BundleDefinition |
||||
|
{ |
||||
|
public int Level { get; set; } |
||||
|
public Type BundleContributerType { get; set; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,12 @@ |
|||||
|
using System; |
||||
|
|
||||
|
namespace Volo.Abp.Cli.Bundling |
||||
|
{ |
||||
|
public class BundlingException : Exception |
||||
|
{ |
||||
|
public BundlingException(string message) : base(message) |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,224 @@ |
|||||
|
using Microsoft.Extensions.Logging; |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.IO; |
||||
|
using System.Linq; |
||||
|
using System.Reflection; |
||||
|
using System.Text; |
||||
|
using System.Threading.Tasks; |
||||
|
using System.Xml; |
||||
|
using Volo.Abp.Bundling; |
||||
|
using Volo.Abp.Cli.Build; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
using Volo.Abp.Modularity; |
||||
|
|
||||
|
namespace Volo.Abp.Cli.Bundling |
||||
|
{ |
||||
|
public class BundlingService : IBundlingService, ITransientDependency |
||||
|
{ |
||||
|
const string StylePlaceholderStart = "<!--ABP:Styles-->"; |
||||
|
const string StylePlaceholderEnd = "<!--/ABP:Styles-->"; |
||||
|
const string ScriptPlaceholderStart = "<!--ABP:Scripts-->"; |
||||
|
const string ScriptPlaceholderEnd = "<!--/ABP:Scripts-->"; |
||||
|
const string SupportedWebAssemblyProjectType = "Microsoft.NET.Sdk.BlazorWebAssembly"; |
||||
|
|
||||
|
protected IDotNetProjectBuilder DotNetProjectBuilder { get; set; } |
||||
|
protected ILogger<BundlingService> Logger { get; set; } |
||||
|
|
||||
|
public async Task BundleAsync(string directory, bool forceBuild) |
||||
|
{ |
||||
|
var projectFiles = Directory.GetFiles(directory, "*.csproj"); |
||||
|
if (!projectFiles.Any()) |
||||
|
{ |
||||
|
throw new BundlingException("No project file found in the directory"); |
||||
|
} |
||||
|
|
||||
|
var projectFilePath = projectFiles[0]; |
||||
|
|
||||
|
if (forceBuild) |
||||
|
{ |
||||
|
var projects = new List<DotNetProjectInfo>() |
||||
|
{ |
||||
|
new DotNetProjectInfo(string.Empty, projectFilePath, true) |
||||
|
}; |
||||
|
Logger.LogInformation("Build starting..."); |
||||
|
DotNetProjectBuilder.Build(projects, string.Empty); |
||||
|
Logger.LogInformation("Build completed..."); |
||||
|
} |
||||
|
|
||||
|
var frameworkVersion = GetTargetFrameworkVersion(projectFilePath); |
||||
|
var assemblyFilePath = GetAssemblyFilePath(directory, frameworkVersion, Path.GetFileNameWithoutExtension(projectFilePath)); |
||||
|
var startupModule = GetStartupModule(assemblyFilePath); |
||||
|
|
||||
|
var bundleDefinitions = new List<BundleDefinition>(); |
||||
|
FindBundleContributersRecursively(startupModule, 0, bundleDefinitions); |
||||
|
bundleDefinitions = bundleDefinitions.OrderByDescending(t => t.Level).ToList(); |
||||
|
|
||||
|
var styleDefinitons = GenerateStyleDefinitions(bundleDefinitions); |
||||
|
var scriptDefinitions = GenerateScriptDefinitions(bundleDefinitions); |
||||
|
|
||||
|
await UpdateDependenciesInHtmlFileAsync(directory, styleDefinitons, scriptDefinitions); |
||||
|
} |
||||
|
|
||||
|
protected virtual async Task UpdateDependenciesInHtmlFileAsync(string directory, string styleDefinitions, string scriptDefinitions) |
||||
|
{ |
||||
|
var htmlFilePath = Path.Combine(directory, "wwwroot", "index.html"); |
||||
|
if (!File.Exists(htmlFilePath)) |
||||
|
{ |
||||
|
throw new BundlingException($"index.html file could not be found in the following path:{htmlFilePath}"); |
||||
|
} |
||||
|
|
||||
|
Encoding fileEncoding; |
||||
|
string content; |
||||
|
using (var reader = new StreamReader(htmlFilePath, true)) |
||||
|
{ |
||||
|
fileEncoding = reader.CurrentEncoding; |
||||
|
content = await reader.ReadToEndAsync(); |
||||
|
} |
||||
|
|
||||
|
content = UpdatePlaceholders(content, StylePlaceholderStart, StylePlaceholderEnd, styleDefinitions); |
||||
|
content = UpdatePlaceholders(content, ScriptPlaceholderStart, ScriptPlaceholderEnd, scriptDefinitions); |
||||
|
|
||||
|
using var writer = new StreamWriter(htmlFilePath, false, fileEncoding); |
||||
|
await writer.WriteAsync(content); |
||||
|
await writer.FlushAsync(); |
||||
|
} |
||||
|
|
||||
|
private string UpdatePlaceholders(string content, string placeholderStart, string placeholderEnd, string definitions) |
||||
|
{ |
||||
|
var placeholderStartIndex = content.IndexOf(placeholderStart); |
||||
|
var placeholderEndIndex = content.IndexOf(placeholderEnd); |
||||
|
var updatedContent = content.Remove(placeholderStartIndex, (placeholderEndIndex + placeholderEnd.Length) - placeholderStartIndex); |
||||
|
return updatedContent.Insert(placeholderStartIndex, definitions); |
||||
|
} |
||||
|
|
||||
|
private string GenerateStyleDefinitions(List<BundleDefinition> bundleDefinitions) |
||||
|
{ |
||||
|
var styles = new List<string>(); |
||||
|
foreach (var bundleDefinition in bundleDefinitions) |
||||
|
{ |
||||
|
var contributer = CreateContributerInstance(bundleDefinition.BundleContributerType); |
||||
|
contributer.AddStyles(styles); |
||||
|
} |
||||
|
|
||||
|
var builder = new StringBuilder(); |
||||
|
builder.AppendLine($"{StylePlaceholderStart}"); |
||||
|
foreach (var style in styles) |
||||
|
{ |
||||
|
builder.AppendLine($"\t<link href=\"{style}\" rel=\"stylesheet\" />"); |
||||
|
} |
||||
|
builder.AppendLine($"\t{StylePlaceholderEnd}"); |
||||
|
|
||||
|
return builder.ToString(); |
||||
|
} |
||||
|
|
||||
|
private string GenerateScriptDefinitions(List<BundleDefinition> bundleDefinitions) |
||||
|
{ |
||||
|
var scripts = new List<string> |
||||
|
{ |
||||
|
"_framework/blazor.webassembly.js" |
||||
|
}; |
||||
|
foreach (var bundleDefinition in bundleDefinitions) |
||||
|
{ |
||||
|
var contributer = CreateContributerInstance(bundleDefinition.BundleContributerType); |
||||
|
contributer.AddScripts(scripts); |
||||
|
} |
||||
|
|
||||
|
var builder = new StringBuilder(); |
||||
|
builder.AppendLine($"{ScriptPlaceholderStart}"); |
||||
|
foreach (var script in scripts) |
||||
|
{ |
||||
|
builder.AppendLine($"\t<script src=\"{script}\"></script>"); |
||||
|
} |
||||
|
builder.AppendLine($"\t{ScriptPlaceholderEnd}"); |
||||
|
|
||||
|
return builder.ToString(); |
||||
|
} |
||||
|
|
||||
|
private IBundleContributer CreateContributerInstance(Type bundleContributerType) |
||||
|
{ |
||||
|
var instance = Activator.CreateInstance(bundleContributerType); |
||||
|
return instance.As<IBundleContributer>(); |
||||
|
} |
||||
|
|
||||
|
private void FindBundleContributersRecursively(Type module, int level, List<BundleDefinition> bundleDefinitions) |
||||
|
{ |
||||
|
var dependencyDescriptors = module |
||||
|
.GetCustomAttributes() |
||||
|
.OfType<IDependedTypesProvider>(); |
||||
|
|
||||
|
var bundleContributer = module.Assembly.GetTypes().SingleOrDefault(t => t.IsAssignableTo<IBundleContributer>()); |
||||
|
if (bundleContributer != null) |
||||
|
{ |
||||
|
var definition = bundleDefinitions.SingleOrDefault(t => t.BundleContributerType == bundleContributer); |
||||
|
if (definition != null) |
||||
|
{ |
||||
|
if (definition.Level < level) |
||||
|
{ |
||||
|
definition.Level = level; |
||||
|
} |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
bundleDefinitions.Add(new BundleDefinition |
||||
|
{ |
||||
|
Level = level, |
||||
|
BundleContributerType = bundleContributer |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
foreach (var descriptor in dependencyDescriptors) |
||||
|
{ |
||||
|
foreach (var dependedModuleType in descriptor.GetDependedTypes()) |
||||
|
{ |
||||
|
FindBundleContributersRecursively(dependedModuleType, level + 1, bundleDefinitions); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private Type GetStartupModule(string assemblyPath) |
||||
|
{ |
||||
|
var assembly = Assembly.LoadFrom(assemblyPath); |
||||
|
return assembly.GetTypes().SingleOrDefault(IsAbpModule); |
||||
|
|
||||
|
static bool IsAbpModule(Type type) |
||||
|
{ |
||||
|
var typeInfo = type.GetTypeInfo(); |
||||
|
|
||||
|
return |
||||
|
typeInfo.IsClass && |
||||
|
!typeInfo.IsAbstract && |
||||
|
!typeInfo.IsGenericType && |
||||
|
typeof(IAbpModule).GetTypeInfo().IsAssignableFrom(type); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private string GetFrameworkFolderPath(string projectDirectory, string frameworkVersion) |
||||
|
{ |
||||
|
return Path.Combine(projectDirectory, "bin", "Debug", frameworkVersion, "wwwroot", "_framework"); ; |
||||
|
} |
||||
|
|
||||
|
private string GetTargetFrameworkVersion(string projectFilePath) |
||||
|
{ |
||||
|
var document = new XmlDocument(); |
||||
|
document.Load(projectFilePath); |
||||
|
var sdk = document.DocumentElement.GetAttribute("Sdk"); |
||||
|
if (sdk == SupportedWebAssemblyProjectType) |
||||
|
{ |
||||
|
var frameworkVersion = document.SelectSingleNode("//TargetFramework").InnerText; |
||||
|
return frameworkVersion; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
throw new BundlingException($"Unsupported project type. Project type must be {SupportedWebAssemblyProjectType}."); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private string GetAssemblyFilePath(string directory, string frameworkVersion, string projectFileName) |
||||
|
{ |
||||
|
var outputDirectory = GetFrameworkFolderPath(directory, frameworkVersion); |
||||
|
return Path.Combine(outputDirectory, projectFileName + ".dll"); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,9 @@ |
|||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
namespace Volo.Abp.Cli.Bundling |
||||
|
{ |
||||
|
public interface IBundlingService |
||||
|
{ |
||||
|
Task BundleAsync(string directory, bool forceBuild); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,74 @@ |
|||||
|
using Microsoft.Extensions.Logging; |
||||
|
using System; |
||||
|
using System.IO; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.Cli.Args; |
||||
|
using Volo.Abp.Cli.Bundling; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
|
||||
|
namespace Volo.Abp.Cli.Commands |
||||
|
{ |
||||
|
public class BundleCommand : IConsoleCommand, ITransientDependency |
||||
|
{ |
||||
|
public ILogger<BundleCommand> Logger { get; set; } |
||||
|
|
||||
|
public IBundlingService BundlingService { get; set; } |
||||
|
|
||||
|
|
||||
|
public async Task ExecuteAsync(CommandLineArgs commandLineArgs) |
||||
|
{ |
||||
|
var workingDirectoryArg = commandLineArgs.Options.GetOrNull( |
||||
|
Options.WorkingDirectory.Short, |
||||
|
Options.WorkingDirectory.Long |
||||
|
); |
||||
|
|
||||
|
var workingDirectory = workingDirectoryArg ?? Directory.GetCurrentDirectory(); |
||||
|
|
||||
|
var forceBuild = commandLineArgs.Options.ContainsKey(Options.ForceBuild.Short) || |
||||
|
commandLineArgs.Options.ContainsKey(Options.ForceBuild.Long); |
||||
|
|
||||
|
if (!Directory.Exists(workingDirectory)) |
||||
|
{ |
||||
|
throw new CliUsageException( |
||||
|
"Specified directory does not exist." + |
||||
|
Environment.NewLine + Environment.NewLine + |
||||
|
GetUsageInfo() |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
try |
||||
|
{ |
||||
|
await BundlingService.BundleAsync(workingDirectory, forceBuild); |
||||
|
} |
||||
|
catch (BundlingException ex) |
||||
|
{ |
||||
|
Logger.LogError(ex.Message); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public string GetShortDescription() |
||||
|
{ |
||||
|
throw new NotImplementedException(); |
||||
|
} |
||||
|
|
||||
|
public string GetUsageInfo() |
||||
|
{ |
||||
|
throw new NotImplementedException(); |
||||
|
} |
||||
|
|
||||
|
public static class Options |
||||
|
{ |
||||
|
public static class WorkingDirectory |
||||
|
{ |
||||
|
public const string Short = "wd"; |
||||
|
public const string Long = "working-directory"; |
||||
|
} |
||||
|
|
||||
|
public static class ForceBuild |
||||
|
{ |
||||
|
public const string Short = "f"; |
||||
|
public const string Long = "force"; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,12 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Text; |
||||
|
|
||||
|
namespace Volo.Abp.Bundling |
||||
|
{ |
||||
|
public interface IBundleContributer |
||||
|
{ |
||||
|
void AddScripts(List<string> scripts); |
||||
|
void AddStyles(List<string> styles); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,18 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using Volo.Abp.Bundling; |
||||
|
|
||||
|
namespace Volo.Abp.Http.Client.IdentityModel.WebAssembly |
||||
|
{ |
||||
|
public class BundleContributer : IBundleContributer |
||||
|
{ |
||||
|
public void AddScripts(List<string> scripts) |
||||
|
{ |
||||
|
scripts.AddIfNotContains("_content/Microsoft.AspNetCore.Components.WebAssembly.Authentication/AuthenticationService.js"); |
||||
|
} |
||||
|
|
||||
|
public void AddStyles(List<string> styles) |
||||
|
{ |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue