From dcac0ddce827f0e258ca10444473c42558223d28 Mon Sep 17 00:00:00 2001 From: Ilkay Ilknur Date: Thu, 26 Nov 2020 17:05:17 +0300 Subject: [PATCH] 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"; + } } } }