/// <summary>Gets the unique object for all the settings, used for determining unique cache id.</summary> /// <param name="imageHasher">The image Hasher.</param> /// <param name="resourcePivotKeys">The resource Pivot Keys.</param> /// <param name="dpiResources">The dpi Resources.</param> /// <returns>The settings object.</returns> private object GetVarBySettings(FileHasherActivity imageHasher, IEnumerable <ResourcePivotKey> resourcePivotKeys, IEnumerable <IDictionary <string, string> > dpiResources) { return(new { resourcePivotKeys, dpiResources, this.ShouldExcludeProperties, this.ShouldValidateForLowerCase, this.ShouldMergeMediaQueries, this.ShouldOptimize, this.ShouldAssembleBackgroundImages, this.ShouldMinify, this.ShouldPreventOrderBasedConflict, this.ShouldMergeBasedOnCommonDeclarations, this.IgnoreImagesWithNonDefaultBackgroundSize, this.HackSelectors, this.BannedSelectors, this.NonMergeSelectors, this.ImageAssembleReferencesToIgnore, this.OutputUnit, this.OutputUnitFactor, this.ImageAssemblyPadding, HashImages = imageHasher == null, this.ForcedSpritingImageType, this.ErrorOnInvalidSprite, this.ImageAssembleScanDestinationFile, this.ImageSpritingLogPath, }); }
internal void Execute(ContentItem contentItem = null, FileHasherActivity imageHasher = null) { if (contentItem == null) { if (string.IsNullOrWhiteSpace(this.SourceFile)) { throw new ArgumentException("MinifyCssActivity - The source file cannot be null or whitespace."); } if (!File.Exists(this.SourceFile)) { throw new FileNotFoundException("MinifyCssActivity - The source file cannot be found.", this.SourceFile); } } if (string.IsNullOrWhiteSpace(this.DestinationFile)) { throw new ArgumentException("MinifyCssActivity - The destination file cannot be null or whitespace."); } if (contentItem == null) { contentItem = ContentItem.FromFile(this.SourceFile, Path.IsPathRooted(this.SourceFile) ? this.SourceFile.MakeRelativeToDirectory(this.context.Configuration.SourceDirectory) : this.SourceFile); } var minifyresult = this.Process(contentItem, imageHasher); var css = minifyresult.Css.FirstOrDefault(); if (css != null) { css.WriteTo(this.DestinationFile); } if (minifyresult.SpritedImages != null && minifyresult.SpritedImages.Any()) { foreach (var spritedImage in minifyresult.SpritedImages) { spritedImage.WriteToContentPath(this.context.Configuration.DestinationDirectory); } } if (minifyresult.HashedImages != null && minifyresult.HashedImages.Any()) { foreach (var hashedImage in minifyresult.HashedImages) { hashedImage.WriteToRelativeHashedPath(this.context.Configuration.DestinationDirectory); } } }
/// <summary>Hash the images.</summary> /// <param name="cssContent">The css content.</param> /// <param name="imageHasher">The image Hasher.</param> /// <param name="cacheSection">The cache section.</param> /// <param name="threadContext">The context.</param> /// <param name="sourceImages">The source Images.</param> /// <param name="missingImage">The missing Image.</param> /// <returns>The css with hashed images.</returns> private static Tuple <string, IEnumerable <ContentItem> > HashImages(string cssContent, FileHasherActivity imageHasher, ICacheSection cacheSection, IWebGreaseContext threadContext, IDictionary <string, string> sourceImages, string missingImage) { return(threadContext.SectionedAction(SectionIdParts.MinifyCssActivity, SectionIdParts.ImageHash).Execute(() => { var contentImagesToHash = new HashSet <string>(); var hashedContentItems = new List <ContentItem>(); var hashedImages = new Dictionary <string, string>(); cssContent = UrlHashRegexPattern.Replace( cssContent, match => { var urlToHash = match.Groups["url"].Value; var extraInfo = match.Groups["extra"].Value; if (ResourcesResolver.LocalizationResourceKeyRegex.IsMatch(urlToHash)) { return match.Value; } var normalizedHashUrl = urlToHash.NormalizeUrl(); var imageContentFile = sourceImages.TryGetValue(normalizedHashUrl); if (imageContentFile == null && !string.IsNullOrWhiteSpace(missingImage)) { imageContentFile = sourceImages.TryGetValue(missingImage); } if (imageContentFile == null) { throw new BuildWorkflowException("Could not find a matching source image for url: {0}".InvariantFormat(urlToHash)); } if (contentImagesToHash.Add(normalizedHashUrl)) { // Add the image as end result var imageContentItem = ContentItem.FromFile(imageContentFile, normalizedHashUrl); imageContentItem = imageHasher.Hash(imageContentItem); cacheSection.AddSourceDependency(imageContentFile); hashedContentItems.Add(imageContentItem); imageContentFile = Path.Combine( imageHasher.BasePrefixToAddToOutputPath ?? Path.AltDirectorySeparatorChar.ToString(CultureInfo.InvariantCulture), imageContentItem.RelativeHashedContentPath.Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)); hashedImages.Add(normalizedHashUrl, imageContentFile); } else { imageContentFile = hashedImages[normalizedHashUrl]; } return "url(" + imageContentFile + extraInfo + ")"; }); return Tuple.Create(cssContent, (IEnumerable <ContentItem>)hashedContentItems); })); }
internal MinifyCssResult Process(ContentItem contentItem, FileHasherActivity imageHasher = null) { if (imageHasher != null) { this.availableSourceImages = this.context.GetAvailableFiles(this.context.Configuration.SourceDirectory, this.ImageDirectories, this.ImageExtensions, FileTypes.Image); } var cssContent = contentItem.Content; var minifiedContentItems = new BlockingCollection <ContentItem>(); var hashedImageContentItems = new BlockingCollection <ContentItem>(); var spritedImageContentItems = new BlockingCollection <ContentItem>(); var mergedResources = ResourcePivotActivity.GetUsedGroupedResources(cssContent, this.MergedResources); var dpiValues = this.Dpi; if (dpiValues == null || !dpiValues.Any()) { dpiValues = new HashSet <float>(new[] { 1f }); } var pivots = GetMinifyCssPivots(contentItem, dpiValues, mergedResources, this.DpiResources).ToArray(); var nonIgnoredPivots = pivots.Where(p => !this.context.TemporaryIgnore(p.NewContentResourcePivotKeys)).ToArray(); var parsedStylesheetNode = CssParser.Parse(this.context, cssContent, false); this.context.ParallelForEach( item => new[] { SectionIdParts.MinifyCssActivity }, nonIgnoredPivots, (threadContext, pivot, parallelLoopState) => { ContentItem minifiedContentItem = null; var resourceContentItem = ContentItem.FromContent(contentItem.Content, pivot.NewContentResourcePivotKeys); var result = threadContext .SectionedAction(SectionIdParts.MinifyCssActivity, SectionIdParts.Process) .MakeCachable(resourceContentItem, this.GetVarBySettings(imageHasher, pivot.NewContentResourcePivotKeys, pivot.MergedResource)) .RestoreFromCacheAction(cacheSection => { minifiedContentItem = cacheSection.GetCachedContentItem(CacheFileCategories.MinifiedCssResult, contentItem.RelativeContentPath, contentItem.AbsoluteDiskPath, pivot.NewContentResourcePivotKeys); hashedImageContentItems.AddRange(cacheSection.GetCachedContentItems(CacheFileCategories.HashedImage)); spritedImageContentItems.AddRange(cacheSection.GetCachedContentItems(CacheFileCategories.HashedSpriteImage)); if (minifiedContentItem == null) { context.Log.Error("Css minify cache result is null"); return(false); } if (spritedImageContentItems.Any(hi => hi == null)) { context.Log.Error("Sprited image cache result is null"); return(false); } if (hashedImageContentItems.Any(hi => hi == null)) { context.Log.Error("Hashed image cache result is null"); return(false); } return(true); }) .Execute(cacheSection => { try { // Apply all configured visitors, including, validating, optimizing, minifying and spriting. // Applying of resources var stylesheetNode = ApplyResources(parsedStylesheetNode, pivot.MergedResource, threadContext) as StyleSheetNode; // Validation stylesheetNode = this.ApplyValidation(stylesheetNode, threadContext) as StyleSheetNode; // Optimization stylesheetNode = this.ApplyOptimization(stylesheetNode, threadContext) as StyleSheetNode; // Spriting stylesheetNode = this.ApplySpriting(stylesheetNode, pivot.Dpi, spritedImageContentItems, threadContext) as StyleSheetNode; // Output css as string var processedCssContent = threadContext.SectionedAction(SectionIdParts.MinifyCssActivity, SectionIdParts.PrintCss).Execute(() => this.ShouldMinify ? stylesheetNode.MinifyPrint() : stylesheetNode.PrettyPrint()); // TODO: Hash the images on the styielsheetnode not on the css result. // Hash images on the result css if (imageHasher != null) { var hashResult = HashImages(processedCssContent, imageHasher, cacheSection, threadContext, this.availableSourceImages, this.MissingImageUrl); processedCssContent = hashResult.Item1; hashedImageContentItems.AddRange(hashResult.Item2); } minifiedContentItem = ContentItem.FromContent(processedCssContent, this.DestinationFile, null, pivot.NewContentResourcePivotKeys); cacheSection.AddResult(minifiedContentItem, CacheFileCategories.MinifiedCssResult); } catch (Exception ex) { context.Log.Error(ex, ex.ToString()); return(false); } return(true); }); Safe.Lock(minifiedContentItems, () => minifiedContentItems.Add(minifiedContentItem)); if (!result) { context.Log.Error("An errror occurred while minifying '{0}' with resources '{1}'".InvariantFormat(contentItem.RelativeContentPath, pivot)); } return(result); }); return(new MinifyCssResult( minifiedContentItems, spritedImageContentItems.DistinctBy(hi => hi.RelativeContentPath).ToArray(), hashedImageContentItems.DistinctBy(hi => hi.RelativeContentPath).ToArray())); }