From dcac0ddce827f0e258ca10444473c42558223d28 Mon Sep 17 00:00:00 2001 From: Ilkay Ilknur Date: Thu, 26 Nov 2020 17:05:17 +0300 Subject: [PATCH 1/8] bundle and minification support added. --- .../Volo/Abp/Cli/AbpCliCoreModule.cs | 4 +- .../Volo/Abp/Cli/Bundling/BundleOptions.cs | 21 ++++ .../Volo/Abp/Cli/Bundling/BundlerBase.cs | 116 ++++++++++++++++++ .../Volo/Abp/Cli/Bundling/BundlingConsts.cs | 11 ++ .../Volo/Abp/Cli/Bundling/BundlingService.cs | 75 ++++++----- .../Volo/Abp/Cli/Bundling/IBundler.cs | 9 ++ .../Volo/Abp/Cli/Bundling/IBundlingService.cs | 2 +- .../Volo/Abp/Cli/Bundling/PathHelper.cs | 23 ++++ .../Cli/Bundling/Scripts/IScriptBundler.cs | 10 ++ .../Abp/Cli/Bundling/Scripts/ScriptBundler.cs | 34 +++++ .../Styles/CssRelativePathAdjuster.cs | 77 ++++++++++++ .../Abp/Cli/Bundling/Styles/IStyleBundler.cs | 10 ++ .../Abp/Cli/Bundling/Styles/StyleBundler.cs | 37 ++++++ .../Volo/Abp/Cli/Commands/BundleCommand.cs | 40 +++++- 14 files changed, 435 insertions(+), 34 deletions(-) create mode 100644 framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/BundleOptions.cs create mode 100644 framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/BundlerBase.cs create mode 100644 framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/BundlingConsts.cs create mode 100644 framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/IBundler.cs create mode 100644 framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/PathHelper.cs create mode 100644 framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/Scripts/IScriptBundler.cs create mode 100644 framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/Scripts/ScriptBundler.cs create mode 100644 framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/Styles/CssRelativePathAdjuster.cs create mode 100644 framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/Styles/IStyleBundler.cs create mode 100644 framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/Styles/StyleBundler.cs diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/AbpCliCoreModule.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/AbpCliCoreModule.cs index d4d1d1d22e..bc4db38c84 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/AbpCliCoreModule.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/AbpCliCoreModule.cs @@ -3,6 +3,7 @@ using Volo.Abp.Cli.Commands; using Volo.Abp.Domain; using Volo.Abp.IdentityModel; using Volo.Abp.Json; +using Volo.Abp.Minify; using Volo.Abp.Modularity; namespace Volo.Abp.Cli @@ -10,7 +11,8 @@ namespace Volo.Abp.Cli [DependsOn( typeof(AbpDddDomainModule), typeof(AbpJsonModule), - typeof(AbpIdentityModelModule) + typeof(AbpIdentityModelModule), + typeof(AbpMinifyModule) )] public class AbpCliCoreModule : AbpModule { diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/BundleOptions.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/BundleOptions.cs new file mode 100644 index 0000000000..fceff11240 --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/BundleOptions.cs @@ -0,0 +1,21 @@ +using JetBrains.Annotations; + +namespace Volo.Abp.Cli.Bundling +{ + public class BundleOptions + { + [NotNull] + public string Directory { get; set; } + + [NotNull] + public string BundleName { get; set; } + + [NotNull] + public string FrameworkVersion { get; set; } + + [NotNull] + public string ProjectFileName { get; set; } + + public bool Minify { get; set; } + } +} diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/BundlerBase.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/BundlerBase.cs new file mode 100644 index 0000000000..de90e4f718 --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/BundlerBase.cs @@ -0,0 +1,116 @@ +using Microsoft.Extensions.Logging; +using System; +using System.IO; +using System.Linq; +using System.Text; +using System.Xml; +using Volo.Abp.Bundling; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Minify; +using Volo.Abp.Minify.NUglify; + +namespace Volo.Abp.Cli.Bundling +{ + public abstract class BundlerBase : IBundler, ITransientDependency + { + private static string[] _minFileSuffixes = { "min", "prod" }; + + protected IMinifier Minifier { get; } + protected ILogger Logger { get; set; } + public abstract string FileExtension { get; } + public abstract string GenerateDefinition(string bundleFilePath); + + protected BundlerBase(IMinifier minifier) + { + Minifier = minifier; + } + + public string Bundle(BundleOptions options, BundleContext context) + { + var bundleFilePath = Path.Combine(PathHelper.GetWwwRootPath(options.Directory), $"{options.BundleName}{FileExtension}"); + var bundledContent = BundleFiles(options, context); + File.WriteAllText(bundleFilePath, bundledContent); + + return GenerateDefinition(bundleFilePath); + } + + private bool IsMinFile(string fileName) + { + foreach (var suffix in _minFileSuffixes) + { + if (fileName.EndsWith($".{suffix}.{FileExtension}", StringComparison.InvariantCultureIgnoreCase)) + { + return true; + } + } + + return false; + } + + private string BundleFiles(BundleOptions options, BundleContext context) + { + var staticAssetsFilePath = Path.Combine(options.Directory, "bin", "Debug", options.FrameworkVersion, $"{options.ProjectFileName}.StaticWebAssets.xml"); + if (!File.Exists(staticAssetsFilePath)) + { + throw new BundlingException("Unable to find static web assets file. You need to build the project to generate static web assets file."); + } + + var staticAssetsDefinitions = new XmlDocument(); + staticAssetsDefinitions.Load(staticAssetsFilePath); + + var builder = new StringBuilder(); + foreach (var definition in context.BundleDefinitions) + { + string content; + if (definition.Source.StartsWith("_content")) + { + var pathFragments = definition.Source.Split('/').ToList(); + var basePath = $"{pathFragments[0]}/{pathFragments[1]}"; + var path = staticAssetsDefinitions.SelectSingleNode($"//ContentRoot[@BasePath='{basePath}']").Attributes["Path"].Value; + var absolutePath = definition.Source.Replace(basePath, path); + content = GetFileContent(absolutePath, options.Minify); + } + else if (definition.Source.StartsWith("_framework")) + { + var slashIndex = definition.Source.IndexOf('/'); + var fileName = definition.Source.Substring(slashIndex + 1, definition.Source.Length - slashIndex - 1); + var filePath = Path.Combine(PathHelper.GetFrameworkFolderPath(options.Directory, options.FrameworkVersion), fileName); + content = GetFileContent(filePath, false); + } + else + { + var filePath = Path.Combine(PathHelper.GetWwwRootPath(options.Directory), definition.Source); + content = GetFileContent(filePath, options.Minify); + } + + content = ProcessBeforeAddingToTheBundle(definition.Source, Path.Combine(options.Directory, "wwwroot"), content); + builder.AppendLine(content); + } + + return builder.ToString(); + } + + private string GetFileContent(string filePath, bool minify) + { + var content = File.ReadAllText(filePath); + if (minify && !IsMinFile(filePath)) + { + try + { + content = Minifier.Minify(content); + } + catch (NUglifyException ex) + { + Logger.LogWarning($"Unable to minify the file: {Path.GetFileName(filePath)}. Adding file to the bundle without minification.", ex); + } + } + + return content; + } + + protected virtual string ProcessBeforeAddingToTheBundle(string referencePath, string bundleDirectory, string fileContent) + { + return fileContent; + } + } +} diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/BundlingConsts.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/BundlingConsts.cs new file mode 100644 index 0000000000..f5153b1fa0 --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/BundlingConsts.cs @@ -0,0 +1,11 @@ +namespace Volo.Abp.Cli.Bundling +{ + internal static class BundlingConsts + { + internal const string StylePlaceholderStart = ""; + internal const string StylePlaceholderEnd = ""; + internal const string ScriptPlaceholderStart = ""; + internal const string ScriptPlaceholderEnd = ""; + internal const string SupportedWebAssemblyProjectType = "Microsoft.NET.Sdk.BlazorWebAssembly"; + } +} diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/BundlingService.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/BundlingService.cs index 698b0978c2..1f731d96f8 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/BundlingService.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/BundlingService.cs @@ -1,4 +1,5 @@ -using System; +using Microsoft.Extensions.Logging; +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -8,22 +9,25 @@ using System.Threading.Tasks; using System.Xml; using Volo.Abp.Bundling; using Volo.Abp.Cli.Build; +using Volo.Abp.Cli.Bundling.Scripts; +using Volo.Abp.Cli.Bundling.Styles; using Volo.Abp.DependencyInjection; +using Volo.Abp.Minify.Scripts; +using Volo.Abp.Minify.Styles; using Volo.Abp.Modularity; namespace Volo.Abp.Cli.Bundling { public class BundlingService : IBundlingService, ITransientDependency { - const string StylePlaceholderStart = ""; - const string StylePlaceholderEnd = ""; - const string ScriptPlaceholderStart = ""; - const string ScriptPlaceholderEnd = ""; - const string SupportedWebAssemblyProjectType = "Microsoft.NET.Sdk.BlazorWebAssembly"; - public IDotNetProjectBuilder DotNetProjectBuilder { get; set; } + public IJavascriptMinifier JsMinifier { get; set; } + public ICssMinifier CssMinifier { get; set; } + public ILogger Logger { get; set; } + public IScriptBundler ScriptBundler { get; set; } + public IStyleBundler StyleBundler { get; set; } - public async Task BundleAsync(string directory, bool forceBuild) + public async Task BundleAsync(string directory, bool forceBuild, bool bundle, bool minify, string bundleName) { var projectFiles = Directory.GetFiles(directory, "*.csproj"); if (!projectFiles.Any()) @@ -44,7 +48,8 @@ namespace Volo.Abp.Cli.Bundling } var frameworkVersion = GetTargetFrameworkVersion(projectFilePath); - var assemblyFilePath = GetAssemblyFilePath(directory, frameworkVersion, Path.GetFileNameWithoutExtension(projectFilePath)); + var projectName = Path.GetFileNameWithoutExtension(projectFilePath); + var assemblyFilePath = PathHelper.GetAssemblyFilePath(directory, frameworkVersion, projectName); var startupModule = GetStartupModule(assemblyFilePath); var bundleDefinitions = new List(); @@ -53,8 +58,27 @@ namespace Volo.Abp.Cli.Bundling var styleContext = GetStyleContext(bundleDefinitions); var scriptContext = GetScriptContext(bundleDefinitions); - var styleDefinitions = GenerateStyleDefinitions(styleContext); - var scriptDefinitions = GenerateScriptDefinitions(scriptContext); + string styleDefinitions; + string scriptDefinitions; + + if (bundle || minify) + { + var options = new BundleOptions + { + Directory = directory, + FrameworkVersion = frameworkVersion, + ProjectFileName = projectName, + BundleName = bundleName + }; + + styleDefinitions = StyleBundler.Bundle(options, styleContext); + scriptDefinitions = ScriptBundler.Bundle(options, scriptContext); + } + else + { + styleDefinitions = GenerateStyleDefinitions(styleContext); + scriptDefinitions = GenerateScriptDefinitions(scriptContext); + } await UpdateDependenciesInHtmlFileAsync(directory, styleDefinitions, scriptDefinitions); } @@ -88,7 +112,7 @@ namespace Volo.Abp.Cli.Bundling private async Task UpdateDependenciesInHtmlFileAsync(string directory, string styleDefinitions, string scriptDefinitions) { - var htmlFilePath = Path.Combine(directory, "wwwroot", "index.html"); + var htmlFilePath = Path.Combine(PathHelper.GetWwwRootPath(directory), "index.html"); if (!File.Exists(htmlFilePath)) { throw new BundlingException($"index.html file could not be found in the following path:{htmlFilePath}"); @@ -102,8 +126,8 @@ namespace Volo.Abp.Cli.Bundling content = await reader.ReadToEndAsync(); } - content = UpdatePlaceholders(content, StylePlaceholderStart, StylePlaceholderEnd, styleDefinitions); - content = UpdatePlaceholders(content, ScriptPlaceholderStart, ScriptPlaceholderEnd, scriptDefinitions); + content = UpdatePlaceholders(content, BundlingConsts.StylePlaceholderStart, BundlingConsts.StylePlaceholderEnd, styleDefinitions); + content = UpdatePlaceholders(content, BundlingConsts.ScriptPlaceholderStart, BundlingConsts.ScriptPlaceholderEnd, scriptDefinitions); using (var writer = new StreamWriter(htmlFilePath, false, fileEncoding)) { @@ -124,7 +148,7 @@ namespace Volo.Abp.Cli.Bundling { var builder = new StringBuilder(); - builder.AppendLine($"{StylePlaceholderStart}"); + builder.AppendLine($"{BundlingConsts.StylePlaceholderStart}"); foreach (var style in context.BundleDefinitions) { @@ -138,7 +162,7 @@ namespace Volo.Abp.Cli.Bundling builder.AppendLine("/>"); } - builder.Append($" {StylePlaceholderEnd}"); + builder.Append($" {BundlingConsts.StylePlaceholderEnd}"); return builder.ToString(); } @@ -146,7 +170,7 @@ namespace Volo.Abp.Cli.Bundling private string GenerateScriptDefinitions(BundleContext context) { var builder = new StringBuilder(); - builder.AppendLine($"{ScriptPlaceholderStart}"); + builder.AppendLine($"{BundlingConsts.ScriptPlaceholderStart}"); foreach (var script in context.BundleDefinitions) { builder.Append($" "); } - builder.Append($" {ScriptPlaceholderEnd}"); + builder.Append($" {BundlingConsts.ScriptPlaceholderEnd}"); return builder.ToString(); } @@ -223,28 +247,17 @@ namespace Volo.Abp.Cli.Bundling .SingleOrDefault(AbpModule.IsAbpModule); } - 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) + if (sdk != BundlingConsts.SupportedWebAssemblyProjectType) { - throw new BundlingException($"Unsupported project type. Project type must be {SupportedWebAssemblyProjectType}."); + throw new BundlingException($"Unsupported project type. Project type must be {BundlingConsts.SupportedWebAssemblyProjectType}."); } return document.SelectSingleNode("//TargetFramework").InnerText; } - - private string GetAssemblyFilePath(string directory, string frameworkVersion, string projectFileName) - { - var outputDirectory = GetFrameworkFolderPath(directory, frameworkVersion); - return Path.Combine(outputDirectory, projectFileName + ".dll"); - } } } diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/IBundler.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/IBundler.cs new file mode 100644 index 0000000000..a29de9f72f --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/IBundler.cs @@ -0,0 +1,9 @@ +using Volo.Abp.Bundling; + +namespace Volo.Abp.Cli.Bundling +{ + public interface IBundler + { + string Bundle(BundleOptions options, BundleContext context); + } +} diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/IBundlingService.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/IBundlingService.cs index 817735ee20..9ddb4d7e96 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/IBundlingService.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/IBundlingService.cs @@ -4,6 +4,6 @@ namespace Volo.Abp.Cli.Bundling { public interface IBundlingService { - Task BundleAsync(string directory, bool forceBuild); + Task BundleAsync(string directory, bool forceBuild, bool bundle, bool minify, string bundleName); } } diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/PathHelper.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/PathHelper.cs new file mode 100644 index 0000000000..6ed6799714 --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/PathHelper.cs @@ -0,0 +1,23 @@ +using System.IO; + +namespace Volo.Abp.Cli.Bundling +{ + internal static class PathHelper + { + internal static string GetFrameworkFolderPath(string projectDirectory, string frameworkVersion) + { + return Path.Combine(projectDirectory, "bin", "Debug", frameworkVersion, "wwwroot", "_framework"); ; + } + + internal static string GetAssemblyFilePath(string directory, string frameworkVersion, string projectFileName) + { + var outputDirectory = GetFrameworkFolderPath(directory, frameworkVersion); + return Path.Combine(outputDirectory, projectFileName + ".dll"); + } + + internal static string GetWwwRootPath(string directory) + { + return Path.Combine(directory, "wwwroot"); + } + } +} diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/Scripts/IScriptBundler.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/Scripts/IScriptBundler.cs new file mode 100644 index 0000000000..fbe8bb5e59 --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/Scripts/IScriptBundler.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Volo.Abp.Cli.Bundling.Scripts +{ + public interface IScriptBundler : IBundler + { + } +} diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/Scripts/ScriptBundler.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/Scripts/ScriptBundler.cs new file mode 100644 index 0000000000..e8856cd449 --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/Scripts/ScriptBundler.cs @@ -0,0 +1,34 @@ +using System; +using System.IO; +using System.Text; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Minify.Scripts; + +namespace Volo.Abp.Cli.Bundling.Scripts +{ + public class ScriptBundler : BundlerBase, IScriptBundler, ITransientDependency + { + public override string FileExtension => ".js"; + + public ScriptBundler(IJavascriptMinifier minifier) + : base(minifier) + { + + } + + public override string GenerateDefinition(string bundleFilePath) + { + var lastModifiedTicks = File.GetLastWriteTime(bundleFilePath).Ticks; + var builder = new StringBuilder(); + builder.AppendLine($"{BundlingConsts.ScriptPlaceholderStart}"); + builder.AppendLine($" "); + builder.Append($" {BundlingConsts.ScriptPlaceholderEnd}"); + return builder.ToString(); + } + + protected override string ProcessBeforeAddingToTheBundle(string referencePath, string bundleDirectory, string fileContent) + { + return fileContent.EnsureEndsWith(';') + Environment.NewLine; + } + } +} diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/Styles/CssRelativePathAdjuster.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/Styles/CssRelativePathAdjuster.cs new file mode 100644 index 0000000000..05aba2f40a --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/Styles/CssRelativePathAdjuster.cs @@ -0,0 +1,77 @@ +using System; +using System.IO; +using System.Text.RegularExpressions; + +namespace Volo.Abp.Cli.Bundling.Styles +{ + internal static class CssRelativePathAdjuster + { + private static readonly Regex _rxUrl = new Regex(@"url\s*\(\s*([""']?)([^:)]+)\1\s*\)", RegexOptions.IgnoreCase | RegexOptions.Compiled); + + public static string Adjust( + string cssFileContents, + string absoluteInputFilePath, + string absoluteOutputPath) + { + var matches = _rxUrl.Matches(cssFileContents); + + if (matches.Count <= 0) + { + return cssFileContents; + } + + var cssDirectoryPath = Path.GetDirectoryName(absoluteInputFilePath); + + foreach (Match match in matches) + { + string quoteDelimiter = match.Groups[1].Value; //url('') vs url("") + string relativePathToCss = match.Groups[2].Value; + + // Ignore root relative references + if (relativePathToCss.StartsWith("/", StringComparison.Ordinal)) + continue; + + //prevent query string from causing error + var pathAndQuery = relativePathToCss.Split(new[] { '?' }, 2, StringSplitOptions.RemoveEmptyEntries); + var pathOnly = pathAndQuery[0]; + var queryOnly = pathAndQuery.Length == 2 ? pathAndQuery[1] : string.Empty; + + string absolutePath = GetAbsolutePath(cssDirectoryPath, pathOnly); + string serverRelativeUrl = MakeRelative(absoluteOutputPath, absolutePath); + + if (!string.IsNullOrEmpty(queryOnly)) + serverRelativeUrl += "?" + queryOnly; + + string replace = string.Format("url({0}{1}{0})", quoteDelimiter, serverRelativeUrl); + + cssFileContents = cssFileContents.Replace(match.Groups[0].Value, replace); + } + + return cssFileContents; + } + + private static string GetAbsolutePath(string cssFilePath, string pathOnly) + { + return Path.GetFullPath(Path.Combine(cssFilePath, pathOnly)); + } + + private static readonly string _protocol = "file:///"; + private static string MakeRelative(string baseFile, string file) + { + if (string.IsNullOrEmpty(file)) + return file; + + Uri baseUri = new Uri(_protocol + baseFile, UriKind.RelativeOrAbsolute); + Uri fileUri = new Uri(_protocol + file, UriKind.RelativeOrAbsolute); + + if (baseUri.IsAbsoluteUri) + { + return Uri.UnescapeDataString(baseUri.MakeRelativeUri(fileUri).ToString()); + } + else + { + return baseUri.ToString(); + } + } + } +} diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/Styles/IStyleBundler.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/Styles/IStyleBundler.cs new file mode 100644 index 0000000000..d2b99bf093 --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/Styles/IStyleBundler.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Volo.Abp.Cli.Bundling.Styles +{ + public interface IStyleBundler : IBundler + { + } +} diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/Styles/StyleBundler.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/Styles/StyleBundler.cs new file mode 100644 index 0000000000..7d531cb94d --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/Styles/StyleBundler.cs @@ -0,0 +1,37 @@ +using System.IO; +using System.Text; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Minify.Styles; + +namespace Volo.Abp.Cli.Bundling.Styles +{ + public class StyleBundler : BundlerBase, IStyleBundler, ITransientDependency + { + public override string FileExtension => ".css"; + + public StyleBundler(ICssMinifier minifier) + : base(minifier) + { + + } + + public override string GenerateDefinition(string bundleFilePath) + { + var lastModifiedTicks = File.GetLastWriteTime(bundleFilePath).Ticks; + var builder = new StringBuilder(); + builder.AppendLine($"{BundlingConsts.StylePlaceholderStart}"); + builder.AppendLine($" "); + builder.Append($" {BundlingConsts.StylePlaceholderEnd}"); + return builder.ToString(); + } + + protected override string ProcessBeforeAddingToTheBundle(string referencePath, string bundleDirectory, string fileContent) + { + return CssRelativePathAdjuster.Adjust( + fileContent, + referencePath, + bundleDirectory + ); + } + } +} diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/BundleCommand.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/BundleCommand.cs index eaf0b33116..f48be659cb 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/BundleCommand.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/BundleCommand.cs @@ -28,6 +28,26 @@ namespace Volo.Abp.Cli.Commands var forceBuild = commandLineArgs.Options.ContainsKey(Options.ForceBuild.Short) || commandLineArgs.Options.ContainsKey(Options.ForceBuild.Long); + var bundle = commandLineArgs.Options.ContainsKey(Options.Bundle.Short) || + commandLineArgs.Options.ContainsKey(Options.Bundle.Long); + + var minify = commandLineArgs.Options.ContainsKey(Options.Minify.Short) || + commandLineArgs.Options.ContainsKey(Options.Minify.Long); + + var name = commandLineArgs.Options.GetOrNull( + Options.Name.Short, + Options.Name.Long + ); + + if ((minify || bundle) && name.IsNullOrEmpty()) + { + throw new CliUsageException( + "Please specify bundle name." + + Environment.NewLine + Environment.NewLine + + GetUsageInfo() + ); + } + if (!Directory.Exists(workingDirectory)) { throw new CliUsageException( @@ -39,7 +59,7 @@ namespace Volo.Abp.Cli.Commands try { - await BundlingService.BundleAsync(workingDirectory, forceBuild); + await BundlingService.BundleAsync(workingDirectory, forceBuild, bundle, minify, name); } catch (BundlingException ex) { @@ -84,6 +104,24 @@ namespace Volo.Abp.Cli.Commands public const string Short = "f"; public const string Long = "force"; } + + public static class Bundle + { + public const string Short = "b"; + public const string Long = "bundle"; + } + + public static class Minify + { + public const string Short = "m"; + public const string Long = "minify"; + } + + public static class Name + { + public const string Short = "n"; + public const string Long = "name"; + } } } } From 5099d5cab8a7d6606589d8180fe678f9448a8ea6 Mon Sep 17 00:00:00 2001 From: Ilkay Ilknur Date: Fri, 27 Nov 2020 14:05:14 +0300 Subject: [PATCH 2/8] unneccesary space removed. --- .../Volo/Abp/Cli/Bundling/Scripts/ScriptBundler.cs | 2 +- .../Volo/Abp/Cli/Bundling/Styles/StyleBundler.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/Scripts/ScriptBundler.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/Scripts/ScriptBundler.cs index e8856cd449..674b3ec7fd 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/Scripts/ScriptBundler.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/Scripts/ScriptBundler.cs @@ -21,7 +21,7 @@ namespace Volo.Abp.Cli.Bundling.Scripts var lastModifiedTicks = File.GetLastWriteTime(bundleFilePath).Ticks; var builder = new StringBuilder(); builder.AppendLine($"{BundlingConsts.ScriptPlaceholderStart}"); - builder.AppendLine($" "); + builder.AppendLine($" "); builder.Append($" {BundlingConsts.ScriptPlaceholderEnd}"); return builder.ToString(); } diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/Styles/StyleBundler.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/Styles/StyleBundler.cs index 7d531cb94d..414c6bec41 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/Styles/StyleBundler.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/Styles/StyleBundler.cs @@ -20,7 +20,7 @@ namespace Volo.Abp.Cli.Bundling.Styles var lastModifiedTicks = File.GetLastWriteTime(bundleFilePath).Ticks; var builder = new StringBuilder(); builder.AppendLine($"{BundlingConsts.StylePlaceholderStart}"); - builder.AppendLine($" "); + builder.AppendLine($" "); builder.Append($" {BundlingConsts.StylePlaceholderEnd}"); return builder.ToString(); } From 23a5301cd63d74575b74c21e9d6f3b8f0179a723 Mon Sep 17 00:00:00 2001 From: Ilkay Ilknur Date: Fri, 27 Nov 2020 14:05:40 +0300 Subject: [PATCH 3/8] usage info updated. --- .../Volo/Abp/Cli/Commands/BundleCommand.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/BundleCommand.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/BundleCommand.cs index f48be659cb..e8554f89ae 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/BundleCommand.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/BundleCommand.cs @@ -42,7 +42,7 @@ namespace Volo.Abp.Cli.Commands if ((minify || bundle) && name.IsNullOrEmpty()) { throw new CliUsageException( - "Please specify bundle name." + + "Please specify a bundle name." + Environment.NewLine + Environment.NewLine + GetUsageInfo() ); @@ -85,6 +85,10 @@ namespace Volo.Abp.Cli.Commands sb.AppendLine(""); sb.AppendLine("-wd|--working-directory (default: empty)"); sb.AppendLine("-f | --force (default: false)"); + sb.AppendLine("-f | --force (default: false)"); + sb.AppendLine("-b | --bundle (default: false)"); + sb.AppendLine("-m | --minify (default: false)"); + sb.AppendLine("-n | --name (default: empty)"); sb.AppendLine(""); sb.AppendLine("See the documentation for more info: https://docs.abp.io/en/abp/latest/CLI"); From 06ac3a515b6514808a2d05b7cad60e3dca684c35 Mon Sep 17 00:00:00 2001 From: Ilkay Ilknur Date: Fri, 27 Nov 2020 14:10:56 +0300 Subject: [PATCH 4/8] typo fixes. --- .../Volo/Abp/Cli/Bundling/BundlingService.cs | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/BundlingService.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/BundlingService.cs index 1f731d96f8..11893c72e1 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/BundlingService.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/BundlingService.cs @@ -53,7 +53,7 @@ namespace Volo.Abp.Cli.Bundling var startupModule = GetStartupModule(assemblyFilePath); var bundleDefinitions = new List(); - FindBundleContributersRecursively(startupModule, 0, bundleDefinitions); + FindBundleContributorsRecursively(startupModule, 0, bundleDefinitions); bundleDefinitions = bundleDefinitions.OrderByDescending(t => t.Level).ToList(); var styleContext = GetStyleContext(bundleDefinitions); @@ -89,8 +89,8 @@ namespace Volo.Abp.Cli.Bundling foreach (var bundleDefinition in bundleDefinitions) { - var contributer = CreateContributerInstance(bundleDefinition.BundleContributerType); - contributer.AddScripts(scriptContext); + var contributor = CreateContributerInstance(bundleDefinition.BundleContributerType); + contributor.AddScripts(scriptContext); } scriptContext.Add("_framework/blazor.webassembly.js"); @@ -103,8 +103,8 @@ namespace Volo.Abp.Cli.Bundling foreach (var bundleDefinition in bundleDefinitions) { - var contributer = CreateContributerInstance(bundleDefinition.BundleContributerType); - contributer.AddStyles(styleContext); + var contributor = CreateContributerInstance(bundleDefinition.BundleContributerType); + contributor.AddStyles(styleContext); } return styleContext; @@ -190,25 +190,25 @@ namespace Volo.Abp.Cli.Bundling return (IBundleContributor)Activator.CreateInstance(bundleContributerType); } - private void FindBundleContributersRecursively( + private void FindBundleContributorsRecursively( Type module, int level, List bundleDefinitions) { - var bundleContributers = module.Assembly + var bundleContributors = module.Assembly .GetTypes() .Where(t => t.IsAssignableTo()) .ToList(); - if (bundleContributers.Count > 1) + if (bundleContributors.Count > 1) { throw new BundlingException($"Each project must contain only one class implementing {nameof(IBundleContributor)}"); } - if (bundleContributers.Any()) + if (bundleContributors.Any()) { - var bundleContributer = bundleContributers[0]; - var definition = bundleDefinitions.SingleOrDefault(t => t.BundleContributerType == bundleContributer); + var bundleContributor = bundleContributors[0]; + var definition = bundleDefinitions.SingleOrDefault(t => t.BundleContributerType == bundleContributor); if (definition != null) { if (definition.Level < level) @@ -221,7 +221,7 @@ namespace Volo.Abp.Cli.Bundling bundleDefinitions.Add(new BundleTypeDefinition { Level = level, - BundleContributerType = bundleContributer + BundleContributerType = bundleContributor }); } } @@ -234,7 +234,7 @@ namespace Volo.Abp.Cli.Bundling { foreach (var dependedModuleType in descriptor.GetDependedTypes()) { - FindBundleContributersRecursively(dependedModuleType, level + 1, bundleDefinitions); + FindBundleContributorsRecursively(dependedModuleType, level + 1, bundleDefinitions); } } } From 117fb8cd4e6e3f7c4d9d1b4d9fa26cd7862c163c Mon Sep 17 00:00:00 2001 From: Ilkay Ilknur Date: Fri, 27 Nov 2020 14:13:37 +0300 Subject: [PATCH 5/8] additional typo fixes. --- .../Volo/Abp/Cli/Bundling/BundleTypeDefinition.cs | 2 +- .../Volo/Abp/Cli/Bundling/BundlingService.cs | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/BundleTypeDefinition.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/BundleTypeDefinition.cs index 57500a1db5..730be91f53 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/BundleTypeDefinition.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/BundleTypeDefinition.cs @@ -6,6 +6,6 @@ namespace Volo.Abp.Cli.Bundling { public int Level { get; set; } - public Type BundleContributerType { get; set; } + public Type BundleContributorType { get; set; } } } diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/BundlingService.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/BundlingService.cs index 11893c72e1..7a8a04bde1 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/BundlingService.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/BundlingService.cs @@ -89,7 +89,7 @@ namespace Volo.Abp.Cli.Bundling foreach (var bundleDefinition in bundleDefinitions) { - var contributor = CreateContributerInstance(bundleDefinition.BundleContributerType); + var contributor = CreateContributorInstance(bundleDefinition.BundleContributorType); contributor.AddScripts(scriptContext); } @@ -103,7 +103,7 @@ namespace Volo.Abp.Cli.Bundling foreach (var bundleDefinition in bundleDefinitions) { - var contributor = CreateContributerInstance(bundleDefinition.BundleContributerType); + var contributor = CreateContributorInstance(bundleDefinition.BundleContributorType); contributor.AddStyles(styleContext); } @@ -185,9 +185,9 @@ namespace Volo.Abp.Cli.Bundling return builder.ToString(); } - private IBundleContributor CreateContributerInstance(Type bundleContributerType) + private IBundleContributor CreateContributorInstance(Type bundleContributorType) { - return (IBundleContributor)Activator.CreateInstance(bundleContributerType); + return (IBundleContributor)Activator.CreateInstance(bundleContributorType); } private void FindBundleContributorsRecursively( @@ -208,7 +208,7 @@ namespace Volo.Abp.Cli.Bundling if (bundleContributors.Any()) { var bundleContributor = bundleContributors[0]; - var definition = bundleDefinitions.SingleOrDefault(t => t.BundleContributerType == bundleContributor); + var definition = bundleDefinitions.SingleOrDefault(t => t.BundleContributorType == bundleContributor); if (definition != null) { if (definition.Level < level) @@ -221,7 +221,7 @@ namespace Volo.Abp.Cli.Bundling bundleDefinitions.Add(new BundleTypeDefinition { Level = level, - BundleContributerType = bundleContributor + BundleContributorType = bundleContributor }); } } From c390564ae34403fa08c1646d51bf0386589a648f Mon Sep 17 00:00:00 2001 From: Ilkay Ilknur Date: Mon, 30 Nov 2020 11:11:45 +0300 Subject: [PATCH 6/8] missing option added. --- .../Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/BundlingService.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/BundlingService.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/BundlingService.cs index 7a8a04bde1..810d7b4335 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/BundlingService.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/BundlingService.cs @@ -68,7 +68,8 @@ namespace Volo.Abp.Cli.Bundling Directory = directory, FrameworkVersion = frameworkVersion, ProjectFileName = projectName, - BundleName = bundleName + BundleName = bundleName, + Minify = minify }; styleDefinitions = StyleBundler.Bundle(options, styleContext); From 5fab936a835219ec4a7373127f78369206dcaa11 Mon Sep 17 00:00:00 2001 From: Ilkay Ilknur Date: Mon, 30 Nov 2020 11:20:48 +0300 Subject: [PATCH 7/8] remove unneccesary character. --- .../src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/BundlerBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/BundlerBase.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/BundlerBase.cs index de90e4f718..2927a21d29 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/BundlerBase.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/BundlerBase.cs @@ -38,7 +38,7 @@ namespace Volo.Abp.Cli.Bundling { foreach (var suffix in _minFileSuffixes) { - if (fileName.EndsWith($".{suffix}.{FileExtension}", StringComparison.InvariantCultureIgnoreCase)) + if (fileName.EndsWith($".{suffix}{FileExtension}", StringComparison.InvariantCultureIgnoreCase)) { return true; } From 45695db7b822025609e8a99ae4eab85e2811bc91 Mon Sep 17 00:00:00 2001 From: Ilkay Ilknur Date: Mon, 30 Nov 2020 11:38:22 +0300 Subject: [PATCH 8/8] NRE exception fix. --- .../src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/BundlerBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/BundlerBase.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/BundlerBase.cs index 2927a21d29..525548db87 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/BundlerBase.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/BundlerBase.cs @@ -16,7 +16,7 @@ namespace Volo.Abp.Cli.Bundling private static string[] _minFileSuffixes = { "min", "prod" }; protected IMinifier Minifier { get; } - protected ILogger Logger { get; set; } + public ILogger Logger { get; set; } public abstract string FileExtension { get; } public abstract string GenerateDefinition(string bundleFilePath);