/// <summary>The sprite image.</summary>
        /// <param name="scanOutput">The scan Output.</param>
        /// <param name="mapXmlFile">The map xml file</param>
        /// <param name="imageAssemblyAnalysisLog">The image Assembly Analysis Log.</param>
        /// <param name="threadContext">The thread Context.</param>
        /// <param name="spritedImageContentItems">The sprited Image Content Items.</param>
        /// <returns>The <see cref="ImageLog"/>.</returns>
        private ImageLog SpriteImageFromLog(ImageAssemblyScanOutput scanOutput, string mapXmlFile, ImageAssemblyAnalysisLog imageAssemblyAnalysisLog, IWebGreaseContext threadContext, BlockingCollection <ContentItem> spritedImageContentItems)
        {
            if (scanOutput == null || !scanOutput.ImageReferencesToAssemble.Any())
            {
                return(null);
            }

            ImageLog imageLog = null;
            var      imageReferencesToAssemble = scanOutput.ImageReferencesToAssemble;

            if (imageReferencesToAssemble == null || imageReferencesToAssemble.Count == 0)
            {
                return(null);
            }

            var varBySettings = new { imageMap = GetRelativeSpriteCacheKey(imageReferencesToAssemble, threadContext), this.ImageAssemblyPadding };
            var success       = threadContext.SectionedAction(SectionIdParts.MinifyCssActivity, SectionIdParts.Spriting, SectionIdParts.Assembly)
                                .MakeCachable(varBySettings, false, true)
                                .RestoreFromCacheAction(cacheSection =>
            {
                imageLog = RestoreSpritedImagesFromCache(mapXmlFile, cacheSection, spritedImageContentItems, threadContext.Configuration.DestinationDirectory, this.ImageAssembleScanDestinationFile);
                return(imageLog != null);
            })
                                .Execute(cacheSection =>
            {
                imageLog = this.CreateSpritedImages(mapXmlFile, imageAssemblyAnalysisLog, imageReferencesToAssemble, cacheSection, spritedImageContentItems, threadContext);
                return(imageLog != null);
            });

            return
                (success
                ? imageLog
                : null);
        }
        /// <summary>Applies the css validation visitors.</summary>
        /// <param name="stylesheetNode">The stylesheet node.</param>
        /// <param name="threadContext">The thread Context.</param>
        /// <returns>The processed node.</returns>
        private AstNode ApplyValidation(AstNode stylesheetNode, IWebGreaseContext threadContext)
        {
            threadContext.SectionedAction(SectionIdParts.MinifyCssActivity, SectionIdParts.Validate).Execute(() =>
            {
                // Step # 1 - Remove the Css properties from Ast which need to be excluded (Bridging)
                if (this.ShouldExcludeProperties)
                {
                    stylesheetNode = stylesheetNode.Accept(new ExcludePropertyVisitor());
                }

                // Step # 2 - Validate for lower case
                if (this.ShouldValidateForLowerCase)
                {
                    stylesheetNode = stylesheetNode.Accept(new ValidateLowercaseVisitor());
                }

                // Step # 3 - Validate for Css hacks which don't work cross browser
                if (this.HackSelectors != null && this.HackSelectors.Any())
                {
                    stylesheetNode = stylesheetNode.Accept(new SelectorValidationOptimizationVisitor(this.HackSelectors, false, true));
                }

                // Step # 4 - Remove any banned selectors which are exposed for page efficiency
                if (this.BannedSelectors != null && this.BannedSelectors.Any())
                {
                    stylesheetNode = stylesheetNode.Accept(new SelectorValidationOptimizationVisitor(this.BannedSelectors, false, false));
                }
            });

            return(stylesheetNode);
        }
        /// <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);
            }));
        }
Esempio n. 4
0
        /// <summary>Parse the styleSheet.</summary>
        /// <param name="cssContent">The css content.</param>
        /// <param name="shouldLogDiagnostics">The should log diagnostics.</param>
        /// <returns>The styleSheet node.</returns>
        private static StyleSheetNode ParseStyleSheet(IWebGreaseContext context, string cssContent, bool shouldLogDiagnostics)
        {
            var lexer       = new CssLexer(new ANTLRStringStream(cssContent));
            var tokenStream = new CommonTokenStream(lexer);
            var parser      = new CssParser(tokenStream);

            var commonTree = context
                             .SectionedAction("CssParser", "Antlr")
                             .Execute(() =>
            {
                // Assign listener to parser.
                if (shouldLogDiagnostics)
                {
                    var listener = Trace.Listeners.OfType <TextWriterTraceListener>().FirstOrDefault();
                    if (listener != null)
                    {
                        parser.TraceDestination = listener.Writer;
                    }
                }

                var styleSheet = parser.main();
                return(styleSheet.Tree as CommonTree);
            });

            if (commonTree != null)
            {
                return(context.SectionedAction("CssParser", "CreateObjects").Execute(() =>
                {
                    if (shouldLogDiagnostics)
                    {
                        LogDiagnostics(cssContent, commonTree);
                    }

                    if (parser.NumberOfSyntaxErrors > 0)
                    {
                        throw new AggregateException("Syntax errors found.", parser._exceptions);
                    }

                    return CommonTreeTransformer.CreateStyleSheetNode(commonTree);
                }));
            }

            return(null);
        }
        /// <summary>Assembles the background images</summary>
        /// <param name="stylesheetNode">the style sheet node</param>
        /// <param name="dpi">The dpi.</param>
        /// <param name="threadContext">The thread Context.</param>
        /// <param name="spritedImageContentItems">The sprited Image Content Items.</param>
        /// <returns>The stylesheet node with the sprited images aplied.</returns>
        private AstNode SpriteBackgroundImages(AstNode stylesheetNode, float dpi, IWebGreaseContext threadContext, BlockingCollection <ContentItem> spritedImageContentItems)
        {
            // The image assembly is a 3 step process:
            // 1. Scan the Css followed by Pretty Print
            // 2. Run the image assembly tool
            // 3. Update the Css with generated images followed by Pretty Print
            return(threadContext.SectionedAction(SectionIdParts.MinifyCssActivity, SectionIdParts.Spriting).Execute(() =>
            {
                // Execute the pipeline for image assembly scan
                var scanLog = this.ExecuteImageAssemblyScan(stylesheetNode, threadContext);

                // Execute the pipeline for image assembly tool
                var imageMaps = new List <ImageLog>();
                var count = 0;
                foreach (var scanOutput in scanLog.ImageAssemblyScanOutputs)
                {
                    var spriteResult = this.SpriteImageFromLog(scanOutput, this.ImageAssembleScanDestinationFile + (count == 0 ? string.Empty : "." + count) + ".xml", scanLog.ImageAssemblyAnalysisLog, threadContext, spritedImageContentItems);
                    if (spriteResult != null)
                    {
                        imageMaps.Add(spriteResult);
                        count++;
                    }
                }

                // Step # 8 - Execute the pipeline for image assembly update
                stylesheetNode = this.ExecuteImageAssemblyUpdate(stylesheetNode, imageMaps, dpi);

                if (!string.IsNullOrWhiteSpace(this.ImageSpritingLogPath))
                {
                    scanLog.ImageAssemblyAnalysisLog.Save(this.ImageSpritingLogPath);
                }

                var imageAssemblyAnalysisLog = scanLog.ImageAssemblyAnalysisLog;
                if (this.ErrorOnInvalidSprite && imageAssemblyAnalysisLog.FailedSprites.Any())
                {
                    foreach (var failedSprite in imageAssemblyAnalysisLog.FailedSprites)
                    {
                        var failureMessage = ImageAssemblyAnalysisLog.GetFailureMessage(failedSprite);
                        if (!string.IsNullOrWhiteSpace(failedSprite.Image))
                        {
                            threadContext.Log.Error(
                                "Failed to sprite image {0}\r\nReason:{1}\r\nCss:{2}".InvariantFormat(
                                    failedSprite.Image, failureMessage, failedSprite.AstNode.PrettyPrint()));
                        }
                        else
                        {
                            threadContext.Log.Error(
                                "Failed to sprite:{0}\r\nReason:{1}".InvariantFormat(
                                    failedSprite.Image, failureMessage));
                        }
                    }
                }

                return stylesheetNode;
            }));
        }
        /// <summary>Applies the css resource visitors.</summary>
        /// <param name="stylesheetNode">The stylesheet node.</param>
        /// <param name="resources">The resources.</param>
        /// <param name="threadContext">The thread Context.</param>
        /// <returns>The processed node.</returns>
        private static AstNode ApplyResources(AstNode stylesheetNode, IEnumerable <IDictionary <string, string> > resources, IWebGreaseContext threadContext)
        {
            if (resources.Any())
            {
                threadContext.SectionedAction(SectionIdParts.MinifyCssActivity, SectionIdParts.ResourcesResolution)
                .Execute(() => { stylesheetNode = stylesheetNode.Accept(new ResourceResolutionVisitor(resources)); });
            }

            return(stylesheetNode);
        }
        /// <summary>Applies the css optimization visitors.</summary>
        /// <param name="stylesheetNode">The stylesheet node.</param>
        /// <param name="threadContext">The thread Context.</param>
        /// <returns>The processed node.</returns>
        private AstNode ApplyOptimization(AstNode stylesheetNode, IWebGreaseContext threadContext)
        {
            // Step # 5 - Run the Css optimization visitors
            if (this.ShouldOptimize)
            {
                threadContext.SectionedAction(SectionIdParts.MinifyCssActivity, SectionIdParts.Optimize).Execute(
                    () =>
                {
                    stylesheetNode = stylesheetNode.Accept(new OptimizationVisitor {
                        ShouldMergeMediaQueries = this.ShouldMergeMediaQueries, ShouldPreventOrderBasedConflict = this.ShouldPreventOrderBasedConflict, ShouldMergeBasedOnCommonDeclarations = this.ShouldMergeBasedOnCommonDeclarations, NonMergeRuleSetSelectors = this.NonMergeSelectors
                    });
                    stylesheetNode = stylesheetNode.Accept(new ColorOptimizationVisitor());
                    stylesheetNode = stylesheetNode.Accept(new FloatOptimizationVisitor());
                });
            }

            return(stylesheetNode);
        }
Esempio n. 8
0
        /// <summary>Processed the contents of the file and returns the processed content.
        /// returns null if anything went wrong, and reports any errors through the lot delegates.</summary>
        /// <param name="context">The context.</param>
        /// <param name="contentItem">The content of the file.</param>
        /// <param name="preprocessingConfig">The pre processing configuration</param>
        /// <param name="minimalOutput">Is the goal to have the most minimal output (true skips lots of comments)</param>
        /// <returns>The processed contents or null of an error occurred.</returns>
        public ContentItem Process(IWebGreaseContext context, ContentItem contentItem, PreprocessingConfig preprocessingConfig, bool minimalOutput)
        {
            var settingsMinimalOutput = preprocessingConfig != null && preprocessingConfig.Element != null && (bool?)preprocessingConfig.Element.Attribute("minimalOutput") == true;

            context.SectionedAction(SectionIdParts.Preprocessing, SectionIdParts.Process, "WgInclude")
            .MakeCachable(contentItem, new { minimalOutput })
            .Execute(wgincludeCacheImportsSection =>
            {
                var workingFolder = context.GetWorkingSourceDirectory(contentItem.RelativeContentPath);
                var content       = contentItem.Content;
                if (string.IsNullOrWhiteSpace(content))
                {
                    return(true);
                }

                content     = IncludeRegex.Replace(content, match => ReplaceInputs(match, workingFolder, wgincludeCacheImportsSection, minimalOutput || settingsMinimalOutput));
                contentItem = ContentItem.FromContent(content, contentItem);
                return(true);
            });

            return(contentItem);
        }
Esempio n. 9
0
        public ContentItem Process(IWebGreaseContext context, ContentItem contentItem, PreprocessingConfig preprocessingConfig, bool minimalOutput)
        {
            var settingsMinimalOutput = preprocessingConfig != null && preprocessingConfig.Element != null && (bool?)preprocessingConfig.Element.Attribute("minimalOutput") == true;
            var relativeContentPath   = contentItem.RelativeContentPath;

            context.Log.Information("Sass: Processing contents for file {0}".InvariantFormat(relativeContentPath));

            context.SectionedAction(SectionIdParts.Preprocessing, SectionIdParts.Process, "Sass")
            .Execute(
                () =>
            {
                var sassCacheImportsSection = context.Cache.CurrentCacheSection;

                string fileToProcess = null;
                var isTemp           = false;
                try
                {
                    var workingDirectory = Path.IsPathRooted(relativeContentPath)
                                                       ? Path.GetDirectoryName(relativeContentPath)
                                                       : context.GetWorkingSourceDirectory(relativeContentPath);

                    var content = ParseImports(contentItem.Content, workingDirectory, sassCacheImportsSection, minimalOutput || settingsMinimalOutput);

                    var currentContentHash = context.GetValueHash(content);

                    var contentIsUnchangedFromDisk = !string.IsNullOrWhiteSpace(contentItem.AbsoluteDiskPath) &&
                                                     File.Exists(contentItem.AbsoluteDiskPath) &&
                                                     context.GetFileHash(contentItem.AbsoluteDiskPath).Equals(currentContentHash);

                    if (contentIsUnchangedFromDisk)
                    {
                        fileToProcess = contentItem.AbsoluteDiskPath;
                    }
                    else if (!string.IsNullOrWhiteSpace(relativeContentPath))
                    {
                        fileToProcess = Path.Combine(context.Configuration.SourceDirectory ?? string.Empty, relativeContentPath);

                        fileToProcess       = PrependToExtension(fileToProcess, ".imports");
                        relativeContentPath = PrependToExtension(relativeContentPath, ".imports");

                        if (!File.Exists(fileToProcess) || !context.GetFileHash(fileToProcess).Equals(currentContentHash))
                        {
                            File.WriteAllText(fileToProcess, content);
                        }
                    }
                    else
                    {
                        isTemp        = true;
                        fileToProcess = Path.GetTempFileName() + Path.GetExtension(relativeContentPath);
                        File.WriteAllText(fileToProcess, content);
                    }

                    content = ProcessFile(fileToProcess, workingDirectory, relativeContentPath, context);

                    contentItem = content != null ? ContentItem.FromContent(content, contentItem) : null;

                    return(true);
                }
                finally
                {
                    if (isTemp && !string.IsNullOrWhiteSpace(fileToProcess))
                    {
                        try
                        {
                            File.Delete(fileToProcess);
                        }
                        catch (Exception)
                        {
                        }
                    }
                }
            });

            return(contentItem);
        }
Esempio n. 10
0
        /// <summary>The execute.</summary>
        /// <param name="fileType">The file type.</param>
        /// <param name="activity">The activity.</param>
        /// <param name="configurationPath">The configuration path</param>
        /// <param name="sessionContext">The session context</param>
        /// <returns>The <see cref="Tuple"/>.</returns>
        private bool Execute(FileTypes fileType, ActivityName activity, string configurationPath, IWebGreaseContext sessionContext)
        {
            var availableFileSetsForFileType = 0;

            var fullPathToConfigFiles = Path.GetFullPath(configurationPath);

            var contentTypeSectionId = new[] { fileType.ToString(), SectionIdParts.WebGreaseBuildTask };

            var sessionSuccess = sessionContext
                                 .SectionedAction(SectionIdParts.WebGreaseBuildTaskSession, fileType.ToString())
                                 .MakeCachable(new { fullPathToConfigFiles, activity })
                                 .Execute(sessionCacheSection =>
            {
                var sessionCacheData = sessionCacheSection.GetCacheData <SessionCacheData>(CacheFileCategories.SolutionCacheConfig);

                var inputFiles = new InputSpec {
                    Path = fullPathToConfigFiles, IsOptional = true, SearchOption = this.ConfigurationPathRecursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly
                }.GetFiles();
                var contentTypeSuccess = sessionContext
                                         .SectionedAction(contentTypeSectionId)
                                         .MakeCachable(new { activity, inputFiles, sessionContext.Configuration }, activity == ActivityName.Bundle)
                                         .Execute(contentTypeCacheSection =>
                {
                    var success = true;
                    try
                    {
                        // get a list of the files in the configuration folder
                        foreach (var configFile in inputFiles)
                        {
                            var executeConfigFileResult = ExecuteConfigFile(sessionContext, configFile, activity, fileType, this.Measure, fullPathToConfigFiles);
                            success &= executeConfigFileResult.Item1;
                            availableFileSetsForFileType += executeConfigFileResult.Item2;
                            GC.Collect();
                        }
                    }
                    catch (BuildWorkflowException exception)
                    {
                        this.LogError(exception.Subcategory, exception.ErrorCode, exception.HelpKeyword, exception.File, exception.LineNumber, exception.EndLineNumber, exception.ColumnNumber, exception.EndColumnNumber, exception.Message);
                        return(false);
                    }
                    catch (Exception exception)
                    {
                        this.LogError(exception, exception.Message, null);
                        return(false);
                    }

                    if (success)
                    {
                        // Add the current cachesection to the sessionCacheData
                        if (sessionCacheData != null)
                        {
                            sessionCacheData.SetConfigTypeUniqueKey(this.ConfigType, contentTypeCacheSection.UniqueKey);
                            sessionCacheSection.SetCacheData(CacheFileCategories.SolutionCacheConfig, sessionCacheData);
                        }
                    }

                    return(success);
                });

                if (contentTypeSuccess)
                {
                    // Add the cache sections of the other already cached contentTypes (Debug/Release) so that they will not get removed.
                    this.HandleOtherContentTypeCacheSections(sessionContext, sessionCacheSection, sessionCacheData, contentTypeSectionId);
                }

                return(contentTypeSuccess);
            });

            if (sessionSuccess && (availableFileSetsForFileType > 0))
            {
                sessionContext.Cache.CleanUp();
            }

            // Return tuple, that contains,the success, the results and the configuration for this thread.
            return(sessionSuccess);
        }