From 36def34e3dc4057da19ea7133f81127b5939e64e Mon Sep 17 00:00:00 2001 From: Halil ibrahim Kalkan Date: Mon, 11 Jun 2018 17:38:13 +0300 Subject: [PATCH] Bundling & minification system. --- .../Bundling/AbpBundleTagHelperServiceBase.cs | 5 +- .../compilerconfig.json | 6 -- .../compilerconfig.json.defaults | 49 ------------ .../SharedThemeGlobalScriptContributor.cs | 14 ++-- ....Abp.AspNetCore.Mvc.UI.Theme.Shared.csproj | 4 - .../bootstrap/dom-event-handlers.js | 0 .../bootstrap/modal-manager.js | 0 .../datatables/datatables-extensions.js | 0 .../jquery-form/jquery-form-extensions.js | 0 .../jquery/jquery-extensions.js | 0 .../sweetalert/abp-sweetalert.js | 0 .../toastr/abp-toastr.js | 0 .../UI/Bundling/BundleManager.cs | 61 ++++++++++++--- .../UI/Bundling/BundleResult.cs | 12 +++ .../UI/Bundling/Bundler.cs | 18 ----- .../UI/Bundling/BundlerBase.cs | 53 +++++++++++++ .../UI/Bundling/BundlerContext.cs | 17 +++++ .../UI/Bundling/BundlingOptions.cs | 7 ++ .../UI/Bundling/IBundler.cs | 6 +- .../UI/Bundling/IBundlerContext.cs | 11 +++ .../UI/Bundling/Scripts/IScriptBundler.cs | 7 ++ .../UI/Bundling/Scripts/ScriptBundler.cs | 15 ++++ .../Styles/CssRelativePathAdjuster.cs | 74 +++++++++++++++++++ .../UI/Bundling/Styles/IStyleBundler.cs | 7 ++ .../UI/Bundling/Styles/StyleBundler.cs | 27 +++++++ .../{IJavascriptMinifier.cs => IMinifier.cs} | 2 +- .../NUglify/NUglifyCssMinifier.cs | 13 ++++ .../NUglify/NUglifyJavascriptMinifier.cs | 21 ++---- .../NUglify/NUglifyMinifierBase.cs | 31 ++++++++ .../Scripts/IJavascriptMinifier.cs | 7 ++ .../UI/Minification/Styles/ICssMinifier.cs | 7 ++ ...lFileSystemApplicationBuilderExtensions.cs | 2 +- .../HybridWebRootFileProvider.cs | 53 +++++++++++++ .../IHybridWebRootFileProvider.cs | 9 +++ .../Volo/Abp/IO/DirectoryHelper.cs | 22 ++++++ src/Volo.Abp.Core/Volo/Abp/IO/FileHelper.cs | 22 ++++++ .../VirtualFileSystem/VirtualFileProvider.cs | 10 +-- 37 files changed, 471 insertions(+), 121 deletions(-) delete mode 100644 src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/compilerconfig.json delete mode 100644 src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/compilerconfig.json.defaults rename src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/{aspnetcore.mvc.ui.theme.shared => aspnetcore-mvc-ui-theme-shared}/bootstrap/dom-event-handlers.js (100%) rename src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/{aspnetcore.mvc.ui.theme.shared => aspnetcore-mvc-ui-theme-shared}/bootstrap/modal-manager.js (100%) rename src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/{aspnetcore.mvc.ui.theme.shared => aspnetcore-mvc-ui-theme-shared}/datatables/datatables-extensions.js (100%) rename src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/{aspnetcore.mvc.ui.theme.shared => aspnetcore-mvc-ui-theme-shared}/jquery-form/jquery-form-extensions.js (100%) rename src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/{aspnetcore.mvc.ui.theme.shared => aspnetcore-mvc-ui-theme-shared}/jquery/jquery-extensions.js (100%) rename src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/{aspnetcore.mvc.ui.theme.shared => aspnetcore-mvc-ui-theme-shared}/sweetalert/abp-sweetalert.js (100%) rename src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/{aspnetcore.mvc.ui.theme.shared => aspnetcore-mvc-ui-theme-shared}/toastr/abp-toastr.js (100%) create mode 100644 src/Volo.Abp.AspNetCore.Mvc.UI/UI/Bundling/BundleResult.cs delete mode 100644 src/Volo.Abp.AspNetCore.Mvc.UI/UI/Bundling/Bundler.cs create mode 100644 src/Volo.Abp.AspNetCore.Mvc.UI/UI/Bundling/BundlerBase.cs create mode 100644 src/Volo.Abp.AspNetCore.Mvc.UI/UI/Bundling/BundlerContext.cs create mode 100644 src/Volo.Abp.AspNetCore.Mvc.UI/UI/Bundling/IBundlerContext.cs create mode 100644 src/Volo.Abp.AspNetCore.Mvc.UI/UI/Bundling/Scripts/IScriptBundler.cs create mode 100644 src/Volo.Abp.AspNetCore.Mvc.UI/UI/Bundling/Scripts/ScriptBundler.cs create mode 100644 src/Volo.Abp.AspNetCore.Mvc.UI/UI/Bundling/Styles/CssRelativePathAdjuster.cs create mode 100644 src/Volo.Abp.AspNetCore.Mvc.UI/UI/Bundling/Styles/IStyleBundler.cs create mode 100644 src/Volo.Abp.AspNetCore.Mvc.UI/UI/Bundling/Styles/StyleBundler.cs rename src/Volo.Abp.AspNetCore.Mvc.UI/UI/Minification/{IJavascriptMinifier.cs => IMinifier.cs} (75%) create mode 100644 src/Volo.Abp.AspNetCore.Mvc.UI/UI/Minification/NUglify/NUglifyCssMinifier.cs create mode 100644 src/Volo.Abp.AspNetCore.Mvc.UI/UI/Minification/NUglify/NUglifyMinifierBase.cs create mode 100644 src/Volo.Abp.AspNetCore.Mvc.UI/UI/Minification/Scripts/IJavascriptMinifier.cs create mode 100644 src/Volo.Abp.AspNetCore.Mvc.UI/UI/Minification/Styles/ICssMinifier.cs create mode 100644 src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/VirtualFileSystem/HybridWebRootFileProvider.cs create mode 100644 src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/VirtualFileSystem/IHybridWebRootFileProvider.cs create mode 100644 src/Volo.Abp.Core/Volo/Abp/IO/DirectoryHelper.cs create mode 100644 src/Volo.Abp.Core/Volo/Abp/IO/FileHelper.cs diff --git a/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Bundling/AbpBundleTagHelperServiceBase.cs b/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Bundling/AbpBundleTagHelperServiceBase.cs index ad4126abfc..d2ba25b79e 100644 --- a/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Bundling/AbpBundleTagHelperServiceBase.cs +++ b/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); var bundleFiles = GetBundleFiles(bundleName); - await output.GetChildContentAsync(); //TODO: Suppress child execution! + output.Content.Clear(); AddHtmlTags(context, output, bundleFiles); } protected abstract void CreateBundle(string bundleName, List files); + protected abstract List GetBundleFiles(string bundleName); protected abstract void AddHtmlTags(TagHelperContext context, TagHelperOutput output, List files); @@ -49,7 +50,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Bundling { var fileList = new List(); context.Items[AbpBundleFileTagHelperService.ContextFileListKey] = fileList; - await output.GetChildContentAsync(); + await output.GetChildContentAsync(); //TODO: Suppress child execution! return fileList; } } diff --git a/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/compilerconfig.json b/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/compilerconfig.json deleted file mode 100644 index 305d31c48d..0000000000 --- a/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/compilerconfig.json +++ /dev/null @@ -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" - } -] \ No newline at end of file diff --git a/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/compilerconfig.json.defaults b/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/compilerconfig.json.defaults deleted file mode 100644 index c75eb7d519..0000000000 --- a/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/compilerconfig.json.defaults +++ /dev/null @@ -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 - } - } -} \ No newline at end of file diff --git a/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Bundling/SharedThemeGlobalScriptContributor.cs b/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Bundling/SharedThemeGlobalScriptContributor.cs index 1c88c9d8d9..f73c38dd36 100644 --- a/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Bundling/SharedThemeGlobalScriptContributor.cs +++ b/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[] { - "/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/bootstrap/dom-event-handlers.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/sweetalert/abp-sweetalert.js", - "/libs/abp/aspnetcore.mvc.ui.theme.shared/toastr/abp-toastr.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/bootstrap/dom-event-handlers.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/sweetalert/abp-sweetalert.js", + "/libs/abp/aspnetcore-mvc-ui-theme-shared/toastr/abp-toastr.js" }); } } diff --git a/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.csproj b/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.csproj index 3d3b7e7a3e..051eece443 100644 --- a/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.csproj +++ b/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.csproj @@ -21,10 +21,6 @@ - - - - diff --git a/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore.mvc.ui.theme.shared/bootstrap/dom-event-handlers.js b/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/bootstrap/dom-event-handlers.js similarity index 100% rename from src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore.mvc.ui.theme.shared/bootstrap/dom-event-handlers.js rename to src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/bootstrap/dom-event-handlers.js diff --git a/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore.mvc.ui.theme.shared/bootstrap/modal-manager.js b/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/bootstrap/modal-manager.js similarity index 100% rename from src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore.mvc.ui.theme.shared/bootstrap/modal-manager.js rename to src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/bootstrap/modal-manager.js diff --git a/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore.mvc.ui.theme.shared/datatables/datatables-extensions.js b/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/datatables/datatables-extensions.js similarity index 100% rename from src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore.mvc.ui.theme.shared/datatables/datatables-extensions.js rename to src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/datatables/datatables-extensions.js diff --git a/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore.mvc.ui.theme.shared/jquery-form/jquery-form-extensions.js b/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/jquery-form/jquery-form-extensions.js similarity index 100% rename from src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore.mvc.ui.theme.shared/jquery-form/jquery-form-extensions.js rename to src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/jquery-form/jquery-form-extensions.js diff --git a/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore.mvc.ui.theme.shared/jquery/jquery-extensions.js b/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/jquery/jquery-extensions.js similarity index 100% rename from src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore.mvc.ui.theme.shared/jquery/jquery-extensions.js rename to src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/jquery/jquery-extensions.js diff --git a/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore.mvc.ui.theme.shared/sweetalert/abp-sweetalert.js b/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/sweetalert/abp-sweetalert.js similarity index 100% rename from src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore.mvc.ui.theme.shared/sweetalert/abp-sweetalert.js rename to src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/sweetalert/abp-sweetalert.js diff --git a/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore.mvc.ui.theme.shared/toastr/abp-toastr.js b/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/toastr/abp-toastr.js similarity index 100% rename from src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore.mvc.ui.theme.shared/toastr/abp-toastr.js rename to src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/toastr/abp-toastr.js diff --git a/src/Volo.Abp.AspNetCore.Mvc.UI/UI/Bundling/BundleManager.cs b/src/Volo.Abp.AspNetCore.Mvc.UI/UI/Bundling/BundleManager.cs index 2251d5d65d..0244da1107 100644 --- a/src/Volo.Abp.AspNetCore.Mvc.UI/UI/Bundling/BundleManager.cs +++ b/src/Volo.Abp.AspNetCore.Mvc.UI/UI/Bundling/BundleManager.cs @@ -1,11 +1,16 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; +using System.IO; +using System.Text; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; 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.DependencyInjection; +using Volo.Abp.IO; namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling { @@ -14,7 +19,8 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling { private readonly BundlingOptions _options; private readonly IHostingEnvironment _hostingEnvironment; - private readonly IBundler _bundler; + private readonly IScriptBundler _scriptBundler; + private readonly IStyleBundler _styleBundler; private readonly IThemeManager _themeManager; private readonly IServiceProvider _serviceProvider; @@ -22,15 +28,17 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling public BundleManager( IOptions options, + IScriptBundler scriptBundler, + IStyleBundler styleBundler, IHostingEnvironment hostingEnvironment, - IBundler bundler, IThemeManager themeManager, IServiceProvider serviceProvider) { _hostingEnvironment = hostingEnvironment; - _bundler = bundler; + _scriptBundler = scriptBundler; _themeManager = themeManager; _serviceProvider = serviceProvider; + _styleBundler = styleBundler; _options = options.Value; _cache = new ConcurrentDictionary(); @@ -38,28 +46,63 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling public List GetStyleBundleFiles(string bundleName) { - return GetFiles(_options.StyleBundles.Get(bundleName)); + return GetBundleFiles(_options.StyleBundles.Get(bundleName), _styleBundler); } public List GetScriptBundleFiles(string bundleName) { - return GetFiles(_options.ScriptBundles.Get(bundleName)); + return GetBundleFiles(_options.ScriptBundles.Get(bundleName), _scriptBundler); } - public void CreateStyleBundle(string bundleName, Action configureAction) + protected virtual List 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 + { + "/" + 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 configureAction) { _options.StyleBundles.GetOrAdd(bundleName, configureAction); } - public void CreateScriptBundle(string bundleName, Action configureAction) + public virtual void CreateScriptBundle(string bundleName, Action configureAction) { _options.ScriptBundles.GetOrAdd(bundleName, configureAction); } - protected virtual List GetFiles(BundleConfiguration bundleConfiguration) + protected virtual bool IsBundlingEnabled() { - //TODO: Caching, Bundling & Minifying! + return true; + //return !_hostingEnvironment.IsDevelopment(); + } + protected virtual List CreateFileListFromConfiguration(BundleConfiguration bundleConfiguration) + { using (var scope = _serviceProvider.CreateScope()) { var context = new BundleConfigurationContext( diff --git a/src/Volo.Abp.AspNetCore.Mvc.UI/UI/Bundling/BundleResult.cs b/src/Volo.Abp.AspNetCore.Mvc.UI/UI/Bundling/BundleResult.cs new file mode 100644 index 0000000000..d647c3b6fc --- /dev/null +++ b/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; + } + } +} \ No newline at end of file diff --git a/src/Volo.Abp.AspNetCore.Mvc.UI/UI/Bundling/Bundler.cs b/src/Volo.Abp.AspNetCore.Mvc.UI/UI/Bundling/Bundler.cs deleted file mode 100644 index 796ac447d5..0000000000 --- a/src/Volo.Abp.AspNetCore.Mvc.UI/UI/Bundling/Bundler.cs +++ /dev/null @@ -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 files) - { - return ""; - } - } -} \ No newline at end of file diff --git a/src/Volo.Abp.AspNetCore.Mvc.UI/UI/Bundling/BundlerBase.cs b/src/Volo.Abp.AspNetCore.Mvc.UI/UI/Bundling/BundlerBase.cs new file mode 100644 index 0000000000..05c7b7d1b9 --- /dev/null +++ b/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; + } + } +} \ No newline at end of file diff --git a/src/Volo.Abp.AspNetCore.Mvc.UI/UI/Bundling/BundlerContext.cs b/src/Volo.Abp.AspNetCore.Mvc.UI/UI/Bundling/BundlerContext.cs new file mode 100644 index 0000000000..7af5cda1cd --- /dev/null +++ b/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 ContentFiles { get; } + + public BundlerContext(string bundleRelativePath, IReadOnlyList contentFiles) + { + BundleRelativePath = bundleRelativePath; + ContentFiles = contentFiles; + } + } +} \ No newline at end of file diff --git a/src/Volo.Abp.AspNetCore.Mvc.UI/UI/Bundling/BundlingOptions.cs b/src/Volo.Abp.AspNetCore.Mvc.UI/UI/Bundling/BundlingOptions.cs index 7c5d02f3b5..009cb2d4e1 100644 --- a/src/Volo.Abp.AspNetCore.Mvc.UI/UI/Bundling/BundlingOptions.cs +++ b/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; } + //TODO: Add option to enable/disable bundling / minification + + /// + /// Default: "__bundles". + /// + public string BundleFolderName { get; } = "__bundles"; + public BundlingOptions() { StyleBundles = new BundleConfigurationCollection(); diff --git a/src/Volo.Abp.AspNetCore.Mvc.UI/UI/Bundling/IBundler.cs b/src/Volo.Abp.AspNetCore.Mvc.UI/UI/Bundling/IBundler.cs index 1066a08b9b..45f70a12b4 100644 --- a/src/Volo.Abp.AspNetCore.Mvc.UI/UI/Bundling/IBundler.cs +++ b/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 { public interface IBundler { - string CreateBundle(List files); + string FileExtension { get; } + + BundleResult Bundle(IBundlerContext context); } } \ No newline at end of file diff --git a/src/Volo.Abp.AspNetCore.Mvc.UI/UI/Bundling/IBundlerContext.cs b/src/Volo.Abp.AspNetCore.Mvc.UI/UI/Bundling/IBundlerContext.cs new file mode 100644 index 0000000000..e43c4b5ba8 --- /dev/null +++ b/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 ContentFiles { get; } + } +} \ No newline at end of file diff --git a/src/Volo.Abp.AspNetCore.Mvc.UI/UI/Bundling/Scripts/IScriptBundler.cs b/src/Volo.Abp.AspNetCore.Mvc.UI/UI/Bundling/Scripts/IScriptBundler.cs new file mode 100644 index 0000000000..165828c22e --- /dev/null +++ b/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 + { + + } +} \ No newline at end of file diff --git a/src/Volo.Abp.AspNetCore.Mvc.UI/UI/Bundling/Scripts/ScriptBundler.cs b/src/Volo.Abp.AspNetCore.Mvc.UI/UI/Bundling/Scripts/ScriptBundler.cs new file mode 100644 index 0000000000..e653684139 --- /dev/null +++ b/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) + { + } + } +} \ No newline at end of file diff --git a/src/Volo.Abp.AspNetCore.Mvc.UI/UI/Bundling/Styles/CssRelativePathAdjuster.cs b/src/Volo.Abp.AspNetCore.Mvc.UI/UI/Bundling/Styles/CssRelativePathAdjuster.cs new file mode 100644 index 0000000000..983d823937 --- /dev/null +++ b/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(); + } + } + } +} diff --git a/src/Volo.Abp.AspNetCore.Mvc.UI/UI/Bundling/Styles/IStyleBundler.cs b/src/Volo.Abp.AspNetCore.Mvc.UI/UI/Bundling/Styles/IStyleBundler.cs new file mode 100644 index 0000000000..e2cd75f14c --- /dev/null +++ b/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 + { + + } +} \ No newline at end of file diff --git a/src/Volo.Abp.AspNetCore.Mvc.UI/UI/Bundling/Styles/StyleBundler.cs b/src/Volo.Abp.AspNetCore.Mvc.UI/UI/Bundling/Styles/StyleBundler.cs new file mode 100644 index 0000000000..1c521d3230 --- /dev/null +++ b/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) + ); + } + } +} \ No newline at end of file diff --git a/src/Volo.Abp.AspNetCore.Mvc.UI/UI/Minification/IJavascriptMinifier.cs b/src/Volo.Abp.AspNetCore.Mvc.UI/UI/Minification/IMinifier.cs similarity index 75% rename from src/Volo.Abp.AspNetCore.Mvc.UI/UI/Minification/IJavascriptMinifier.cs rename to src/Volo.Abp.AspNetCore.Mvc.UI/UI/Minification/IMinifier.cs index 883af39e41..8b2390d7bb 100644 --- a/src/Volo.Abp.AspNetCore.Mvc.UI/UI/Minification/IJavascriptMinifier.cs +++ b/src/Volo.Abp.AspNetCore.Mvc.UI/UI/Minification/IMinifier.cs @@ -1,6 +1,6 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Minification { - public interface IJavascriptMinifier + public interface IMinifier { string Minify(string source, string fileName = null); } diff --git a/src/Volo.Abp.AspNetCore.Mvc.UI/UI/Minification/NUglify/NUglifyCssMinifier.cs b/src/Volo.Abp.AspNetCore.Mvc.UI/UI/Minification/NUglify/NUglifyCssMinifier.cs new file mode 100644 index 0000000000..e1e0ba4236 --- /dev/null +++ b/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); + } + } +} \ No newline at end of file diff --git a/src/Volo.Abp.AspNetCore.Mvc.UI/UI/Minification/NUglify/NUglifyJavascriptMinifier.cs b/src/Volo.Abp.AspNetCore.Mvc.UI/UI/Minification/NUglify/NUglifyJavascriptMinifier.cs index 46438757d9..7224acd5a0 100644 --- a/src/Volo.Abp.AspNetCore.Mvc.UI/UI/Minification/NUglify/NUglifyJavascriptMinifier.cs +++ b/src/Volo.Abp.AspNetCore.Mvc.UI/UI/Minification/NUglify/NUglifyJavascriptMinifier.cs @@ -1,26 +1,15 @@ using NUglify; +using NUglify.JavaScript; +using Volo.Abp.AspNetCore.Mvc.UI.Minification.Scripts; using Volo.Abp.DependencyInjection; 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); - 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 - ); - } + return Uglify.Js(source, fileName); } } } \ No newline at end of file diff --git a/src/Volo.Abp.AspNetCore.Mvc.UI/UI/Minification/NUglify/NUglifyMinifierBase.cs b/src/Volo.Abp.AspNetCore.Mvc.UI/UI/Minification/NUglify/NUglifyMinifierBase.cs new file mode 100644 index 0000000000..b6fd576603 --- /dev/null +++ b/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); + } +} \ No newline at end of file diff --git a/src/Volo.Abp.AspNetCore.Mvc.UI/UI/Minification/Scripts/IJavascriptMinifier.cs b/src/Volo.Abp.AspNetCore.Mvc.UI/UI/Minification/Scripts/IJavascriptMinifier.cs new file mode 100644 index 0000000000..84dfe9dd4e --- /dev/null +++ b/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 + { + + } +} \ No newline at end of file diff --git a/src/Volo.Abp.AspNetCore.Mvc.UI/UI/Minification/Styles/ICssMinifier.cs b/src/Volo.Abp.AspNetCore.Mvc.UI/UI/Minification/Styles/ICssMinifier.cs new file mode 100644 index 0000000000..42935b7c65 --- /dev/null +++ b/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 + { + + } +} \ No newline at end of file diff --git a/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/Builder/VirtualFileSystemApplicationBuilderExtensions.cs b/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/Builder/VirtualFileSystemApplicationBuilderExtensions.cs index 8d9cabbab4..c26519bb20 100644 --- a/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/Builder/VirtualFileSystemApplicationBuilderExtensions.cs +++ b/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/Builder/VirtualFileSystemApplicationBuilderExtensions.cs @@ -13,7 +13,7 @@ namespace Microsoft.AspNetCore.Builder IFileProvider fileProvider = new AspNetCoreVirtualFileProvider( app.ApplicationServices, - "/wwwroot" + "/wwwroot" //TODO: Hard-coded "/wwwroot" is not good! ); app.UseStaticFiles( diff --git a/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/VirtualFileSystem/HybridWebRootFileProvider.cs b/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/VirtualFileSystem/HybridWebRootFileProvider.cs new file mode 100644 index 0000000000..73658c7391 --- /dev/null +++ b/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 + ); + } + } +} \ No newline at end of file diff --git a/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/VirtualFileSystem/IHybridWebRootFileProvider.cs b/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/VirtualFileSystem/IHybridWebRootFileProvider.cs new file mode 100644 index 0000000000..47f938cc86 --- /dev/null +++ b/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); + } +} diff --git a/src/Volo.Abp.Core/Volo/Abp/IO/DirectoryHelper.cs b/src/Volo.Abp.Core/Volo/Abp/IO/DirectoryHelper.cs new file mode 100644 index 0000000000..93afea9264 --- /dev/null +++ b/src/Volo.Abp.Core/Volo/Abp/IO/DirectoryHelper.cs @@ -0,0 +1,22 @@ +using System.IO; + +namespace Volo.Abp.IO +{ + /// + /// A helper class for Directory operations. + /// + public static class DirectoryHelper + { + /// + /// Creates a new directory if it does not exists. + /// + /// Directory to create + public static void CreateIfNotExists(string directory) + { + if (!Directory.Exists(directory)) + { + Directory.CreateDirectory(directory); + } + } + } +} diff --git a/src/Volo.Abp.Core/Volo/Abp/IO/FileHelper.cs b/src/Volo.Abp.Core/Volo/Abp/IO/FileHelper.cs new file mode 100644 index 0000000000..766507b840 --- /dev/null +++ b/src/Volo.Abp.Core/Volo/Abp/IO/FileHelper.cs @@ -0,0 +1,22 @@ +using System.IO; + +namespace Volo.Abp.IO +{ + /// + /// A helper class for File operations. + /// + public static class FileHelper + { + /// + /// Checks and deletes given file if it does exists. + /// + /// Path of the file + public static void DeleteIfExists(string filePath) + { + if (File.Exists(filePath)) + { + File.Delete(filePath); + } + } + } +} \ No newline at end of file diff --git a/src/Volo.Abp.VirtualFileSystem/Volo/Abp/VirtualFileSystem/VirtualFileProvider.cs b/src/Volo.Abp.VirtualFileSystem/Volo/Abp/VirtualFileSystem/VirtualFileProvider.cs index ac2c5a7b9c..750ab29b15 100644 --- a/src/Volo.Abp.VirtualFileSystem/Volo/Abp/VirtualFileSystem/VirtualFileProvider.cs +++ b/src/Volo.Abp.VirtualFileSystem/Volo/Abp/VirtualFileSystem/VirtualFileProvider.cs @@ -19,22 +19,22 @@ namespace Volo.Abp.VirtualFileSystem _fileProvider = CreateHybridProvider(); } - public IFileInfo GetFileInfo(string subpath) + public virtual IFileInfo GetFileInfo(string subpath) { return _fileProvider.GetFileInfo(subpath); } - public IDirectoryContents GetDirectoryContents(string subpath) + public virtual IDirectoryContents GetDirectoryContents(string subpath) { return _fileProvider.GetDirectoryContents(subpath); } - public IChangeToken Watch(string filter) + public virtual IChangeToken Watch(string filter) { return _fileProvider.Watch(filter); } - private IFileProvider CreateHybridProvider() + protected virtual IFileProvider CreateHybridProvider() { IFileProvider fileProvider = new InternalVirtualFileProvider(_options); @@ -54,7 +54,7 @@ namespace Volo.Abp.VirtualFileSystem return fileProvider; } - private class InternalVirtualFileProvider : IFileProvider + protected class InternalVirtualFileProvider : IFileProvider { private readonly VirtualFileSystemOptions _options; private readonly Lazy> _files;