Browse Source

Bundling & minification system.

pull/301/head
Halil ibrahim Kalkan 8 years ago
parent
commit
36def34e3d
  1. 5
      src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Bundling/AbpBundleTagHelperServiceBase.cs
  2. 6
      src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/compilerconfig.json
  3. 49
      src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/compilerconfig.json.defaults
  4. 14
      src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Bundling/SharedThemeGlobalScriptContributor.cs
  5. 4
      src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.csproj
  6. 0
      src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/bootstrap/dom-event-handlers.js
  7. 0
      src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/bootstrap/modal-manager.js
  8. 0
      src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/datatables/datatables-extensions.js
  9. 0
      src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/jquery-form/jquery-form-extensions.js
  10. 0
      src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/jquery/jquery-extensions.js
  11. 0
      src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/sweetalert/abp-sweetalert.js
  12. 0
      src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/toastr/abp-toastr.js
  13. 61
      src/Volo.Abp.AspNetCore.Mvc.UI/UI/Bundling/BundleManager.cs
  14. 12
      src/Volo.Abp.AspNetCore.Mvc.UI/UI/Bundling/BundleResult.cs
  15. 18
      src/Volo.Abp.AspNetCore.Mvc.UI/UI/Bundling/Bundler.cs
  16. 53
      src/Volo.Abp.AspNetCore.Mvc.UI/UI/Bundling/BundlerBase.cs
  17. 17
      src/Volo.Abp.AspNetCore.Mvc.UI/UI/Bundling/BundlerContext.cs
  18. 7
      src/Volo.Abp.AspNetCore.Mvc.UI/UI/Bundling/BundlingOptions.cs
  19. 6
      src/Volo.Abp.AspNetCore.Mvc.UI/UI/Bundling/IBundler.cs
  20. 11
      src/Volo.Abp.AspNetCore.Mvc.UI/UI/Bundling/IBundlerContext.cs
  21. 7
      src/Volo.Abp.AspNetCore.Mvc.UI/UI/Bundling/Scripts/IScriptBundler.cs
  22. 15
      src/Volo.Abp.AspNetCore.Mvc.UI/UI/Bundling/Scripts/ScriptBundler.cs
  23. 74
      src/Volo.Abp.AspNetCore.Mvc.UI/UI/Bundling/Styles/CssRelativePathAdjuster.cs
  24. 7
      src/Volo.Abp.AspNetCore.Mvc.UI/UI/Bundling/Styles/IStyleBundler.cs
  25. 27
      src/Volo.Abp.AspNetCore.Mvc.UI/UI/Bundling/Styles/StyleBundler.cs
  26. 2
      src/Volo.Abp.AspNetCore.Mvc.UI/UI/Minification/IMinifier.cs
  27. 13
      src/Volo.Abp.AspNetCore.Mvc.UI/UI/Minification/NUglify/NUglifyCssMinifier.cs
  28. 21
      src/Volo.Abp.AspNetCore.Mvc.UI/UI/Minification/NUglify/NUglifyJavascriptMinifier.cs
  29. 31
      src/Volo.Abp.AspNetCore.Mvc.UI/UI/Minification/NUglify/NUglifyMinifierBase.cs
  30. 7
      src/Volo.Abp.AspNetCore.Mvc.UI/UI/Minification/Scripts/IJavascriptMinifier.cs
  31. 7
      src/Volo.Abp.AspNetCore.Mvc.UI/UI/Minification/Styles/ICssMinifier.cs
  32. 2
      src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/Builder/VirtualFileSystemApplicationBuilderExtensions.cs
  33. 53
      src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/VirtualFileSystem/HybridWebRootFileProvider.cs
  34. 9
      src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/VirtualFileSystem/IHybridWebRootFileProvider.cs
  35. 22
      src/Volo.Abp.Core/Volo/Abp/IO/DirectoryHelper.cs
  36. 22
      src/Volo.Abp.Core/Volo/Abp/IO/FileHelper.cs
  37. 10
      src/Volo.Abp.VirtualFileSystem/Volo/Abp/VirtualFileSystem/VirtualFileProvider.cs

5
src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Bundling/AbpBundleTagHelperServiceBase.cs

@ -30,12 +30,13 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Bundling
CreateBundle(bundleName, files); CreateBundle(bundleName, files);
var bundleFiles = GetBundleFiles(bundleName); var bundleFiles = GetBundleFiles(bundleName);
await output.GetChildContentAsync(); //TODO: Suppress child execution!
output.Content.Clear(); output.Content.Clear();
AddHtmlTags(context, output, bundleFiles); AddHtmlTags(context, output, bundleFiles);
} }
protected abstract void CreateBundle(string bundleName, List<string> files); protected abstract void CreateBundle(string bundleName, List<string> files);
protected abstract List<string> GetBundleFiles(string bundleName); protected abstract List<string> GetBundleFiles(string bundleName);
protected abstract void AddHtmlTags(TagHelperContext context, TagHelperOutput output, List<string> files); protected abstract void AddHtmlTags(TagHelperContext context, TagHelperOutput output, List<string> files);
@ -49,7 +50,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Bundling
{ {
var fileList = new List<string>(); var fileList = new List<string>();
context.Items[AbpBundleFileTagHelperService.ContextFileListKey] = fileList; context.Items[AbpBundleFileTagHelperService.ContextFileListKey] = fileList;
await output.GetChildContentAsync(); await output.GetChildContentAsync(); //TODO: Suppress child execution!
return fileList; return fileList;
} }
} }

6
src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/compilerconfig.json

@ -1,6 +0,0 @@
[
{
"outputFile": "wwwroot/libs/abp/aspnetcore.mvc.ui.theme.shared/datatables/datatables.css",
"inputFile": "wwwroot/libs/abp/aspnetcore.mvc.ui.theme.shared/datatables/datatables.scss"
}
]

49
src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/compilerconfig.json.defaults

@ -1,49 +0,0 @@
{
"compilers": {
"less": {
"autoPrefix": "",
"cssComb": "none",
"ieCompat": true,
"strictMath": false,
"strictUnits": false,
"relativeUrls": true,
"rootPath": "",
"sourceMapRoot": "",
"sourceMapBasePath": "",
"sourceMap": false
},
"sass": {
"includePath": "",
"indentType": "space",
"indentWidth": 2,
"outputStyle": "nested",
"Precision": 5,
"relativeUrls": true,
"sourceMapRoot": "",
"sourceMap": false
},
"stylus": {
"sourceMap": false
},
"babel": {
"sourceMap": false
},
"coffeescript": {
"bare": false,
"runtimeMode": "node",
"sourceMap": false
}
},
"minifiers": {
"css": {
"enabled": true,
"termSemicolons": true,
"gzip": false
},
"javascript": {
"enabled": true,
"termSemicolons": true,
"gzip": false
}
}
}

14
src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Bundling/SharedThemeGlobalScriptContributor.cs

@ -25,13 +25,13 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Bundling
{ {
context.Files.AddRange(new[] context.Files.AddRange(new[]
{ {
"/libs/abp/aspnetcore.mvc.ui.theme.shared/jquery/jquery-extensions.js", "/libs/abp/aspnetcore-mvc-ui-theme-shared/jquery/jquery-extensions.js",
"/libs/abp/aspnetcore.mvc.ui.theme.shared/jquery-form/jquery-form-extensions.js", "/libs/abp/aspnetcore-mvc-ui-theme-shared/jquery-form/jquery-form-extensions.js",
"/libs/abp/aspnetcore.mvc.ui.theme.shared/bootstrap/dom-event-handlers.js", "/libs/abp/aspnetcore-mvc-ui-theme-shared/bootstrap/dom-event-handlers.js",
"/libs/abp/aspnetcore.mvc.ui.theme.shared/bootstrap/modal-manager.js", "/libs/abp/aspnetcore-mvc-ui-theme-shared/bootstrap/modal-manager.js",
"/libs/abp/aspnetcore.mvc.ui.theme.shared/datatables/datatables-extensions.js", "/libs/abp/aspnetcore-mvc-ui-theme-shared/datatables/datatables-extensions.js",
"/libs/abp/aspnetcore.mvc.ui.theme.shared/sweetalert/abp-sweetalert.js", "/libs/abp/aspnetcore-mvc-ui-theme-shared/sweetalert/abp-sweetalert.js",
"/libs/abp/aspnetcore.mvc.ui.theme.shared/toastr/abp-toastr.js" "/libs/abp/aspnetcore-mvc-ui-theme-shared/toastr/abp-toastr.js"
}); });
} }
} }

4
src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.csproj

@ -21,10 +21,6 @@
<EmbeddedResource Include="Areas\**\*.cshtml" /> <EmbeddedResource Include="Areas\**\*.cshtml" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<EmbeddedResource Remove="Pages\Error\Index.cshtml" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Diagnostics" Version="2.1.0" /> <PackageReference Include="Microsoft.AspNetCore.Diagnostics" Version="2.1.0" />
</ItemGroup> </ItemGroup>

0
src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore.mvc.ui.theme.shared/bootstrap/dom-event-handlers.js → src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/bootstrap/dom-event-handlers.js

0
src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore.mvc.ui.theme.shared/bootstrap/modal-manager.js → src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/bootstrap/modal-manager.js

0
src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore.mvc.ui.theme.shared/datatables/datatables-extensions.js → src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/datatables/datatables-extensions.js

0
src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore.mvc.ui.theme.shared/jquery-form/jquery-form-extensions.js → src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/jquery-form/jquery-form-extensions.js

0
src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore.mvc.ui.theme.shared/jquery/jquery-extensions.js → src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/jquery/jquery-extensions.js

0
src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore.mvc.ui.theme.shared/sweetalert/abp-sweetalert.js → src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/sweetalert/abp-sweetalert.js

0
src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore.mvc.ui.theme.shared/toastr/abp-toastr.js → src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/toastr/abp-toastr.js

61
src/Volo.Abp.AspNetCore.Mvc.UI/UI/Bundling/BundleManager.cs

@ -1,11 +1,16 @@
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Text;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Volo.Abp.AspNetCore.Mvc.UI.Bundling.Scripts;
using Volo.Abp.AspNetCore.Mvc.UI.Bundling.Styles;
using Volo.Abp.AspNetCore.Mvc.UI.Theming; using Volo.Abp.AspNetCore.Mvc.UI.Theming;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
using Volo.Abp.IO;
namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling
{ {
@ -14,7 +19,8 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling
{ {
private readonly BundlingOptions _options; private readonly BundlingOptions _options;
private readonly IHostingEnvironment _hostingEnvironment; private readonly IHostingEnvironment _hostingEnvironment;
private readonly IBundler _bundler; private readonly IScriptBundler _scriptBundler;
private readonly IStyleBundler _styleBundler;
private readonly IThemeManager _themeManager; private readonly IThemeManager _themeManager;
private readonly IServiceProvider _serviceProvider; private readonly IServiceProvider _serviceProvider;
@ -22,15 +28,17 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling
public BundleManager( public BundleManager(
IOptions<BundlingOptions> options, IOptions<BundlingOptions> options,
IScriptBundler scriptBundler,
IStyleBundler styleBundler,
IHostingEnvironment hostingEnvironment, IHostingEnvironment hostingEnvironment,
IBundler bundler,
IThemeManager themeManager, IThemeManager themeManager,
IServiceProvider serviceProvider) IServiceProvider serviceProvider)
{ {
_hostingEnvironment = hostingEnvironment; _hostingEnvironment = hostingEnvironment;
_bundler = bundler; _scriptBundler = scriptBundler;
_themeManager = themeManager; _themeManager = themeManager;
_serviceProvider = serviceProvider; _serviceProvider = serviceProvider;
_styleBundler = styleBundler;
_options = options.Value; _options = options.Value;
_cache = new ConcurrentDictionary<string, string>(); _cache = new ConcurrentDictionary<string, string>();
@ -38,28 +46,63 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling
public List<string> GetStyleBundleFiles(string bundleName) public List<string> GetStyleBundleFiles(string bundleName)
{ {
return GetFiles(_options.StyleBundles.Get(bundleName)); return GetBundleFiles(_options.StyleBundles.Get(bundleName), _styleBundler);
} }
public List<string> GetScriptBundleFiles(string bundleName) public List<string> GetScriptBundleFiles(string bundleName)
{ {
return GetFiles(_options.ScriptBundles.Get(bundleName)); return GetBundleFiles(_options.ScriptBundles.Get(bundleName), _scriptBundler);
} }
public void CreateStyleBundle(string bundleName, Action<BundleConfiguration> configureAction) protected virtual List<string> GetBundleFiles(BundleConfiguration bundleConfiguration, IBundler bundler)
{
//TODO: Caching
//TODO: Concurrency
var files = CreateFileListFromConfiguration(bundleConfiguration);
if (!IsBundlingEnabled())
{
return files;
}
var bundleRelativePath = _options.BundleFolderName.EnsureEndsWith('/') + bundleConfiguration.Name + "." + bundler.FileExtension;
var bundleResult = bundler.Bundle(new BundlerContext(bundleRelativePath, files));
SaveBundleResult(bundleRelativePath, bundleResult);
return new List<string>
{
"/" + bundleRelativePath
};
}
protected virtual void SaveBundleResult(string bundleRelativePath, BundleResult bundleResult)
{
var bundleFilePath = Path.Combine(_hostingEnvironment.WebRootPath, bundleRelativePath);
DirectoryHelper.CreateIfNotExists(Path.GetDirectoryName(bundleFilePath));
File.WriteAllText(bundleFilePath, bundleResult.Content, Encoding.UTF8);
}
public virtual void CreateStyleBundle(string bundleName, Action<BundleConfiguration> configureAction)
{ {
_options.StyleBundles.GetOrAdd(bundleName, configureAction); _options.StyleBundles.GetOrAdd(bundleName, configureAction);
} }
public void CreateScriptBundle(string bundleName, Action<BundleConfiguration> configureAction) public virtual void CreateScriptBundle(string bundleName, Action<BundleConfiguration> configureAction)
{ {
_options.ScriptBundles.GetOrAdd(bundleName, configureAction); _options.ScriptBundles.GetOrAdd(bundleName, configureAction);
} }
protected virtual List<string> GetFiles(BundleConfiguration bundleConfiguration) protected virtual bool IsBundlingEnabled()
{ {
//TODO: Caching, Bundling & Minifying! return true;
//return !_hostingEnvironment.IsDevelopment();
}
protected virtual List<string> CreateFileListFromConfiguration(BundleConfiguration bundleConfiguration)
{
using (var scope = _serviceProvider.CreateScope()) using (var scope = _serviceProvider.CreateScope())
{ {
var context = new BundleConfigurationContext( var context = new BundleConfigurationContext(

12
src/Volo.Abp.AspNetCore.Mvc.UI/UI/Bundling/BundleResult.cs

@ -0,0 +1,12 @@
namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling
{
public class BundleResult
{
public string Content { get; }
public BundleResult(string content)
{
Content = content;
}
}
}

18
src/Volo.Abp.AspNetCore.Mvc.UI/UI/Bundling/Bundler.cs

@ -1,18 +0,0 @@
using System.Collections.Generic;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling
{
public class Bundler : IBundler, ITransientDependency
{
public Bundler()
{
}
public string CreateBundle(List<string> files)
{
return "";
}
}
}

53
src/Volo.Abp.AspNetCore.Mvc.UI/UI/Bundling/BundlerBase.cs

@ -0,0 +1,53 @@
using System.Text;
using Microsoft.Extensions.FileProviders;
using Volo.Abp.AspNetCore.Mvc.UI.Minification;
using Volo.Abp.AspNetCore.VirtualFileSystem;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling
{
public abstract class BundlerBase : IBundler, ITransientDependency
{
protected IHybridWebRootFileProvider WebRootFileProvider { get; }
protected IMinifier Minifier { get; }
protected BundlerBase(IHybridWebRootFileProvider webRootFileProvider, IMinifier minifier)
{
WebRootFileProvider = webRootFileProvider;
Minifier = minifier;
}
public abstract string FileExtension { get; }
public BundleResult Bundle(IBundlerContext context)
{
var sb = new StringBuilder();
foreach (var file in context.ContentFiles)
{
sb.AppendLine(GetFileContent(context, file));
}
return new BundleResult(
Minifier.Minify(sb.ToString(), context.BundleRelativePath)
);
}
protected virtual string GetFileContent(IBundlerContext context, string file)
{
return GetFileInfo(context, file).ReadAsString();
}
protected virtual IFileInfo GetFileInfo(IBundlerContext context, string file)
{
var fileInfo = WebRootFileProvider.GetFileInfo(file);
if (!fileInfo.Exists)
{
throw new AbpException($"Could not find file '{file}' using {nameof(IHybridWebRootFileProvider)}");
}
return fileInfo;
}
}
}

17
src/Volo.Abp.AspNetCore.Mvc.UI/UI/Bundling/BundlerContext.cs

@ -0,0 +1,17 @@
using System.Collections.Generic;
namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling
{
public class BundlerContext : IBundlerContext
{
public string BundleRelativePath { get; }
public IReadOnlyList<string> ContentFiles { get; }
public BundlerContext(string bundleRelativePath, IReadOnlyList<string> contentFiles)
{
BundleRelativePath = bundleRelativePath;
ContentFiles = contentFiles;
}
}
}

7
src/Volo.Abp.AspNetCore.Mvc.UI/UI/Bundling/BundlingOptions.cs

@ -6,6 +6,13 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling
public BundleConfigurationCollection ScriptBundles { get; set; } public BundleConfigurationCollection ScriptBundles { get; set; }
//TODO: Add option to enable/disable bundling / minification
/// <summary>
/// Default: "__bundles".
/// </summary>
public string BundleFolderName { get; } = "__bundles";
public BundlingOptions() public BundlingOptions()
{ {
StyleBundles = new BundleConfigurationCollection(); StyleBundles = new BundleConfigurationCollection();

6
src/Volo.Abp.AspNetCore.Mvc.UI/UI/Bundling/IBundler.cs

@ -1,9 +1,9 @@
using System.Collections.Generic;
namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling
{ {
public interface IBundler public interface IBundler
{ {
string CreateBundle(List<string> files); string FileExtension { get; }
BundleResult Bundle(IBundlerContext context);
} }
} }

11
src/Volo.Abp.AspNetCore.Mvc.UI/UI/Bundling/IBundlerContext.cs

@ -0,0 +1,11 @@
using System.Collections.Generic;
namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling
{
public interface IBundlerContext
{
string BundleRelativePath { get; }
IReadOnlyList<string> ContentFiles { get; }
}
}

7
src/Volo.Abp.AspNetCore.Mvc.UI/UI/Bundling/Scripts/IScriptBundler.cs

@ -0,0 +1,7 @@
namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling.Scripts
{
public interface IScriptBundler : IBundler
{
}
}

15
src/Volo.Abp.AspNetCore.Mvc.UI/UI/Bundling/Scripts/ScriptBundler.cs

@ -0,0 +1,15 @@
using Volo.Abp.AspNetCore.Mvc.UI.Minification.Scripts;
using Volo.Abp.AspNetCore.VirtualFileSystem;
namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling.Scripts
{
public class ScriptBundler : BundlerBase, IScriptBundler
{
public override string FileExtension => "js";
public ScriptBundler(IHybridWebRootFileProvider webRootFileProvider, IJavascriptMinifier minifier)
: base(webRootFileProvider, minifier)
{
}
}
}

74
src/Volo.Abp.AspNetCore.Mvc.UI/UI/Bundling/Styles/CssRelativePathAdjuster.cs

@ -0,0 +1,74 @@
using System;
using System.IO;
using System.Text.RegularExpressions;
namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling.Styles
{
internal static class CssRelativePath
{
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();
}
}
}
}

7
src/Volo.Abp.AspNetCore.Mvc.UI/UI/Bundling/Styles/IStyleBundler.cs

@ -0,0 +1,7 @@
namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling.Styles
{
public interface IStyleBundler : IBundler
{
}
}

27
src/Volo.Abp.AspNetCore.Mvc.UI/UI/Bundling/Styles/StyleBundler.cs

@ -0,0 +1,27 @@
using System;
using Volo.Abp.AspNetCore.Mvc.UI.Minification.Styles;
using Volo.Abp.AspNetCore.VirtualFileSystem;
namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling.Styles
{
public class StyleBundler : BundlerBase, IStyleBundler
{
public override string FileExtension => "css";
public StyleBundler(IHybridWebRootFileProvider webRootFileProvider, ICssMinifier minifier)
: base(webRootFileProvider, minifier)
{
}
protected override string GetFileContent(IBundlerContext context, string file)
{
var content = base.GetFileContent(context, file);
return CssRelativePath.Adjust(
content,
WebRootFileProvider.GetAbsolutePath(file),
WebRootFileProvider.GetAbsolutePath(context.BundleRelativePath)
);
}
}
}

2
src/Volo.Abp.AspNetCore.Mvc.UI/UI/Minification/IJavascriptMinifier.cs → src/Volo.Abp.AspNetCore.Mvc.UI/UI/Minification/IMinifier.cs

@ -1,6 +1,6 @@
namespace Volo.Abp.AspNetCore.Mvc.UI.Minification namespace Volo.Abp.AspNetCore.Mvc.UI.Minification
{ {
public interface IJavascriptMinifier public interface IMinifier
{ {
string Minify(string source, string fileName = null); string Minify(string source, string fileName = null);
} }

13
src/Volo.Abp.AspNetCore.Mvc.UI/UI/Minification/NUglify/NUglifyCssMinifier.cs

@ -0,0 +1,13 @@
using NUglify;
using Volo.Abp.AspNetCore.Mvc.UI.Minification.Styles;
namespace Volo.Abp.AspNetCore.Mvc.UI.Minification.NUglify
{
public class NUglifyCssMinifier : NUglifyMinifierBase, ICssMinifier
{
protected override UglifyResult UglifySource(string source, string fileName)
{
return Uglify.Css(source, fileName);
}
}
}

21
src/Volo.Abp.AspNetCore.Mvc.UI/UI/Minification/NUglify/NUglifyJavascriptMinifier.cs

@ -1,26 +1,15 @@
using NUglify; using NUglify;
using NUglify.JavaScript;
using Volo.Abp.AspNetCore.Mvc.UI.Minification.Scripts;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
namespace Volo.Abp.AspNetCore.Mvc.UI.Minification.NUglify namespace Volo.Abp.AspNetCore.Mvc.UI.Minification.NUglify
{ {
public class NUglifyJavascriptMinifier : IJavascriptMinifier, ITransientDependency public class NUglifyJavascriptMinifier : NUglifyMinifierBase, IJavascriptMinifier
{ {
public string Minify(string source, string fileName = null) protected override UglifyResult UglifySource(string source, string fileName)
{ {
var result = Uglify.Js(source, fileName); return Uglify.Js(source, fileName);
CheckErrors(result);
return result.Code;
}
private static void CheckErrors(UglifyResult result)
{
if (result.HasErrors)
{
throw new NUglifyException(
"There are some errors on uglifying the given source code!",
result.Errors
);
}
} }
} }
} }

31
src/Volo.Abp.AspNetCore.Mvc.UI/UI/Minification/NUglify/NUglifyMinifierBase.cs

@ -0,0 +1,31 @@
using System;
using System.Collections.Generic;
using System.Linq;
using NUglify;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.AspNetCore.Mvc.UI.Minification.NUglify
{
public abstract class NUglifyMinifierBase : IMinifier, ITransientDependency
{
private static void CheckErrors(UglifyResult result)
{
if (result.HasErrors)
{
throw new NUglifyException(
$"There are some errors on uglifying the given source code!{Environment.NewLine}{result.Errors.Select(err => err.ToString()).JoinAsString(Environment.NewLine)}",
result.Errors
);
}
}
public string Minify(string source, string fileName = null)
{
var result = UglifySource(source, fileName);
CheckErrors(result);
return result.Code;
}
protected abstract UglifyResult UglifySource(string source, string fileName);
}
}

7
src/Volo.Abp.AspNetCore.Mvc.UI/UI/Minification/Scripts/IJavascriptMinifier.cs

@ -0,0 +1,7 @@
namespace Volo.Abp.AspNetCore.Mvc.UI.Minification.Scripts
{
public interface IJavascriptMinifier : IMinifier
{
}
}

7
src/Volo.Abp.AspNetCore.Mvc.UI/UI/Minification/Styles/ICssMinifier.cs

@ -0,0 +1,7 @@
namespace Volo.Abp.AspNetCore.Mvc.UI.Minification.Styles
{
public interface ICssMinifier : IMinifier
{
}
}

2
src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/Builder/VirtualFileSystemApplicationBuilderExtensions.cs

@ -13,7 +13,7 @@ namespace Microsoft.AspNetCore.Builder
IFileProvider fileProvider = new AspNetCoreVirtualFileProvider( IFileProvider fileProvider = new AspNetCoreVirtualFileProvider(
app.ApplicationServices, app.ApplicationServices,
"/wwwroot" "/wwwroot" //TODO: Hard-coded "/wwwroot" is not good!
); );
app.UseStaticFiles( app.UseStaticFiles(

53
src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/VirtualFileSystem/HybridWebRootFileProvider.cs

@ -0,0 +1,53 @@
using System;
using System.IO;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Primitives;
using Volo.Abp.DependencyInjection;
using Volo.Abp.VirtualFileSystem;
namespace Volo.Abp.AspNetCore.VirtualFileSystem
{
public class HybridWebRootFileProvider : IHybridWebRootFileProvider, ISingletonDependency
{
private readonly IVirtualFileProvider _virtualFileProvider;
private readonly IFileProvider _fileProvider;
private readonly IHostingEnvironment _hostingEnvironment;
public HybridWebRootFileProvider(IVirtualFileProvider virtualFileProvider, IHostingEnvironment hostingEnvironment)
{
_virtualFileProvider = virtualFileProvider;
_hostingEnvironment = hostingEnvironment;
_fileProvider = CreateHybridProvider();
}
public virtual IFileInfo GetFileInfo(string subpath)
{
return _fileProvider.GetFileInfo("/wwwroot" + subpath); //TODO: Hard-coded "/wwwroot" is not good!
}
public virtual IDirectoryContents GetDirectoryContents(string subpath)
{
return _fileProvider.GetDirectoryContents("/wwwroot" + subpath);
}
public virtual IChangeToken Watch(string filter)
{
return _fileProvider.Watch("/wwwroot" + filter);
}
public string GetAbsolutePath(string relativePath)
{
return Path.Combine(_hostingEnvironment.ContentRootPath, "wwwroot", relativePath.RemovePreFix("/"));
}
protected virtual IFileProvider CreateHybridProvider()
{
return new CompositeFileProvider(
_hostingEnvironment.ContentRootFileProvider,
_virtualFileProvider
);
}
}
}

9
src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/VirtualFileSystem/IHybridWebRootFileProvider.cs

@ -0,0 +1,9 @@
using Microsoft.Extensions.FileProviders;
namespace Volo.Abp.AspNetCore.VirtualFileSystem
{
public interface IHybridWebRootFileProvider : IFileProvider
{
string GetAbsolutePath(string relativePath);
}
}

22
src/Volo.Abp.Core/Volo/Abp/IO/DirectoryHelper.cs

@ -0,0 +1,22 @@
using System.IO;
namespace Volo.Abp.IO
{
/// <summary>
/// A helper class for Directory operations.
/// </summary>
public static class DirectoryHelper
{
/// <summary>
/// Creates a new directory if it does not exists.
/// </summary>
/// <param name="directory">Directory to create</param>
public static void CreateIfNotExists(string directory)
{
if (!Directory.Exists(directory))
{
Directory.CreateDirectory(directory);
}
}
}
}

22
src/Volo.Abp.Core/Volo/Abp/IO/FileHelper.cs

@ -0,0 +1,22 @@
using System.IO;
namespace Volo.Abp.IO
{
/// <summary>
/// A helper class for File operations.
/// </summary>
public static class FileHelper
{
/// <summary>
/// Checks and deletes given file if it does exists.
/// </summary>
/// <param name="filePath">Path of the file</param>
public static void DeleteIfExists(string filePath)
{
if (File.Exists(filePath))
{
File.Delete(filePath);
}
}
}
}

10
src/Volo.Abp.VirtualFileSystem/Volo/Abp/VirtualFileSystem/VirtualFileProvider.cs

@ -19,22 +19,22 @@ namespace Volo.Abp.VirtualFileSystem
_fileProvider = CreateHybridProvider(); _fileProvider = CreateHybridProvider();
} }
public IFileInfo GetFileInfo(string subpath) public virtual IFileInfo GetFileInfo(string subpath)
{ {
return _fileProvider.GetFileInfo(subpath); return _fileProvider.GetFileInfo(subpath);
} }
public IDirectoryContents GetDirectoryContents(string subpath) public virtual IDirectoryContents GetDirectoryContents(string subpath)
{ {
return _fileProvider.GetDirectoryContents(subpath); return _fileProvider.GetDirectoryContents(subpath);
} }
public IChangeToken Watch(string filter) public virtual IChangeToken Watch(string filter)
{ {
return _fileProvider.Watch(filter); return _fileProvider.Watch(filter);
} }
private IFileProvider CreateHybridProvider() protected virtual IFileProvider CreateHybridProvider()
{ {
IFileProvider fileProvider = new InternalVirtualFileProvider(_options); IFileProvider fileProvider = new InternalVirtualFileProvider(_options);
@ -54,7 +54,7 @@ namespace Volo.Abp.VirtualFileSystem
return fileProvider; return fileProvider;
} }
private class InternalVirtualFileProvider : IFileProvider protected class InternalVirtualFileProvider : IFileProvider
{ {
private readonly VirtualFileSystemOptions _options; private readonly VirtualFileSystemOptions _options;
private readonly Lazy<Dictionary<string, IFileInfo>> _files; private readonly Lazy<Dictionary<string, IFileInfo>> _files;

Loading…
Cancel
Save