/// <summary>When overridden in a derived class, executes the task.</summary> /// <param name="contentItem">The result File.</param> internal void Execute(ContentItem contentItem = null) { var destinationDirectory = this.context.Configuration.DestinationDirectory; if ((contentItem == null) && string.IsNullOrWhiteSpace(this.SourceFile)) { throw new ArgumentException("MinifyJSActivity - The source file cannot be null or whitespace."); } if (string.IsNullOrWhiteSpace(this.DestinationFile)) { throw new ArgumentException("MinifyJSActivity - The destination file cannot be null or whitespace."); } if (contentItem == null) { contentItem = ContentItem.FromFile( this.SourceFile, Path.IsPathRooted(this.SourceFile) ? this.SourceFile.MakeRelativeToDirectory(destinationDirectory) : this.SourceFile); } var minifiedContentItem = this.Minify(contentItem); if (minifiedContentItem != null) { minifiedContentItem.WriteTo(this.DestinationFile); } }
/// <summary>The execute.</summary> /// <returns>The list of processed files.</returns> internal IEnumerable <ContentItem> Execute() { var preprocessedFiles = new List <ContentItem>(); var sourceDirectory = this.context.Configuration.SourceDirectory; foreach (var file in this.Inputs.GetFiles(sourceDirectory)) { var fi = new FileInfo(file); if (!fi.Exists) { throw new FileNotFoundException("Could not find the file {0} to preprocess on."); } if (!Directory.Exists(this.OutputFolder)) { Directory.CreateDirectory(this.OutputFolder); } var contentItem = ContentItem.FromFile(file, file.MakeRelativeToDirectory(sourceDirectory)); contentItem = this.context.Preprocessing.Process(contentItem, this.PreprocessingConfig, this.MinimalOutput); if (contentItem == null) { throw new WorkflowException("An error occurred while processing the file: " + file); } preprocessedFiles.Add(contentItem); } return(preprocessedFiles); }
/// <summary>Copies and hashes the Directory.</summary> /// <param name="sourceDirectory">Path to source directory.</param> /// <param name="destinationDirectory">Path to destination directory.</param> /// <param name="filters">Array of file filters to apply.</param> /// <param name="rootSourceDirectory">The root source directory</param> /// <returns>The result files after the hash.</returns> private IEnumerable <ContentItem> Hash(string sourceDirectory, string destinationDirectory, IEnumerable <string> filters, string rootSourceDirectory = null) { var results = new List <ContentItem>(); // Create the directory if does not exist. Directory.CreateDirectory(destinationDirectory); var sourceDirectoryInfo = new DirectoryInfo(sourceDirectory); rootSourceDirectory = rootSourceDirectory ?? sourceDirectoryInfo.FullName; // Need to do this for only the file type(s) desired results.AddRange( filters.SelectMany(filter => sourceDirectoryInfo .EnumerateFiles(filter, SearchOption.TopDirectoryOnly) .Select(sourceFileInfo => this.Hash(ContentItem.FromFile(sourceFileInfo.FullName, sourceFileInfo.FullName.MakeRelativeToDirectory(rootSourceDirectory)))))); // recurse through subdirs, either keeping the source dir structure in the destination or flattening it foreach (var subDirectoryInfo in sourceDirectoryInfo.GetDirectories()) { var subDestinationDirectory = this.ShouldPreserveSourceDirectoryStructure ? Path.Combine(destinationDirectory, subDirectoryInfo.Name) : destinationDirectory; results.AddRange( this.Hash(subDirectoryInfo.FullName, subDestinationDirectory, filters, rootSourceDirectory)); } return(results); }
/// <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); })); }
/// <summary>Execute a single config file.</summary> /// <param name="sessionContext">The session context.</param> /// <param name="configFile">The config file.</param> /// <param name="activity">The activity</param> /// <param name="fileType">The file types</param> /// <param name="measure">If it should measure.</param> /// <param name="fullPathToConfigFiles">The full Path To Config Files.</param> /// <returns>The success.</returns> private static Tuple <bool, int> ExecuteConfigFile(IWebGreaseContext sessionContext, string configFile, ActivityName activity, FileTypes fileType, bool measure, string fullPathToConfigFiles) { var configFileStart = DateTimeOffset.Now; var configFileInfo = new FileInfo(configFile); // Creates the context specific to the configuration file var fileContext = new WebGreaseContext(sessionContext, configFileInfo); var configFileSuccess = true; var fileSets = GetFileSets(fileContext.Configuration, fileType).ToArray(); if (fileSets.Length > 0 || (fileType.HasFlag(FileTypes.Image) && fileContext.Configuration.ImageDirectoriesToHash.Any())) { var configFileContentItem = ContentItem.FromFile(configFileInfo.FullName); configFileSuccess = sessionContext .SectionedActionGroup(fileType.ToString(), SectionIdParts.WebGreaseBuildTask, SectionIdParts.ConfigurationFile) .MakeCachable(configFileContentItem, new { activity, fileContext.Configuration }, activity == ActivityName.Bundle) // Cached action can only be skipped when it is the bundle activity, otherwise don't. .Execute(configFileCacheSection => { fileContext.Configuration.AllLoadedConfigurationFiles.ForEach(configFileCacheSection.AddSourceDependency); var success = true; fileContext.Log.Information("Activity Start: [{0}] for [{1}] on configuration file \"{2}\"".InvariantFormat(activity, fileType, configFile), MessageImportance.High); switch (activity) { case ActivityName.Bundle: // execute the bundle pipeline var bundleActivity = new BundleActivity(fileContext); success &= bundleActivity.Execute(fileSets); break; case ActivityName.Everything: // execute the full pipeline var everythingActivity = new EverythingActivity(fileContext); success &= everythingActivity.Execute(fileSets, fileType); break; } if (success && measure) { var configReportFile = Path.Combine(sessionContext.Configuration.ReportPath, (configFileInfo.Directory != null ? configFileInfo.Directory.Name + "." : string.Empty) + activity.ToString() + "." + fileType + "." + configFileInfo.Name); fileContext.Measure.WriteResults(configReportFile, configFileInfo.FullName, configFileStart); } fileContext.Log.Information("Activity End: [{0}] for [{1}] on configuration file \"{2}\"".InvariantFormat(activity, fileType, configFile), MessageImportance.High); return(success); }); } return(Tuple.Create(configFileSuccess, fileSets.Length)); }
/// <summary>The create sprited images.</summary> /// <param name="mapXmlFile">The map xml file.</param> /// <param name="imageAssemblyAnalysisLog">The image assembly analysis log.</param> /// <param name="imageReferencesToAssemble">The image references to assemble.</param> /// <param name="cacheSection">The cache section.</param> /// <param name="results">The results.</param> /// <param name="threadContext">The thread Context.</param> /// <returns>The <see cref="bool"/>.</returns> private ImageLog CreateSpritedImages( string mapXmlFile, ImageAssemblyAnalysisLog imageAssemblyAnalysisLog, IEnumerable <InputImage> imageReferencesToAssemble, ICacheSection cacheSection, BlockingCollection <ContentItem> results, IWebGreaseContext threadContext) { if (!Directory.Exists(this.ImagesOutputDirectory)) { Directory.CreateDirectory(this.ImagesOutputDirectory); } var imageMap = ImageAssembleGenerator.AssembleImages( imageReferencesToAssemble.ToSafeReadOnlyCollection(), SpritePackingType.Vertical, this.ImagesOutputDirectory, string.Empty, true, threadContext, this.ImageAssemblyPadding, imageAssemblyAnalysisLog, this.ForcedSpritingImageType); if (imageMap == null || imageMap.Document == null) { return(null); } var destinationDirectory = threadContext.Configuration.DestinationDirectory; if (!string.IsNullOrWhiteSpace(this.ImageAssembleScanDestinationFile)) { var scanXml = imageMap.Document.ToString(); FileHelper.WriteFile(mapXmlFile, scanXml); cacheSection.AddResult(ContentItem.FromFile(mapXmlFile), CacheFileCategories.SpriteLogFileXml); } var imageLog = new ImageLog(imageMap.Document); cacheSection.AddResult(ContentItem.FromContent(imageLog.ToJson(true)), CacheFileCategories.SpriteLogFile); foreach (var spritedFile in imageLog.InputImages.Select(il => il.OutputFilePath).Distinct()) { var spritedImageContentItem = ContentItem.FromFile(spritedFile, spritedFile.MakeRelativeToDirectory(destinationDirectory)); results.Add(spritedImageContentItem); cacheSection.AddResult(spritedImageContentItem, CacheFileCategories.HashedSpriteImage); } return(imageLog); }
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>The append file.</summary> /// <param name="writer">The writer.</param> /// <param name="filePath">The file path</param> /// <param name="sourceDirectory">The source directory</param> /// <param name="preprocessingConfig">The configuration for the preprocessing.</param> private void Append(TextWriter writer, string filePath, string sourceDirectory, PreprocessingConfig preprocessingConfig = null) { // Add a newline to make sure what comes next doesn't get mistakenly attached to the end of // a single-line comment or anything. add two so we get an easy-to-read separation between files // for debugging purposes. writer.WriteLine(); writer.WriteLine(); // if we want to separate files with semicolons and the previous file didn't have one, add one now if (this.AddSemicolons && !this.endedInSemicolon) { writer.Write(';'); } string relativeFilePath = Path.IsPathRooted(filePath) && !sourceDirectory.IsNullOrWhitespace() ? filePath.MakeRelativeTo(sourceDirectory) : filePath; if (!this.MinimalOutput) { writer.WriteLine("/* {0} {1} */".InvariantFormat(relativeFilePath, filePath != relativeFilePath ? "(" + filePath + ")" : string.Empty)); writer.WriteLine(); } var contentItem = ContentItem.FromFile(filePath, relativeFilePath); // Executing any applicable preprocessors from the list of configured preprocessors on the file content if (preprocessingConfig != null && preprocessingConfig.Enabled) { contentItem = this.context.Preprocessing.Process(contentItem, preprocessingConfig, this.MinimalOutput); if (contentItem == null) { throw new WorkflowException("Could not assembly the file {0} because one of the preprocessors threw an error.".InvariantFormat(filePath)); } } // TODO:RTUIT: Use a writer/reader instead of getting the content and check differently for the endoign semicolon. Also fix not passing encoding. ONly when not using any preprocessors. var content = contentItem.Content; writer.Write(content); writer.WriteLine(); // don't even bother checking for a semicolon if we aren't interested in adding one if (this.AddSemicolons) { this.endedInSemicolon = EndsWithSemicolon.IsMatch(content); } }
public void TestWgInclude1() { var includeFile = Path.Combine(TestDeploymentPaths.TestDirectory, @"WebGrease.Tests\IncludeTest\Test1\test1.js"); var ie = new IncludePreprocessingEngine(); var webGreaseContext = new WebGreaseContext(new WebGreaseConfiguration() { DestinationDirectory = TestDeploymentPaths.TestDirectory }); var result = ie.Process(webGreaseContext, ContentItem.FromFile(includeFile, includeFile.MakeRelativeToDirectory(TestDeploymentPaths.TestDirectory)), new PreprocessingConfig(), false).Content; Assert.IsTrue(result.Contains("included1();")); Assert.IsTrue(result.Contains("included2();")); Assert.IsTrue(result.Contains("included3();")); Assert.IsTrue(result.Contains("included4();")); Assert.IsFalse(result.Contains("included5();")); }
private ContentItem Bundle(ContentItemType targetContentItemType, string outputDirectory, string outputFile, string sourceDirectory) { var contentBuilder = new StringBuilder(); using ( var writer = targetContentItemType == ContentItemType.Path ? new StreamWriter(outputFile, false, Encoding.UTF8) : new StringWriter(contentBuilder, CultureInfo.InvariantCulture) as TextWriter) { this.context.Log.Information("Start bundling output file: {0}".InvariantFormat(outputFile)); foreach (var file in this.Inputs.GetFiles(sourceDirectory, this.context.Log, true)) { this.Append(writer, file, sourceDirectory, this.PreprocessingConfig); } this.context.Log.Information("End bundling output file: {0}".InvariantFormat(outputFile)); } return(targetContentItemType == ContentItemType.Path ? ContentItem.FromFile(outputFile, outputFile.MakeRelativeTo(outputDirectory)) : ContentItem.FromContent(contentBuilder.ToString(), outputFile.MakeRelativeTo(outputDirectory))); }
private static string ProcessSass(string content, string filename, LogExtendedError logExtendedError = null) { var sassPreprocessingEngine = new SassPreprocessingEngine(); var webGreaseContext = new WebGreaseContext(new WebGreaseConfiguration(), logInformation: null, logExtendedWarning: null, logError: null, logExtendedError: logExtendedError); File.WriteAllText(filename, content); var processSassResult = sassPreprocessingEngine.Process(webGreaseContext, ContentItem.FromFile(filename), null, false); return(processSassResult != null ? processSassResult.Content : null); }