Browse Source

bundle command initial implementation.

pull/6034/head
Ilkay Ilknur 6 years ago
parent
commit
4564d3e6d5
  1. 18
      framework/src/Volo.Abp.AspNetCore.Components.WebAssembly.BasicTheme/BundleContributer.cs
  2. 20
      framework/src/Volo.Abp.AspNetCore.Components.WebAssembly.Theming/BundleContributer.cs
  3. 18
      framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/BundleContributer.cs
  4. 21
      framework/src/Volo.Abp.BlazoriseUI/BundleContributer.cs
  5. 1
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/AbpCliCoreModule.cs
  6. 10
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/BundleDefinition.cs
  7. 12
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/BundlingException.cs
  8. 224
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/BundlingService.cs
  9. 9
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/IBundlingService.cs
  10. 74
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/BundleCommand.cs
  11. 12
      framework/src/Volo.Abp.Core/Volo/Abp/Bundling/IBundleContributer.cs
  12. 18
      framework/src/Volo.Abp.Http.Client.IdentityModel.WebAssembly/Volo/Abp/Http/Client/IdentityModel/WebAssembly/BundleContributer.cs

18
framework/src/Volo.Abp.AspNetCore.Components.WebAssembly.BasicTheme/BundleContributer.cs

@ -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");
}
}
}

20
framework/src/Volo.Abp.AspNetCore.Components.WebAssembly.Theming/BundleContributer.cs

@ -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");
}
}
}

18
framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/BundleContributer.cs

@ -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)
{
}
}
}

21
framework/src/Volo.Abp.BlazoriseUI/BundleContributer.cs

@ -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");
}
}
}

1
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/AbpCliCoreModule.cs

@ -37,6 +37,7 @@ namespace Volo.Abp.Cli
options.Commands["switch-to-nightly"] = typeof(SwitchToNightlyCommand);
options.Commands["translate"] = typeof(TranslateCommand);
options.Commands["build"] = typeof(BuildCommand);
options.Commands["bundle"] = typeof(BundleCommand);
});
}
}

10
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/BundleDefinition.cs

@ -0,0 +1,10 @@
using System;
namespace Volo.Abp.Cli.Bundling
{
internal class BundleDefinition
{
public int Level { get; set; }
public Type BundleContributerType { get; set; }
}
}

12
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/BundlingException.cs

@ -0,0 +1,12 @@
using System;
namespace Volo.Abp.Cli.Bundling
{
public class BundlingException : Exception
{
public BundlingException(string message) : base(message)
{
}
}
}

224
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/BundlingService.cs

@ -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");
}
}
}

9
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/IBundlingService.cs

@ -0,0 +1,9 @@
using System.Threading.Tasks;
namespace Volo.Abp.Cli.Bundling
{
public interface IBundlingService
{
Task BundleAsync(string directory, bool forceBuild);
}
}

74
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/BundleCommand.cs

@ -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";
}
}
}
}

12
framework/src/Volo.Abp.Core/Volo/Abp/Bundling/IBundleContributer.cs

@ -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);
}
}

18
framework/src/Volo.Abp.Http.Client.IdentityModel.WebAssembly/Volo/Abp/Http/Client/IdentityModel/WebAssembly/BundleContributer.cs

@ -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…
Cancel
Save