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"
+ ]
}
+ }
}
}