using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Extensions; using Smidge; using Smidge.CompositeFiles; using Smidge.FileProcessors; using Smidge.Nuglify; using Umbraco.Core; using Umbraco.Core.Assets; using Umbraco.Core.Configuration; using Umbraco.Core.Hosting; using Umbraco.Core.IO; using Umbraco.Core.Manifest; using Umbraco.Core.PropertyEditors; using Umbraco.Core.Runtime; using Umbraco.Web.JavaScript; using CssFile = Smidge.Models.CssFile; using JavaScriptFile = Smidge.Models.JavaScriptFile; namespace Umbraco.Web.Common.RuntimeMinification { public class SmidgeRuntimeMinifier : IRuntimeMinifier { private readonly IGlobalSettings _globalSettings; private readonly IHttpContextAccessor _httpContextAccessor; private readonly IIOHelper _ioHelper; private readonly IHostingEnvironment _hostingEnvironment; private readonly ISmidgeConfig _smidgeConfig; private readonly IConfigManipulator _configManipulator; private readonly IManifestParser _manifestParser; private readonly PreProcessPipelineFactory _preProcessPipelineFactory; private readonly PropertyEditorCollection _propertyEditorCollection; private readonly SmidgeHelper _smidge; private PreProcessPipeline _jsPipeline; private PreProcessPipeline _cssPipeline; public SmidgeRuntimeMinifier( SmidgeHelper smidge, PreProcessPipelineFactory preProcessPipelineFactory, IManifestParser manifestParser, IHttpContextAccessor httpContextAccessor, PropertyEditorCollection propertyEditorCollection, IGlobalSettings globalSettings, IIOHelper ioHelper, IHostingEnvironment hostingEnvironment, ISmidgeConfig smidgeConfig, IConfigManipulator configManipulator) { _smidge = smidge; _preProcessPipelineFactory = preProcessPipelineFactory; _manifestParser = manifestParser; _httpContextAccessor = httpContextAccessor; _propertyEditorCollection = propertyEditorCollection; _globalSettings = globalSettings; _ioHelper = ioHelper; _hostingEnvironment = hostingEnvironment; _smidgeConfig = smidgeConfig; _configManipulator = configManipulator; } private PreProcessPipeline JsPipeline => _jsPipeline ??= _preProcessPipelineFactory.Create(typeof(JsMinifier)); private PreProcessPipeline CssPipeline => _cssPipeline ??= _preProcessPipelineFactory.Create(typeof(NuglifyCss)); private Uri GetRequestUrl() => new Uri(_httpContextAccessor.HttpContext.Request.GetEncodedUrl(), UriKind.Absolute); public string GetHashValue => _smidgeConfig.Version; public void RequiresCss(string bundleName, params string[] filePaths) => _smidge.CreateCssBundle(bundleName).RequiresCss(filePaths); public string RenderCssHere(string bundleName) => _smidge.CssHereAsync(bundleName).ToString(); public void RequiresJs(string bundleName, params string[] filePaths) => _smidge.CreateJsBundle(bundleName).RequiresJs(filePaths); public string RenderJsHere(string bundleName) => _smidge.JsHereAsync(bundleName).ToString(); public async Task> GetAssetPathsAsync(AssetType assetType, List attributes) { var files = attributes .Where(x => x.DependencyType == assetType) .Select(x=>x.FilePath) .ToArray(); if (files.Length == 0) return Array.Empty(); if (assetType == AssetType.Javascript) { _smidge.RequiresJs(files); return await _smidge.GenerateJsUrlsAsync(JsPipeline, _hostingEnvironment.IsDebugMode); } else { _smidge.RequiresCss(files); return await _smidge.GenerateCssUrlsAsync(CssPipeline, _hostingEnvironment.IsDebugMode); } } public async Task MinifyAsync(string fileContent, AssetType assetType) { if (assetType == AssetType.Javascript) { return await JsPipeline .ProcessAsync( new FileProcessContext(fileContent, new JavaScriptFile(), BundleContext.CreateEmpty())); } else { return await CssPipeline .ProcessAsync(new FileProcessContext(fileContent, new CssFile(), BundleContext.CreateEmpty())); } } /// /// /// /// /// Smidge uses the version number as cache buster (configurable). /// We therefore can reset, by updating the version number in config /// public void Reset() { var version = DateTime.UtcNow.Ticks.ToString(); _configManipulator.SaveConfigValue(Constants.Configuration.ConfigRuntimeMinificationVersion, version.ToString()); } public async Task GetScriptForBackOfficeAsync() { var initJs = new JsInitialization(_manifestParser, this, _propertyEditorCollection); var initCss = new CssInitialization(_manifestParser, this, _propertyEditorCollection); var requestUrl = GetRequestUrl(); var files = await initJs.OptimizeBackOfficeScriptFilesAsync(requestUrl, JsInitialization.GetDefaultInitialization()); var result = JavaScriptHelper.GetJavascriptInitialization(files, "umbraco", _globalSettings, _ioHelper); result += await initCss.GetStylesheetInitializationAsync(requestUrl); return result; } public async Task> GetAssetListAsync() { var initJs = new JsInitialization(_manifestParser, this, _propertyEditorCollection); var initCss = new CssInitialization(_manifestParser, this, _propertyEditorCollection); var assets = new List(); var requestUrl = GetRequestUrl(); assets.AddRange(await initJs.OptimizeBackOfficeScriptFilesAsync(requestUrl, Enumerable.Empty())); assets.AddRange(await initCss.GetStylesheetFilesAsync(requestUrl)); return assets; } } }