From 193bf6c51e3ef69ec534955933680796dd95d8b7 Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 16 Mar 2021 17:15:40 +1100 Subject: [PATCH 1/6] starts getting the in-memory runtime min working --- .../UmbracoBuilderExtensions.cs | 2 +- .../RuntimeMinification/SmidgeNuglifyJs.cs | 7 +++-- .../SmidgeRuntimeMinifier.cs | 29 ++++++++++++++----- .../Umbraco.Web.Common.csproj | 5 ++-- 4 files changed, 30 insertions(+), 13 deletions(-) diff --git a/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs b/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs index 4b3715cd52..e0aa1671b7 100644 --- a/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs +++ b/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs @@ -219,7 +219,7 @@ namespace Umbraco.Extensions /// public static IUmbracoBuilder AddRuntimeMinifier(this IUmbracoBuilder builder) { - builder.Services.AddSmidge(builder.Config.GetSection(Cms.Core.Constants.Configuration.ConfigRuntimeMinification)); + builder.Services.AddSmidge(builder.Config.GetSection(Constants.Configuration.ConfigRuntimeMinification)); builder.Services.AddSmidgeNuglify(); return builder; diff --git a/src/Umbraco.Web.Common/RuntimeMinification/SmidgeNuglifyJs.cs b/src/Umbraco.Web.Common/RuntimeMinification/SmidgeNuglifyJs.cs index cf85ae568d..74c298a4f5 100644 --- a/src/Umbraco.Web.Common/RuntimeMinification/SmidgeNuglifyJs.cs +++ b/src/Umbraco.Web.Common/RuntimeMinification/SmidgeNuglifyJs.cs @@ -1,4 +1,5 @@ -using Smidge.Nuglify; +using Smidge; +using Smidge.Nuglify; namespace Umbraco.Cms.Web.Common.RuntimeMinification { @@ -7,8 +8,8 @@ namespace Umbraco.Cms.Web.Common.RuntimeMinification /// public class SmidgeNuglifyJs : NuglifyJs { - public SmidgeNuglifyJs(NuglifySettings settings, ISourceMapDeclaration sourceMapDeclaration) - : base(GetSettings(settings), sourceMapDeclaration) + public SmidgeNuglifyJs(NuglifySettings settings, ISourceMapDeclaration sourceMapDeclaration, IRequestHelper requestHelper) + : base(GetSettings(settings), sourceMapDeclaration, requestHelper) { } diff --git a/src/Umbraco.Web.Common/RuntimeMinification/SmidgeRuntimeMinifier.cs b/src/Umbraco.Web.Common/RuntimeMinification/SmidgeRuntimeMinifier.cs index f39050d766..11ed3680c1 100644 --- a/src/Umbraco.Web.Common/RuntimeMinification/SmidgeRuntimeMinifier.cs +++ b/src/Umbraco.Web.Common/RuntimeMinification/SmidgeRuntimeMinifier.cs @@ -1,8 +1,10 @@ -using System; +using System; using System.Collections.Generic; +using System.ComponentModel; using System.Linq; using System.Threading.Tasks; using Smidge; +using Smidge.Cache; using Smidge.CompositeFiles; using Smidge.FileProcessors; using Smidge.Models; @@ -15,6 +17,19 @@ using JavaScriptFile = Smidge.Models.JavaScriptFile; namespace Umbraco.Cms.Web.Common.RuntimeMinification { +// public class UmbracoCacheBuster : ICacheBuster +// { +// /// +// /// Gets the cache buster value +// /// +// /// +// public string GetValue() => throw new NotImplementedException(); + +// // This doesn't do anything in Smidge (is removed in v4 unreleased version) +// [EditorBrowsable(EditorBrowsableState.Never)] +// public bool PersistProcessedFiles => throw new NotImplementedException(); +// } + public class SmidgeRuntimeMinifier : IRuntimeMinifier { private readonly IHostingEnvironment _hostingEnvironment; @@ -24,12 +39,12 @@ namespace Umbraco.Cms.Web.Common.RuntimeMinification private readonly SmidgeHelperAccessor _smidge; // used only for minifying in MinifyAsync not for an actual pipeline - private Lazy _jsMinPipeline; - private Lazy _cssMinPipeline; + private readonly Lazy _jsMinPipeline; + private readonly Lazy _cssMinPipeline; // default pipelines for processing js/css files for the back office - private Lazy _jsPipeline; - private Lazy _cssPipeline; + private readonly Lazy _jsPipeline; + private readonly Lazy _cssPipeline; public SmidgeRuntimeMinifier( IBundleManager bundles, @@ -65,7 +80,7 @@ namespace Umbraco.Cms.Web.Common.RuntimeMinification if (_bundles.Exists(bundleName)) throw new InvalidOperationException($"The bundle name {bundleName} already exists"); - // Here we could configure bundle options instead of using smidge's global defaults. + // TODO: Here we could configure bundle options instead of using smidge's global defaults. // For example we can use our own custom cache buster for this bundle without having the global one // affect this or vice versa. var bundle = _bundles.Create(bundleName, _cssPipeline.Value, WebFileType.Css, filePaths); @@ -81,7 +96,7 @@ namespace Umbraco.Cms.Web.Common.RuntimeMinification if (_bundles.Exists(bundleName)) throw new InvalidOperationException($"The bundle name {bundleName} already exists"); - // Here we could configure bundle options instead of using smidge's global defaults. + // TODO: Here we could configure bundle options instead of using smidge's global defaults. // For example we can use our own custom cache buster for this bundle without having the global one // affect this or vice versa. var pipeline = optimizeOutput ? _jsPipeline.Value : _bundles.PipelineFactory.Create(); diff --git a/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj b/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj index dce3ad7336..d5ae00b3f1 100644 --- a/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj +++ b/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj @@ -30,8 +30,9 @@ - - + + + From ecbd6d1c810dfb5de7479dde0f507f438ab5a004 Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 16 Mar 2021 19:19:03 +1100 Subject: [PATCH 2/6] Adds better support for app_plugins loading assets in a safer way and without having to pass refs --- .../WebAssets/BackOfficeWebAssets.cs | 20 +++++++--- .../UmbracoTestServerTestBase.cs | 2 +- .../Testing/UmbracoIntegrationTest.cs | 2 +- .../UmbracoBuilderExtensions.cs | 4 +- .../UmbracoBuilderExtensions.cs | 40 +++++++------------ .../Umbraco.Web.Common.csproj | 4 +- src/Umbraco.Web.UI.NetCore/Program.cs | 19 ++++----- src/Umbraco.Web.UI.NetCore/Startup.cs | 2 +- 8 files changed, 46 insertions(+), 47 deletions(-) diff --git a/src/Umbraco.Infrastructure/WebAssets/BackOfficeWebAssets.cs b/src/Umbraco.Infrastructure/WebAssets/BackOfficeWebAssets.cs index 2fe367c739..ef598e6150 100644 --- a/src/Umbraco.Infrastructure/WebAssets/BackOfficeWebAssets.cs +++ b/src/Umbraco.Infrastructure/WebAssets/BackOfficeWebAssets.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using Microsoft.Extensions.Options; @@ -94,10 +94,15 @@ namespace Umbraco.Cms.Infrastructure.WebAssets private string[] GetScriptsForBackOfficeExtensions(IEnumerable propertyEditorScripts) { var scripts = new HashSet(); - foreach (var script in _parser.Manifest.Scripts) + foreach (string script in _parser.Manifest.Scripts) + { scripts.Add(script); - foreach (var script in propertyEditorScripts) + } + + foreach (string script in propertyEditorScripts) + { scripts.Add(script); + } return scripts.ToArray(); } @@ -120,10 +125,15 @@ namespace Umbraco.Cms.Infrastructure.WebAssets { var stylesheets = new HashSet(); - foreach (var script in _parser.Manifest.Stylesheets) + foreach (string script in _parser.Manifest.Stylesheets) + { stylesheets.Add(script); - foreach (var stylesheet in propertyEditorStyles) + } + + foreach (string stylesheet in propertyEditorStyles) + { stylesheets.Add(stylesheet); + } return stylesheets.ToArray(); } diff --git a/src/Umbraco.Tests.Integration/TestServerTest/UmbracoTestServerTestBase.cs b/src/Umbraco.Tests.Integration/TestServerTest/UmbracoTestServerTestBase.cs index 6b61307bb3..d6492f67bb 100644 --- a/src/Umbraco.Tests.Integration/TestServerTest/UmbracoTestServerTestBase.cs +++ b/src/Umbraco.Tests.Integration/TestServerTest/UmbracoTestServerTestBase.cs @@ -151,7 +151,7 @@ namespace Umbraco.Cms.Tests.Integration.TestServerTest .AddConfiguration() .AddUmbracoCore() .AddWebComponents() - .AddRuntimeMinifier(webHostEnvironment) + .AddRuntimeMinifier() .AddBackOfficeCore() .AddBackOfficeAuthentication() .AddBackOfficeIdentity() diff --git a/src/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs b/src/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs index 16a8a392cf..dbf047cf48 100644 --- a/src/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs +++ b/src/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs @@ -212,7 +212,7 @@ namespace Umbraco.Cms.Tests.Integration.Testing builder.AddConfiguration() .AddUmbracoCore() .AddWebComponents() - .AddRuntimeMinifier(webHostEnvironment) + .AddRuntimeMinifier() .AddBackOfficeAuthentication() .AddBackOfficeIdentity() .AddMembersIdentity() diff --git a/src/Umbraco.Web.BackOffice/DependencyInjection/UmbracoBuilderExtensions.cs b/src/Umbraco.Web.BackOffice/DependencyInjection/UmbracoBuilderExtensions.cs index e80e77e179..092290977f 100644 --- a/src/Umbraco.Web.BackOffice/DependencyInjection/UmbracoBuilderExtensions.cs +++ b/src/Umbraco.Web.BackOffice/DependencyInjection/UmbracoBuilderExtensions.cs @@ -35,11 +35,11 @@ namespace Umbraco.Extensions /// /// Adds all required components to run the Umbraco back office /// - public static IUmbracoBuilder AddBackOffice(this IUmbracoBuilder builder, IWebHostEnvironment webHostEnvironment) => builder + public static IUmbracoBuilder AddBackOffice(this IUmbracoBuilder builder) => builder .AddConfiguration() .AddUmbracoCore() .AddWebComponents() - .AddRuntimeMinifier(webHostEnvironment) + .AddRuntimeMinifier() .AddBackOfficeCore() .AddBackOfficeAuthentication() .AddBackOfficeIdentity() diff --git a/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs b/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs index 060cbbd113..d4d9ee5047 100644 --- a/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs +++ b/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs @@ -4,6 +4,7 @@ using System.Data.SqlClient; using System.IO; using System.Linq; using System.Reflection; +using Dazinator.Extensions.FileProviders.GlobPatternFilter; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; @@ -219,9 +220,21 @@ namespace Umbraco.Extensions /// /// Add runtime minifier support for Umbraco /// - public static IUmbracoBuilder AddRuntimeMinifier(this IUmbracoBuilder builder, IWebHostEnvironment webHostEnvironment) + public static IUmbracoBuilder AddRuntimeMinifier(this IUmbracoBuilder builder) { - var smidgePhysicalFileProvider = new SmidgePhysicalFileProvider(webHostEnvironment.ContentRootFileProvider, webHostEnvironment.WebRootFileProvider); + // Add custom ISmidgeFileProvider to include the additional App_Plugins location + // to load assets from. + builder.Services.AddSingleton(f => + { + IWebHostEnvironment hostEnv = f.GetRequiredService(); + + return new SmidgeFileProvider( + hostEnv.WebRootFileProvider, + new GlobPatternFilterFileProvider( + hostEnv.ContentRootFileProvider, + // only include js or css files within App_Plugins + new[] { "App_Plugins/**/*.js", "App_Plugins/**/*.css" })); + }); builder.Services.AddSmidge(builder.Config.GetSection(Constants.Configuration.ConfigRuntimeMinification)); builder.Services.AddSmidgeNuglify(); @@ -416,28 +429,5 @@ namespace Umbraco.Extensions return new AspNetCoreHostingEnvironment(wrappedHostingSettings,wrappedWebRoutingSettings, webHostEnvironment); } - /// - /// This file provider lets us serve physical files to Smidge for minification from both wwwroot and App_Plugins (which is outside wwwroot). - /// This file provider is NOT intended for use anywhere else, as it exposes files from the content root. - /// - private class SmidgePhysicalFileProvider : IFileProvider - { - private readonly IFileProvider _contentRootFileProvider; - private readonly IFileProvider _webRooFileProvider; - - public SmidgePhysicalFileProvider(IFileProvider contentRootFileProvider, IFileProvider webRooFileProvider) - { - _contentRootFileProvider = contentRootFileProvider; - _webRooFileProvider = webRooFileProvider; - } - - public IFileInfo GetFileInfo(string subpath) => subpath.InvariantStartsWith(Constants.SystemDirectories.AppPlugins) - ? _contentRootFileProvider.GetFileInfo(subpath) - : _webRooFileProvider.GetFileInfo(subpath); - - public IDirectoryContents GetDirectoryContents(string subpath) => throw new NotSupportedException(); - - public IChangeToken Watch(string filter) => throw new NotSupportedException(); - } } } diff --git a/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj b/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj index d5ae00b3f1..03fcca3c90 100644 --- a/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj +++ b/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj @@ -1,4 +1,4 @@ - + net5.0 @@ -33,6 +33,8 @@ + + diff --git a/src/Umbraco.Web.UI.NetCore/Program.cs b/src/Umbraco.Web.UI.NetCore/Program.cs index 7a6ee4dcfd..89ce7d92bc 100644 --- a/src/Umbraco.Web.UI.NetCore/Program.cs +++ b/src/Umbraco.Web.UI.NetCore/Program.cs @@ -1,5 +1,4 @@ using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; @@ -8,20 +7,18 @@ namespace Umbraco.Cms.Web.UI.NetCore public class Program { public static void Main(string[] args) - => CreateHostBuilder(args) + { + CreateHostBuilder(args) .Build() .Run(); + } public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) -#if DEBUG - .ConfigureAppConfiguration(config - => config.AddJsonFile( - "appsettings.Local.json", - optional: true, - reloadOnChange: true)) -#endif - .ConfigureLogging(x => x.ClearProviders()) - .ConfigureWebHostDefaults(webBuilder => webBuilder.UseStartup()); + .ConfigureLogging(x => + { + x.ClearProviders(); + }) + .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup(); }); } } diff --git a/src/Umbraco.Web.UI.NetCore/Startup.cs b/src/Umbraco.Web.UI.NetCore/Startup.cs index 79a9bff03f..b0c9c465ad 100644 --- a/src/Umbraco.Web.UI.NetCore/Startup.cs +++ b/src/Umbraco.Web.UI.NetCore/Startup.cs @@ -45,7 +45,7 @@ namespace Umbraco.Cms.Web.UI.NetCore { #pragma warning disable IDE0022 // Use expression body for methods services.AddUmbraco(_env, _config) - .AddBackOffice(_env) + .AddBackOffice() .AddBackOfficeExternalLogins(logins => { var loginProviderOptions = new BackOfficeExternalLoginProviderOptions( From 2d14d0bd4a57f5cf650cc160d81badda9ab2f327 Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 16 Mar 2021 19:20:29 +1100 Subject: [PATCH 3/6] oops didn't mean to commit that --- src/Umbraco.Web.UI.NetCore/Startup.cs | 69 +-------------------------- 1 file changed, 1 insertion(+), 68 deletions(-) diff --git a/src/Umbraco.Web.UI.NetCore/Startup.cs b/src/Umbraco.Web.UI.NetCore/Startup.cs index b0c9c465ad..820ec6d706 100644 --- a/src/Umbraco.Web.UI.NetCore/Startup.cs +++ b/src/Umbraco.Web.UI.NetCore/Startup.cs @@ -4,12 +4,7 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Umbraco.Cms.Core.DependencyInjection; -using Umbraco.Cms.Infrastructure.DependencyInjection; -using Umbraco.Cms.Web.BackOffice.ModelsBuilder; -using Microsoft.Identity.Web; using Umbraco.Extensions; -using Umbraco.Cms.Web.BackOffice.Security; -using Microsoft.AspNetCore.Identity; namespace Umbraco.Cms.Web.UI.NetCore { @@ -45,69 +40,7 @@ namespace Umbraco.Cms.Web.UI.NetCore { #pragma warning disable IDE0022 // Use expression body for methods services.AddUmbraco(_env, _config) - .AddBackOffice() - .AddBackOfficeExternalLogins(logins => - { - var loginProviderOptions = new BackOfficeExternalLoginProviderOptions( - "btn-google", - "fa-google", - new ExternalSignInAutoLinkOptions(true) - { - OnAutoLinking = (user, login) => - { - }, - OnExternalLogin = (user, login) => - { - user.Claims.Add(new IdentityUserClaim - { - ClaimType = "hello", - ClaimValue = "world" - }); - return true; - } - }, - denyLocalLogin: false, - autoRedirectLoginToExternalProvider: false); - - logins.AddBackOfficeLogin( - loginProviderOptions, - auth => - { - auth.AddGoogle( - auth.SchemeForBackOffice("Google"), // The scheme must be set with this method to work for the back office - options => - { - // By default this is '/signin-google' but it needs to be changed to this - options.CallbackPath = "/umbraco-google-signin"; - options.ClientId = "1072120697051-p41pro11srud3o3n90j7m00geq426jqt.apps.googleusercontent.com"; - options.ClientSecret = "cs_LJTXh2rtI01C5OIt9WFkt"; - }); - - // NOTE: Adding additional providers here is possible via the API but - // it will mean that the same BackOfficeExternalLoginProviderOptions will be registered - // for them. In some weird cases maybe people would want that? - }); - - logins.AddBackOfficeLogin( - new BackOfficeExternalLoginProviderOptions("btn-microsoft", "fa-windows"), - auth => - { - auth.AddMicrosoftIdentityWebApp( - options => - { - options.SaveTokens = true; - - // By default this is '/signin-oidc' but it needs to be changed to this - options.CallbackPath = "/umbraco-signin-oidc"; - options.Instance = "https://login.microsoftonline.com/"; - options.TenantId = "3bb0b4c5-364f-4394-ad36-0f29f95e5ddd"; - options.ClientId = "56e98cad-ed2d-4f1b-8f85-bef11adc163f"; - options.ClientSecret = "-1E9_fdPHi_ZkSQOb2.O5LG025sv6-NQ3h"; - }, - openIdConnectScheme: auth.SchemeForBackOffice("AzureAD"), // The scheme must be set with this method to work for the back office - cookieScheme: "Fake"); - }); - }) + .AddBackOffice() .AddWebsite() .AddComposers() .Build(); From eba659b9ee565a9645c198a92d4fb317b7fdc607 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 17 Mar 2021 17:44:52 +1100 Subject: [PATCH 4/6] Gets in-memory runtime minifiy cache working, ensures no files can be accessed at the root, adds config to enable in-memory and configure a cache buster including a timestamp cache buster. --- .../Models/RuntimeMinificationCacheBuster.cs | 9 ++ .../Models/RuntimeMinificationSettings.cs | 16 +++ .../UmbracoBuilder.Configuration.cs | 6 +- .../WebAssets/IRuntimeMinifier.cs | 4 +- .../UmbracoBuilder.CoreServices.cs | 3 +- .../UmbracoBuilder.WebAssets.cs | 16 +++ .../WebAssets/BackOfficeWebAssets.cs | 9 +- .../WebAssets/WebAssetsComponent.cs | 25 ---- .../WebAssets/WebAssetsComposer.cs | 15 -- .../UmbracoBuilderExtensions.cs | 15 +- .../Middleware/UmbracoRequestMiddleware.cs | 53 ++++++- .../RuntimeMinification/SmidgeComposer.cs | 22 --- .../RuntimeMinification/SmidgeOptionsSetup.cs | 21 +++ .../SmidgeRuntimeMinifier.cs | 132 +++++++++++++----- .../Umbraco.Web.Common.csproj | 9 +- .../appsettings.Development.json | 48 ++++--- 16 files changed, 266 insertions(+), 137 deletions(-) create mode 100644 src/Umbraco.Core/Configuration/Models/RuntimeMinificationCacheBuster.cs create mode 100644 src/Umbraco.Core/Configuration/Models/RuntimeMinificationSettings.cs create mode 100644 src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.WebAssets.cs delete mode 100644 src/Umbraco.Infrastructure/WebAssets/WebAssetsComponent.cs delete mode 100644 src/Umbraco.Infrastructure/WebAssets/WebAssetsComposer.cs delete mode 100644 src/Umbraco.Web.Common/RuntimeMinification/SmidgeComposer.cs create mode 100644 src/Umbraco.Web.Common/RuntimeMinification/SmidgeOptionsSetup.cs diff --git a/src/Umbraco.Core/Configuration/Models/RuntimeMinificationCacheBuster.cs b/src/Umbraco.Core/Configuration/Models/RuntimeMinificationCacheBuster.cs new file mode 100644 index 0000000000..db1e1526e5 --- /dev/null +++ b/src/Umbraco.Core/Configuration/Models/RuntimeMinificationCacheBuster.cs @@ -0,0 +1,9 @@ +namespace Umbraco.Cms.Core.Configuration.Models +{ + public enum RuntimeMinificationCacheBuster + { + Version, + AppDomain, + Timestamp + } +} diff --git a/src/Umbraco.Core/Configuration/Models/RuntimeMinificationSettings.cs b/src/Umbraco.Core/Configuration/Models/RuntimeMinificationSettings.cs new file mode 100644 index 0000000000..fa3519059a --- /dev/null +++ b/src/Umbraco.Core/Configuration/Models/RuntimeMinificationSettings.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Umbraco.Cms.Core.Configuration.Models +{ + public class RuntimeMinificationSettings + { + public bool UseInMemoryCache { get; set; } = false; + + /// + /// The cache buster type to use + /// + public RuntimeMinificationCacheBuster CacheBuster { get; set; } = RuntimeMinificationCacheBuster.Version; + } +} diff --git a/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.Configuration.cs b/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.Configuration.cs index c37f8fb760..a9e112efc4 100644 --- a/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.Configuration.cs +++ b/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.Configuration.cs @@ -13,11 +13,10 @@ namespace Umbraco.Cms.Core.DependencyInjection private static OptionsBuilder AddOptions(IUmbracoBuilder builder, string key) where TOptions : class - { - return builder.Services.AddOptions() + => builder.Services.AddOptions() .Bind(builder.Config.GetSection(key)) .ValidateDataAnnotations(); - } + /// /// Add Umbraco configuration services and options /// @@ -57,6 +56,7 @@ namespace Umbraco.Cms.Core.DependencyInjection AddOptions(builder, Constants.Configuration.ConfigPlugins); AddOptions(builder, Constants.Configuration.ConfigUnattended); AddOptions(builder, Constants.Configuration.ConfigRichTextEditor); + AddOptions(builder, Constants.Configuration.ConfigRuntimeMinification); return builder; } diff --git a/src/Umbraco.Core/WebAssets/IRuntimeMinifier.cs b/src/Umbraco.Core/WebAssets/IRuntimeMinifier.cs index cb8a012205..ac26da104f 100644 --- a/src/Umbraco.Core/WebAssets/IRuntimeMinifier.cs +++ b/src/Umbraco.Core/WebAssets/IRuntimeMinifier.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Threading.Tasks; @@ -25,7 +25,7 @@ namespace Umbraco.Cms.Core.WebAssets /// /// Thrown if any of the paths specified are not absolute /// - void CreateCssBundle(string bundleName, params string[] filePaths); + void CreateCssBundle(string bundleName, bool optimizeOutput, params string[] filePaths); /// /// Renders the html link tag for the bundle diff --git a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs index e3b327ffaa..6eb08bd4d5 100644 --- a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs +++ b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs @@ -66,7 +66,8 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection .AddRepositories() .AddServices() .AddCoreMappingProfiles() - .AddFileSystems(); + .AddFileSystems() + .AddWebAssets(); // register persistence mappers - required by database factory so needs to be done here // means the only place the collection can be modified is in a runtime - afterwards it diff --git a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.WebAssets.cs b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.WebAssets.cs new file mode 100644 index 0000000000..53e638997b --- /dev/null +++ b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.WebAssets.cs @@ -0,0 +1,16 @@ +using Microsoft.Extensions.DependencyInjection; +using Umbraco.Cms.Core.DependencyInjection; +using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Infrastructure.WebAssets; + +namespace Umbraco.Cms.Infrastructure.DependencyInjection +{ + public static partial class UmbracoBuilderExtensions + { + internal static IUmbracoBuilder AddWebAssets(this IUmbracoBuilder builder) + { + builder.Services.AddSingleton(); + return builder; + } + } +} diff --git a/src/Umbraco.Infrastructure/WebAssets/BackOfficeWebAssets.cs b/src/Umbraco.Infrastructure/WebAssets/BackOfficeWebAssets.cs index ef598e6150..05171988cf 100644 --- a/src/Umbraco.Infrastructure/WebAssets/BackOfficeWebAssets.cs +++ b/src/Umbraco.Infrastructure/WebAssets/BackOfficeWebAssets.cs @@ -48,17 +48,18 @@ namespace Umbraco.Cms.Infrastructure.WebAssets { // Create bundles - _runtimeMinifier.CreateCssBundle(UmbracoInitCssBundleName, + // TODO: I think we don't want to optimize these css if/when we get gulp to do that all for us + _runtimeMinifier.CreateCssBundle(UmbracoInitCssBundleName, true, FormatPaths("lib/bootstrap-social/bootstrap-social.css", "assets/css/umbraco.css", "lib/font-awesome/css/font-awesome.min.css")); - _runtimeMinifier.CreateCssBundle(UmbracoUpgradeCssBundleName, + _runtimeMinifier.CreateCssBundle(UmbracoUpgradeCssBundleName, true, FormatPaths("assets/css/umbraco.css", "lib/bootstrap-social/bootstrap-social.css", "lib/font-awesome/css/font-awesome.min.css")); - _runtimeMinifier.CreateCssBundle(UmbracoPreviewCssBundleName, + _runtimeMinifier.CreateCssBundle(UmbracoPreviewCssBundleName, true, FormatPaths("assets/css/canvasdesigner.css")); _runtimeMinifier.CreateJsBundle(UmbracoPreviewJsBundleName, false, @@ -81,7 +82,7 @@ namespace Umbraco.Cms.Infrastructure.WebAssets propertyEditorAssets.TryGetValue(AssetType.Javascript, out var scripts) ? scripts : Enumerable.Empty()))); _runtimeMinifier.CreateCssBundle( - UmbracoCssBundleName, + UmbracoCssBundleName, true, FormatPaths( GetStylesheetsForBackOffice( propertyEditorAssets.TryGetValue(AssetType.Css, out var styles) ? styles : Enumerable.Empty()))); diff --git a/src/Umbraco.Infrastructure/WebAssets/WebAssetsComponent.cs b/src/Umbraco.Infrastructure/WebAssets/WebAssetsComponent.cs deleted file mode 100644 index 2eba85a3c3..0000000000 --- a/src/Umbraco.Infrastructure/WebAssets/WebAssetsComponent.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Umbraco.Cms.Core.Composing; - -namespace Umbraco.Cms.Infrastructure.WebAssets -{ - public sealed class WebAssetsComponent : IComponent - { - private readonly BackOfficeWebAssets _backOfficeWebAssets; - - public WebAssetsComponent(BackOfficeWebAssets backOfficeWebAssets) - { - _backOfficeWebAssets = backOfficeWebAssets; - } - - public void Initialize() - { - // TODO: This will eagerly scan types but we don't really want that, however it works for now. - // We don't actually have to change Smidge or anything, all we have to do is postpone this call for when the first request on the website arrives. - _backOfficeWebAssets.CreateBundles(); - } - - public void Terminate() - { - } - } -} diff --git a/src/Umbraco.Infrastructure/WebAssets/WebAssetsComposer.cs b/src/Umbraco.Infrastructure/WebAssets/WebAssetsComposer.cs deleted file mode 100644 index 1298340a73..0000000000 --- a/src/Umbraco.Infrastructure/WebAssets/WebAssetsComposer.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Umbraco.Cms.Core.Composing; -using Umbraco.Cms.Core.DependencyInjection; -using Umbraco.Extensions; - -namespace Umbraco.Cms.Infrastructure.WebAssets -{ - public sealed class WebAssetsComposer : ComponentComposer - { - public override void Compose(IUmbracoBuilder builder) - { - base.Compose(builder); - builder.Services.AddUnique(); - } - } -} diff --git a/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs b/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs index d4d9ee5047..b08acdb5ef 100644 --- a/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs +++ b/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs @@ -18,6 +18,8 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Primitives; using Serilog; using Smidge; +using Smidge.FileProcessors; +using Smidge.InMemory; using Smidge.Nuglify; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Cache; @@ -34,6 +36,7 @@ using Umbraco.Cms.Core.Security; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Templates; using Umbraco.Cms.Core.Web; +using Umbraco.Cms.Core.WebAssets; using Umbraco.Cms.Infrastructure.DependencyInjection; using Umbraco.Cms.Infrastructure.HostedServices; using Umbraco.Cms.Infrastructure.HostedServices.ServerRegistration; @@ -53,6 +56,7 @@ using Umbraco.Cms.Web.Common.ModelBinders; using Umbraco.Cms.Web.Common.Mvc; using Umbraco.Cms.Web.Common.Profiler; using Umbraco.Cms.Web.Common.Routing; +using Umbraco.Cms.Web.Common.RuntimeMinification; using Umbraco.Cms.Web.Common.Security; using Umbraco.Cms.Web.Common.Templates; using Umbraco.Cms.Web.Common.UmbracoContext; @@ -233,10 +237,17 @@ namespace Umbraco.Extensions new GlobPatternFilterFileProvider( hostEnv.ContentRootFileProvider, // only include js or css files within App_Plugins - new[] { "App_Plugins/**/*.js", "App_Plugins/**/*.css" })); + new[] { "/App_Plugins/**/*.js", "/App_Plugins/**/*.css" })); }); + builder.Services.AddSmidge(builder.Config.GetSection(Constants.Configuration.ConfigRuntimeMinification)); builder.Services.AddSmidgeNuglify(); + builder.Services.AddSmidgeInMemory(false); // it will be enabled based on config/cachebuster + + builder.Services.AddUnique(); + builder.Services.AddUnique(); + builder.Services.AddTransient(); + builder.Services.ConfigureOptions(); return builder; } @@ -426,7 +437,7 @@ namespace Umbraco.Extensions var wrappedHostingSettings = new OptionsMonitorAdapter(hostingSettings); var wrappedWebRoutingSettings = new OptionsMonitorAdapter(webRoutingSettings); - return new AspNetCoreHostingEnvironment(wrappedHostingSettings,wrappedWebRoutingSettings, webHostEnvironment); + return new AspNetCoreHostingEnvironment(wrappedHostingSettings, wrappedWebRoutingSettings, webHostEnvironment); } } diff --git a/src/Umbraco.Web.Common/Middleware/UmbracoRequestMiddleware.cs b/src/Umbraco.Web.Common/Middleware/UmbracoRequestMiddleware.cs index 02976f9e1d..78b8ede69e 100644 --- a/src/Umbraco.Web.Common/Middleware/UmbracoRequestMiddleware.cs +++ b/src/Umbraco.Web.Common/Middleware/UmbracoRequestMiddleware.cs @@ -6,15 +6,19 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Extensions; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Smidge.Options; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.Logging; +using Umbraco.Cms.Core.Routing; using Umbraco.Cms.Core.Scoping; using Umbraco.Cms.Core.Security; using Umbraco.Cms.Core.Web; using Umbraco.Cms.Infrastructure.PublishedCache; +using Umbraco.Cms.Infrastructure.WebAssets; using Umbraco.Cms.Web.Common.Profiler; using Umbraco.Extensions; @@ -41,11 +45,21 @@ namespace Umbraco.Cms.Web.Common.Middleware private readonly PublishedSnapshotServiceEventHandler _publishedSnapshotServiceEventHandler; private readonly IEventAggregator _eventAggregator; private readonly IHostingEnvironment _hostingEnvironment; + private readonly UmbracoRequestPaths _umbracoRequestPaths; + private readonly BackOfficeWebAssets _backOfficeWebAssets; + private readonly SmidgeOptions _smidgeOptions; private readonly WebProfiler _profiler; - private static bool s_cacheInitialized = false; + + private static bool s_cacheInitialized; private static bool s_cacheInitializedFlag = false; private static object s_cacheInitializedLock = new object(); +#pragma warning disable IDE0044 // Add readonly modifier + private static bool s_firstBackOfficeRequest; + private static bool s_firstBackOfficeReqestFlag; + private static object s_firstBackOfficeRequestLocker = new object(); +#pragma warning restore IDE0044 // Add readonly modifier + /// /// Initializes a new instance of the class. /// @@ -56,7 +70,10 @@ namespace Umbraco.Cms.Web.Common.Middleware PublishedSnapshotServiceEventHandler publishedSnapshotServiceEventHandler, IEventAggregator eventAggregator, IProfiler profiler, - IHostingEnvironment hostingEnvironment) + IHostingEnvironment hostingEnvironment, + UmbracoRequestPaths umbracoRequestPaths, + BackOfficeWebAssets backOfficeWebAssets, + IOptions smidgeOptions) { _logger = logger; _umbracoContextFactory = umbracoContextFactory; @@ -64,6 +81,9 @@ namespace Umbraco.Cms.Web.Common.Middleware _publishedSnapshotServiceEventHandler = publishedSnapshotServiceEventHandler; _eventAggregator = eventAggregator; _hostingEnvironment = hostingEnvironment; + _umbracoRequestPaths = umbracoRequestPaths; + _backOfficeWebAssets = backOfficeWebAssets; + _smidgeOptions = smidgeOptions.Value; _profiler = profiler as WebProfiler; // Ignore if not a WebProfiler } @@ -73,6 +93,8 @@ namespace Umbraco.Cms.Web.Common.Middleware // do not process if client-side request if (context.Request.IsClientSideRequest()) { + // we need this here because for bundle requests, these are 'client side' requests that we need to handle + LazyInitializeBackOfficeServices(context.Request.Path); await next(context); return; } @@ -97,7 +119,8 @@ namespace Umbraco.Cms.Web.Common.Middleware _logger.LogTrace("Begin request [{HttpRequestId}]: {RequestUrl}", httpRequestId, pathAndQuery); try - { + { + LazyInitializeBackOfficeServices(context.Request.Path); await _eventAggregator.PublishAsync(new UmbracoRequestBegin(umbracoContextReference.UmbracoContext)); } catch (Exception ex) @@ -141,6 +164,30 @@ namespace Umbraco.Cms.Web.Common.Middleware _profiler?.UmbracoApplicationEndRequest(context); } + /// + /// Used to lazily initialize any back office services when the first request to the back office is made + /// + /// + /// + private void LazyInitializeBackOfficeServices(PathString absPath) + { + if (s_firstBackOfficeRequest) + { + return; + } + + if (_umbracoRequestPaths.IsBackOfficeRequest(absPath) + || absPath.Value.InvariantStartsWith($"/{_smidgeOptions.UrlOptions.CompositeFilePath}") + || absPath.Value.InvariantStartsWith($"/{_smidgeOptions.UrlOptions.BundleFilePath}")) + { + LazyInitializer.EnsureInitialized(ref s_firstBackOfficeRequest, ref s_firstBackOfficeReqestFlag, ref s_firstBackOfficeRequestLocker, () => + { + _backOfficeWebAssets.CreateBundles(); + return true; + }); + } + } + private Uri GetApplicationUrlFromCurrentRequest(HttpRequest request) { // We only consider GET and POST. diff --git a/src/Umbraco.Web.Common/RuntimeMinification/SmidgeComposer.cs b/src/Umbraco.Web.Common/RuntimeMinification/SmidgeComposer.cs deleted file mode 100644 index c4869a9cf9..0000000000 --- a/src/Umbraco.Web.Common/RuntimeMinification/SmidgeComposer.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Microsoft.Extensions.DependencyInjection; -using Smidge.FileProcessors; -using Umbraco.Cms.Core.Composing; -using Umbraco.Cms.Core.DependencyInjection; -using Umbraco.Cms.Core.WebAssets; -using Umbraco.Extensions; - -namespace Umbraco.Cms.Web.Common.RuntimeMinification -{ - public sealed class SmidgeComposer : IComposer - { - public void Compose(IUmbracoBuilder builder) - { - // TODO: For this to work we need to have services.AddSmidge() based on the Smidge APIs but our composer APIs don't really let us do that - // This makes it a bit awkward to boot the runtime since that call would need to be made outside of the composer... .hrm... - - builder.Services.AddUnique(); - builder.Services.AddUnique(); - builder.Services.AddTransient(); - } - } -} diff --git a/src/Umbraco.Web.Common/RuntimeMinification/SmidgeOptionsSetup.cs b/src/Umbraco.Web.Common/RuntimeMinification/SmidgeOptionsSetup.cs new file mode 100644 index 0000000000..d054a0c036 --- /dev/null +++ b/src/Umbraco.Web.Common/RuntimeMinification/SmidgeOptionsSetup.cs @@ -0,0 +1,21 @@ +using Microsoft.Extensions.Options; +using Smidge.Options; +using Umbraco.Cms.Core.Configuration.Models; + +namespace Umbraco.Cms.Web.Common.RuntimeMinification +{ + public class SmidgeOptionsSetup : IConfigureOptions + { + private readonly IOptions _runtimeMinificatinoSettings; + + public SmidgeOptionsSetup(IOptions runtimeMinificatinoSettings) + => _runtimeMinificatinoSettings = runtimeMinificatinoSettings; + + /// + /// Configures Smidge to use in-memory caching if configured that way or if certain cache busters are used + /// + /// + public void Configure(SmidgeOptions options) + => options.CacheOptions.UseInMemoryCache = _runtimeMinificatinoSettings.Value.UseInMemoryCache || _runtimeMinificatinoSettings.Value.CacheBuster == RuntimeMinificationCacheBuster.Timestamp; + } +} diff --git a/src/Umbraco.Web.Common/RuntimeMinification/SmidgeRuntimeMinifier.cs b/src/Umbraco.Web.Common/RuntimeMinification/SmidgeRuntimeMinifier.cs index 11ed3680c1..85f0398ea6 100644 --- a/src/Umbraco.Web.Common/RuntimeMinification/SmidgeRuntimeMinifier.cs +++ b/src/Umbraco.Web.Common/RuntimeMinification/SmidgeRuntimeMinifier.cs @@ -3,13 +3,16 @@ using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Threading.Tasks; +using Microsoft.Extensions.Options; using Smidge; using Smidge.Cache; using Smidge.CompositeFiles; using Smidge.FileProcessors; using Smidge.Models; using Smidge.Nuglify; +using Smidge.Options; using Umbraco.Cms.Core.Configuration; +using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.WebAssets; using CssFile = Smidge.Models.CssFile; @@ -17,24 +20,12 @@ using JavaScriptFile = Smidge.Models.JavaScriptFile; namespace Umbraco.Cms.Web.Common.RuntimeMinification { -// public class UmbracoCacheBuster : ICacheBuster -// { -// /// -// /// Gets the cache buster value -// /// -// /// -// public string GetValue() => throw new NotImplementedException(); - -// // This doesn't do anything in Smidge (is removed in v4 unreleased version) -// [EditorBrowsable(EditorBrowsableState.Never)] -// public bool PersistProcessedFiles => throw new NotImplementedException(); -// } - public class SmidgeRuntimeMinifier : IRuntimeMinifier { private readonly IHostingEnvironment _hostingEnvironment; - private readonly ISmidgeConfig _smidgeConfig; private readonly IConfigManipulator _configManipulator; + private readonly CacheBusterResolver _cacheBusterResolver; + private readonly RuntimeMinificationSettings _runtimeMinificationSettings; private readonly IBundleManager _bundles; private readonly SmidgeHelperAccessor _smidge; @@ -43,47 +34,93 @@ namespace Umbraco.Cms.Web.Common.RuntimeMinification private readonly Lazy _cssMinPipeline; // default pipelines for processing js/css files for the back office - private readonly Lazy _jsPipeline; - private readonly Lazy _cssPipeline; + private readonly Lazy _jsOptimizedPipeline; + private readonly Lazy _jsNonOptimizedPipeline; + private readonly Lazy _cssOptimizedPipeline; + private readonly Lazy _cssNonOptimizedPipeline; + private ICacheBuster _cacheBuster; + private readonly Type _cacheBusterType; public SmidgeRuntimeMinifier( IBundleManager bundles, SmidgeHelperAccessor smidge, IHostingEnvironment hostingEnvironment, - ISmidgeConfig smidgeConfig, - IConfigManipulator configManipulator) + IConfigManipulator configManipulator, + IOptions runtimeMinificationSettings, + CacheBusterResolver cacheBusterResolver) { _bundles = bundles; _smidge = smidge; _hostingEnvironment = hostingEnvironment; - _smidgeConfig = smidgeConfig; _configManipulator = configManipulator; - + _cacheBusterResolver = cacheBusterResolver; + _runtimeMinificationSettings = runtimeMinificationSettings.Value; _jsMinPipeline = new Lazy(() => _bundles.PipelineFactory.Create(typeof(JsMinifier))); _cssMinPipeline = new Lazy(() => _bundles.PipelineFactory.Create(typeof(NuglifyCss))); // replace the default JsMinifier with NuglifyJs and CssMinifier with NuglifyCss in the default pipelines // for use with our bundles only (not modifying global options) - _jsPipeline = new Lazy(() => bundles.PipelineFactory.DefaultJs().Replace(_bundles.PipelineFactory)); - _cssPipeline = new Lazy(() => bundles.PipelineFactory.DefaultCss().Replace(_bundles.PipelineFactory)); + _jsOptimizedPipeline = new Lazy(() => bundles.PipelineFactory.DefaultJs().Replace(_bundles.PipelineFactory)); + _jsNonOptimizedPipeline = new Lazy(() => bundles.PipelineFactory.DefaultJs()); + _cssOptimizedPipeline = new Lazy(() => bundles.PipelineFactory.DefaultCss().Replace(_bundles.PipelineFactory)); + _cssNonOptimizedPipeline = new Lazy(() => bundles.PipelineFactory.DefaultCss()); + Type cacheBusterType = _runtimeMinificationSettings.CacheBuster switch + { + RuntimeMinificationCacheBuster.AppDomain => typeof(AppDomainLifetimeCacheBuster), + RuntimeMinificationCacheBuster.Version => typeof(ConfigCacheBuster), + RuntimeMinificationCacheBuster.Timestamp => typeof(TimestampCacheBuster), + _ => throw new NotImplementedException() + }; + + _cacheBusterType = cacheBusterType; } - public string CacheBuster => _smidgeConfig.Version; + public string CacheBuster => (_cacheBuster ??= _cacheBusterResolver.GetCacheBuster(_cacheBusterType)).GetValue(); // only issue with creating bundles like this is that we don't have full control over the bundle options, though that could - public void CreateCssBundle(string bundleName, params string[] filePaths) + public void CreateCssBundle(string bundleName, bool optimizeOutput, params string[] filePaths) { if (filePaths.Any(f => !f.StartsWith("/") && !f.StartsWith("~/"))) + { throw new InvalidOperationException("All file paths must be absolute"); + } if (_bundles.Exists(bundleName)) + { throw new InvalidOperationException($"The bundle name {bundleName} already exists"); + } - // TODO: Here we could configure bundle options instead of using smidge's global defaults. - // For example we can use our own custom cache buster for this bundle without having the global one - // affect this or vice versa. - var bundle = _bundles.Create(bundleName, _cssPipeline.Value, WebFileType.Css, filePaths); + if (optimizeOutput) + { + var bundle = _bundles.Create(bundleName, _cssOptimizedPipeline.Value, WebFileType.Css, filePaths) + .WithEnvironmentOptions( + BundleEnvironmentOptions.Create() + .ForDebug(builder => builder + // auto-invalidate bundle if files change in debug + .EnableFileWatcher() + // keep using composite files in debug, not raw static files + .EnableCompositeProcessing() + // use the cache buster defined in config + .SetCacheBusterType(_cacheBusterType)) + .ForProduction(builder => builder + // use the cache buster defined in config + .SetCacheBusterType(_cacheBusterType)) + .Build()); + } + else + { + var bundle = _bundles.Create(bundleName, _cssNonOptimizedPipeline.Value, WebFileType.Css, filePaths) + .WithEnvironmentOptions( + BundleEnvironmentOptions.Create() + .ForDebug(builder => builder + // use the cache buster defined in config + .SetCacheBusterType(_cacheBusterType)) + .ForProduction(builder => builder + // use the cache buster defined in config + .SetCacheBusterType(_cacheBusterType)) + .Build()); + } } public async Task RenderCssHereAsync(string bundleName) => (await _smidge.SmidgeHelper.CssHereAsync(bundleName, _hostingEnvironment.IsDebugMode)).ToString(); @@ -91,16 +128,45 @@ namespace Umbraco.Cms.Web.Common.RuntimeMinification public void CreateJsBundle(string bundleName, bool optimizeOutput, params string[] filePaths) { if (filePaths.Any(f => !f.StartsWith("/") && !f.StartsWith("~/"))) + { throw new InvalidOperationException("All file paths must be absolute"); + } if (_bundles.Exists(bundleName)) + { throw new InvalidOperationException($"The bundle name {bundleName} already exists"); + } - // TODO: Here we could configure bundle options instead of using smidge's global defaults. - // For example we can use our own custom cache buster for this bundle without having the global one - // affect this or vice versa. - var pipeline = optimizeOutput ? _jsPipeline.Value : _bundles.PipelineFactory.Create(); - var bundle = _bundles.Create(bundleName, pipeline, WebFileType.Js, filePaths); + if (optimizeOutput) + { + var bundle = _bundles.Create(bundleName, _jsOptimizedPipeline.Value, WebFileType.Js, filePaths) + .WithEnvironmentOptions( + BundleEnvironmentOptions.Create() + .ForDebug(builder => builder + // auto-invalidate bundle if files change in debug + .EnableFileWatcher() + // keep using composite files in debug, not raw static files + .EnableCompositeProcessing() + // use the cache buster defined in config + .SetCacheBusterType(_cacheBusterType)) + .ForProduction(builder => builder + // use the cache buster defined in config + .SetCacheBusterType(_cacheBusterType)) + .Build()); + } + else + { + var bundle = _bundles.Create(bundleName, _jsNonOptimizedPipeline.Value, WebFileType.Js, filePaths) + .WithEnvironmentOptions( + BundleEnvironmentOptions.Create() + .ForDebug(builder => builder + // use the cache buster defined in config + .SetCacheBusterType(_cacheBusterType)) + .ForProduction(builder => builder + // use the cache buster defined in config + .SetCacheBusterType(_cacheBusterType)) + .Build()); + } } public async Task RenderJsHereAsync(string bundleName) => (await _smidge.SmidgeHelper.JsHereAsync(bundleName, _hostingEnvironment.IsDebugMode)).ToString(); diff --git a/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj b/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj index 03fcca3c90..bb2ecbc346 100644 --- a/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj +++ b/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj @@ -1,4 +1,4 @@ - + net5.0 @@ -30,10 +30,9 @@ - - - - + + + diff --git a/src/Umbraco.Web.UI.NetCore/appsettings.Development.json b/src/Umbraco.Web.UI.NetCore/appsettings.Development.json index 06afcf2a7a..edeb4946d6 100644 --- a/src/Umbraco.Web.UI.NetCore/appsettings.Development.json +++ b/src/Umbraco.Web.UI.NetCore/appsettings.Development.json @@ -17,30 +17,34 @@ ] }, "Umbraco": { - "CMS": { - "Global": { - "Smtp": { - //"From": "your@email.here", - //"Host": "localhost", - // "Port": "25" - } - }, - "Hosting": { - "Debug": true - }, - "RichTextEditor": { - "Commands" : [ - { - "Alias": "fullscreen", - "Name": "Full Screen", - "Mode": "All" - } - ], - "Plugins": [ - "fullscreen" - ] + "CMS": { + "Global": { + "Smtp": { + //"From": "your@email.here", + //"Host": "localhost", + // "Port": "25" } + }, + "Hosting": { + "Debug": true + }, + "RuntimeMinification": { + "useInMemoryCache": true, + "cacheBuster": "Timestamp" + }, + "RichTextEditor": { + "Commands": [ + { + "Alias": "fullscreen", + "Name": "Full Screen", + "Mode": "All" + } + ], + "Plugins": [ + "fullscreen" + ] } + } } } From 1a2d8623e635b4d7bb1c33aa01e5340f5f19aadb Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 17 Mar 2021 17:55:45 +1100 Subject: [PATCH 5/6] oops didn't mean to commit that --- src/Umbraco.Web.UI.NetCore/Umbraco.Web.UI.NetCore.csproj | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/Umbraco.Web.UI.NetCore/Umbraco.Web.UI.NetCore.csproj b/src/Umbraco.Web.UI.NetCore/Umbraco.Web.UI.NetCore.csproj index 83343003e9..5aeb0e8bb1 100644 --- a/src/Umbraco.Web.UI.NetCore/Umbraco.Web.UI.NetCore.csproj +++ b/src/Umbraco.Web.UI.NetCore/Umbraco.Web.UI.NetCore.csproj @@ -79,12 +79,6 @@ - - - - - - false From a3b4bb7bd961085266f3821595762a4493978435 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 17 Mar 2021 17:56:40 +1100 Subject: [PATCH 6/6] oops didn't mean to commit that --- src/Umbraco.Web.UI.NetCore/Views/_ViewImports.cshtml | 1 - src/Umbraco.Web.UI.NetCore/appsettings.json | 5 ++--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Web.UI.NetCore/Views/_ViewImports.cshtml b/src/Umbraco.Web.UI.NetCore/Views/_ViewImports.cshtml index 11c1c2646e..d5bf72bbc5 100644 --- a/src/Umbraco.Web.UI.NetCore/Views/_ViewImports.cshtml +++ b/src/Umbraco.Web.UI.NetCore/Views/_ViewImports.cshtml @@ -3,4 +3,3 @@ @using Umbraco.Cms.Web.Common.PublishedModels @using Umbraco.Cms.Web.Common.Views @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers -@addTagHelper *, Umbraco.Cms.Web.UI.NetCore diff --git a/src/Umbraco.Web.UI.NetCore/appsettings.json b/src/Umbraco.Web.UI.NetCore/appsettings.json index 76429dfcc9..937dc158fd 100644 --- a/src/Umbraco.Web.UI.NetCore/appsettings.json +++ b/src/Umbraco.Web.UI.NetCore/appsettings.json @@ -25,8 +25,7 @@ "HideTopLevelNodeFromPath": true, "UmbracoPath": "~/umbraco", "TimeOutInMinutes": 20, - "UseHttps": false, - "Id": "e1a9f744-f81e-4437-aabd-fac783868310" + "UseHttps": false }, "Hosting": { "Debug": false @@ -72,4 +71,4 @@ } } } -} \ No newline at end of file +}