private async static Task <string> CombineFiles(Dictionary <string, string> files, string extension, BundleDocument bundle, string bundleFile) { StringBuilder sb = new StringBuilder(); foreach (string file in files.Keys) { if (extension.Equals(".js", StringComparison.OrdinalIgnoreCase) && WESettings.Instance.JavaScript.GenerateSourceMaps) { sb.AppendLine("///#source 1 1 " + files[file]); } var source = await FileHelpers.ReadAllTextRetry(file); if (extension.Equals(".css", StringComparison.OrdinalIgnoreCase)) { // If the bundle is in the same folder as the CSS, // or if does not have URLs, no need to normalize. if (Path.GetDirectoryName(file) != Path.GetDirectoryName(bundleFile) && source.IndexOf("url(", StringComparison.OrdinalIgnoreCase) > 0 && bundle.AdjustRelativePaths) { source = CssUrlNormalizer.NormalizeUrls( tree: new CssParser().Parse(source, true), targetFile: bundleFile, oldBasePath: file ); } } sb.AppendLine(source); } return(sb.ToString()); }
public async static Task <bool> MakeBundle(BundleDocument document, string bundleFile, Func <string, bool, Task> updateBundle) { // filePath must end in ".targetExtension.bundle" string extension = Path.GetExtension(Path.GetFileNameWithoutExtension(document.FileName)); if (string.IsNullOrEmpty(extension)) { Logger.Log("Skipping bundle file " + document.FileName + " without extension. Bundle files must end with the output extension, followed by '.bundle'."); return(false); } Dictionary <string, string> files = await WatchFiles(document, updateBundle, bundleFile); string combinedContent = await CombineFiles(files, extension, document, bundleFile); bool bundleChanged = !File.Exists(bundleFile) || await FileHelpers.ReadAllTextRetry(bundleFile) != combinedContent; if (bundleChanged) { if (!ProjectHelpers.CheckOutFileFromSourceControl(bundleFile)) { throw new Exception("There was a problem checking out the file: " + bundleFile); } await FileHelpers.WriteAllTextRetry(bundleFile, combinedContent); Logger.Log("Web Essentials: Updated bundle: " + Path.GetFileName(bundleFile)); } ProjectHelpers.AddFileToProject(document.FileName, bundleFile); return(bundleChanged); }
public async void Navigate(object sender, EventArgs e) { ErrorTask task = (ErrorTask)sender; if (MessageBox.Show(Question, "Web Essentials", MessageBoxButtons.YesNo) == DialogResult.Yes) { if (!File.Exists(_file)) { return; } string html = await FileHelpers.ReadAllTextRetry(_file); int index = html.IndexOf("</head>", StringComparison.OrdinalIgnoreCase); if (index > -1) { EditorExtensionsPackage.DTE.ItemOperations.OpenFile(_file); _extension.ErrorList.Tasks.Remove(task); AddMetaTag(index); return; } } }
private async Task <CompilerResult> ProcessResult(Process process, string errorText, string sourceFileName, string targetFileName, string mapFileName) { string result = ""; bool success = false; IEnumerable <CompilerError> errors = null; try { if (process.ExitCode == 0) { if (!string.IsNullOrEmpty(targetFileName) && File.Exists(targetFileName)) { result = await FileHelpers.ReadAllTextRetry(targetFileName); } success = true; } else { errors = ParseErrors(errorText); } } catch (FileNotFoundException missingFileException) { Logger.Log(ServiceName + ": " + Path.GetFileName(targetFileName) + " compilation failed. " + missingFileException.Message); } if (success) { var renewedResult = await PostProcessResult(result, sourceFileName, targetFileName, mapFileName); if (!ReferenceEquals(result, renewedResult)) { await FileHelpers.WriteAllTextRetry(targetFileName, renewedResult); result = renewedResult; } } var compilerResult = await CompilerResultFactory.GenerateResult( sourceFileName : sourceFileName, targetFileName : targetFileName, mapFileName : mapFileName, isSuccess : success, result : result, errors : errors ) as CompilerResult; if (!success) { var firstError = compilerResult.Errors.Where(e => e != null).Select(e => e.Message).FirstOrDefault(); if (firstError != null) { Logger.Log(ServiceName + ": " + Path.GetFileName(sourceFileName) + " compilation failed: " + firstError); } } return(compilerResult); }
protected async override Task RtlVariantHandler(CompilerResult result) { if (!WESettings.Instance.Css.RtlCss || result.RtlTargetFileName == null) { return; } string value = PostProcessResult(result.RtlResult, result.RtlTargetFileName, result.RtlSourceFileName); // Write output file if (result.RtlTargetFileName != null && (MinifyInPlace || !File.Exists(result.RtlTargetFileName) || value != await FileHelpers.ReadAllTextRetry(result.RtlTargetFileName))) { ProjectHelpers.CheckOutFileFromSourceControl(result.RtlTargetFileName); await FileHelpers.WriteAllTextRetry(result.RtlTargetFileName, value); ProjectHelpers.AddFileToProject(result.RtlSourceFileName, result.RtlTargetFileName); } // Write map file if (GenerateSourceMap && (!File.Exists(result.RtlMapFileName) || result.RtlResultMap != await FileHelpers.ReadAllTextRetry(result.RtlMapFileName))) { ProjectHelpers.CheckOutFileFromSourceControl(result.RtlMapFileName); await FileHelpers.WriteAllTextRetry(result.RtlMapFileName, result.RtlResultMap); ProjectHelpers.AddFileToProject(result.RtlTargetFileName, result.RtlMapFileName); } }
private async Task <dynamic> ValidateResult(Process process, string outputFile, string errorText) { string result = null; var isSuccess = false; IEnumerable <CompilerError> errors = null; try { if (process.ExitCode == 0) { if (!string.IsNullOrEmpty(outputFile) && File.Exists(outputFile)) { result = await FileHelpers.ReadAllTextRetry(outputFile); } isSuccess = true; } else { errors = ParseErrors(errorText); } } catch (FileNotFoundException missingFileException) { Logger.Log(ServiceName + ": " + Path.GetFileName(outputFile) + " compilation failed. " + missingFileException.Message); } return(new { Result = result, IsSuccess = isSuccess, Errors = errors }); }
// Don't try-catch this method: We need to "address" all the bugs, // which may occur as the (node.js-based) service implement changes. private async Task <CompilerResult> ProcessResult(CompilerResult result, bool onlyPreview, string sourceFileName, string targetFileName) { if (result == null) { Logger.Log(ServiceName + ": " + Path.GetFileName(sourceFileName) + " compilation failed: The service failed to respond to this request\n\t\t\tPossible cause: Syntax Error!"); return(await CompilerResultFactory.GenerateResult(sourceFileName, targetFileName)); } if (!result.IsSuccess) { var firstError = result.Errors.Where(e => e != null).Select(e => e.Message).FirstOrDefault(); if (firstError != null) { Logger.Log(firstError); } return(result); } string resultString = PostProcessResult(result.Result, result.TargetFileName, result.SourceFileName); if (!onlyPreview) { // Write output file if (result.TargetFileName != null && (MinifyInPlace || !File.Exists(result.TargetFileName) || resultString != await FileHelpers.ReadAllTextRetry(result.TargetFileName))) { ProjectHelpers.CheckOutFileFromSourceControl(result.TargetFileName); await FileHelpers.WriteAllTextRetry(result.TargetFileName, resultString); if (!(this is ILintCompiler)) { ProjectHelpers.AddFileToProject(result.SourceFileName, result.TargetFileName); } } // Write map file if (GenerateSourceMap && (!File.Exists(result.MapFileName) || result.ResultMap != await FileHelpers.ReadAllTextRetry(result.MapFileName))) { ProjectHelpers.CheckOutFileFromSourceControl(result.MapFileName); await FileHelpers.WriteAllTextRetry(result.MapFileName, result.ResultMap); if (!(this is ILintCompiler)) { ProjectHelpers.AddFileToProject(result.TargetFileName, result.MapFileName); } } await RtlVariantHandler(result); } return(CompilerResult.UpdateResult(result, resultString)); }
private async static Task <string> CombineFiles(Dictionary <string, string> files, string extension, BundleDocument bundle, string bundleFile) { StringBuilder sb = new StringBuilder(); foreach (string file in files.Keys) { string actualFile = file; if (extension.Equals(".js", StringComparison.OrdinalIgnoreCase) && WESettings.Instance.JavaScript.GenerateSourceMaps) { sb.AppendLine("///#source 1 1 " + files[file]); } var source = await FileHelpers.ReadAllTextRetry(actualFile); if (extension.Equals(".css", StringComparison.OrdinalIgnoreCase)) { // If the bundle is in the same folder as the CSS, // or if does not have URLs, no need to normalize. if (Path.GetDirectoryName(actualFile) != Path.GetDirectoryName(bundleFile) && source.IndexOf("url(", StringComparison.OrdinalIgnoreCase) > 0 && bundle.AdjustRelativePaths) { source = CssUrlNormalizer.NormalizeUrls( tree: new CssParser().Parse(source, true), targetFile: bundleFile, oldBasePath: actualFile ); } } else if (Path.GetExtension(file).Equals(".ts", StringComparison.OrdinalIgnoreCase)) { // If it is a Type Script include, we might want to alter the define(... at the start of the script to specify the resource location. if (source.StartsWith("define([\"require\", \"exports\"")) { string moduleName = files[file]; moduleName = Path.Combine(Path.GetDirectoryName(moduleName), Path.GetFileNameWithoutExtension(moduleName)).Replace('\\', '/'); if (moduleName.ToLower().StartsWith(WESettings.Instance.JavaScript.RootDirectory.ToLower())) { moduleName = moduleName.Substring(WESettings.Instance.JavaScript.RootDirectory.Length); } source = source.Replace("define([\"require\", \"exports\"", "define(\"" + moduleName + "\", [\"require\", \"exports\""); } } sb.AppendLine(source); } return(sb.ToString()); }
private async Task <IEnumerable <CssSourceMapNode> > ProcessSourceMaps() { ParseItem item = null; Selector selector = null; StyleSheet styleSheet = null; int start; var fileContents = ""; var result = new List <CssSourceMapNode>(); var contentCollection = new HashSet <string>(); foreach (var node in MapNodes) { // Cache source file contents. if (!contentCollection.Contains(node.SourceFilePath)) { if (!File.Exists(node.SourceFilePath)) // Lets say someone deleted the reference file. { continue; } fileContents = await FileHelpers.ReadAllTextRetry(node.SourceFilePath); contentCollection.Add(node.SourceFilePath); styleSheet = _parser.Parse(fileContents, false); } start = fileContents.NthIndexOfCharInString('\n', node.OriginalLine); start += node.OriginalColumn; item = styleSheet.ItemAfterPosition(start); if (item == null) { continue; } selector = item.FindType <Selector>(); if (selector == null) { continue; } node.OriginalSelector = selector; result.Add(node); } return(result); }
private async static Task <XmlDocument> GetXmlDocument(string filePath) { try { XmlDocument xmlDocument = new XmlDocument(); xmlDocument.LoadXml((await FileHelpers.ReadAllTextRetry(filePath)).Trim()); return(xmlDocument); } catch (Exception) { Logger.Log(Path.GetFileName(filePath) + " is not a valid Web Essentials bundle file. Ignoring file."); return(null); } }
public async static Task <CssCompilerResult> GenerateResult(string sourceFileName, string targetFileName, string mapFileName, bool isSuccess, string result, IEnumerable <CompilerError> errors) { CssCompilerResult compilerResult = new CssCompilerResult(sourceFileName, targetFileName, isSuccess, result, errors); if (mapFileName == null) { return(null); } var extension = Path.GetExtension(sourceFileName).TrimStart('.'); compilerResult.SourceMap = CssSourceMap.Create(await FileHelpers.ReadAllTextRetry(targetFileName), await FileHelpers.ReadAllTextRetry(mapFileName), Path.GetDirectoryName(targetFileName), Mef.GetContentType(extension)); return(compilerResult); }
///<summary>Gets the value of the "main" entry in a directory's package.json, or null if it doesn't exist or is invalid.</summary> private async static Task <string> GetPackageMain(string directory) { var packageFile = Path.Combine(directory, "package.json"); if (!File.Exists(packageFile)) { return(null); } try { var json = JObject.Parse(await FileHelpers.ReadAllTextRetry(packageFile).ConfigureAwait(false)); return(json.Value <string>("main")); } catch (Exception ex) { Logger.Log("An error occurred while reading " + packageFile + ": " + ex.Message); return(null); } }
public async static Task <CompilerResult> GenerateResult(string sourceFileName, string targetFileName, string mapFileName, bool isSuccess, string result, string resultMap, IEnumerable <CompilerError> errors, bool hasResult = false, string rtlSourceFileName = "", string rtlTargetFileName = "", string rtlMapFileName = "", string rtlResult = "", string rtlResultMap = "") { CompilerResult instance; mapFileName = mapFileName ?? targetFileName + ".map"; if (result == null && File.Exists(targetFileName)) { result = await FileHelpers.ReadAllTextRetry(targetFileName); } if (targetFileName != null && Path.GetExtension(targetFileName).Equals(".css", StringComparison.OrdinalIgnoreCase)) { instance = CssCompilerResult.GenerateResult(sourceFileName, targetFileName, mapFileName, isSuccess, result, resultMap, errors, rtlSourceFileName, rtlTargetFileName, rtlMapFileName, rtlResult, rtlResultMap, hasResult); } else { instance = CompilerResult.GenerateResult(sourceFileName, targetFileName, mapFileName, isSuccess, result, resultMap, errors, hasResult); } return(instance); }
private async Task HandleRtlCss(CompilerResult result) { string value = result.RtlResult; // Write output file if (result.RtlTargetFileName != null && (MinifyInPlace || !File.Exists(result.RtlTargetFileName) || value != await FileHelpers.ReadAllTextRetry(result.RtlTargetFileName))) { ProjectHelpers.CheckOutFileFromSourceControl(result.RtlTargetFileName); await FileHelpers.WriteAllTextRetry(result.RtlTargetFileName, value); ProjectHelpers.AddFileToProject(result.RtlSourceFileName, result.RtlTargetFileName); } // Write map file if (GenerateSourceMap && (!File.Exists(result.RtlMapFileName) || result.RtlResultMap != await FileHelpers.ReadAllTextRetry(result.RtlMapFileName))) { ProjectHelpers.CheckOutFileFromSourceControl(result.RtlMapFileName); await FileHelpers.WriteAllTextRetry(result.RtlMapFileName, result.RtlResultMap); ProjectHelpers.AddFileToProject(result.RtlTargetFileName, result.RtlMapFileName); } }
public async Task <CompilerResult> CompileAsync(string sourceFileName, string targetFileName) { if (RequireMatchingFileName && Path.GetFileName(targetFileName) != Path.GetFileNameWithoutExtension(sourceFileName) + TargetExtension && Path.GetFileName(targetFileName) != Path.GetFileNameWithoutExtension(sourceFileName) + ".min" + TargetExtension) { throw new ArgumentException(ServiceName + " cannot compile to a targetFileName with a different name. Only the containing directory can be different.", "targetFileName"); } var mapFileName = GetMapFileName(sourceFileName, targetFileName); var scriptArgs = GetArguments(sourceFileName, targetFileName, mapFileName); var errorOutputFile = Path.GetTempFileName(); var cmdArgs = string.Format("\"{0}\" \"{1}\"", NodePath, CompilerPath); cmdArgs = string.Format("/c \"{0} {1} > \"{2}\" 2>&1\"", cmdArgs, scriptArgs, errorOutputFile); ProcessStartInfo start = new ProcessStartInfo("cmd") { WindowStyle = ProcessWindowStyle.Hidden, WorkingDirectory = Path.GetDirectoryName(sourceFileName), Arguments = cmdArgs, UseShellExecute = false, CreateNoWindow = true }; try { ProjectHelpers.CheckOutFileFromSourceControl(targetFileName); mapFileName = mapFileName ?? targetFileName + ".map"; if (GenerateSourceMap) { ProjectHelpers.CheckOutFileFromSourceControl(mapFileName); } using (var process = await start.ExecuteAsync()) { if (targetFileName != null) { await MoveOutputContentToCorrectTarget(targetFileName); } return(await ProcessResult( process, (await FileHelpers.ReadAllTextRetry(errorOutputFile)).Trim(), sourceFileName, targetFileName, mapFileName )); } } finally { File.Delete(errorOutputFile); if (!GenerateSourceMap) { File.Delete(mapFileName); } } }
public static async Task <BundleDocument> FromFile(string fileName) { var extension = Path.GetExtension(fileName).TrimStart('.').ToLowerInvariant(); string root = ProjectHelpers.GetProjectFolder(fileName); string folder = Path.GetDirectoryName(root); if (folder == null || root == null) { return(null); } XDocument doc = null; string contents = await FileHelpers.ReadAllTextRetry(fileName); try { doc = XDocument.Parse(contents); } catch (XmlException) { return(null); } // Migrate old bundles doc = await MigrateBundle(doc, fileName, root, folder); if (doc == null) { return(null); } XElement element = null; IEnumerable <string> constituentFiles = from f in doc.Descendants("file") select ProjectHelpers.ToAbsoluteFilePath(f.Value, root, folder); BundleDocument bundle = new BundleDocument(fileName, constituentFiles.ToArray()); element = doc.Descendants("minify").FirstOrDefault(); if (element != null) { bundle.Minified = element.Value.Equals("true", StringComparison.OrdinalIgnoreCase); } element = doc.Descendants("runOnBuild").FirstOrDefault(); if (element != null) { bundle.RunOnBuild = element.Value.Equals("true", StringComparison.OrdinalIgnoreCase); } if (extension == "css") { element = doc.Descendants("adjustRelativePaths").FirstOrDefault(); if (element != null) { bundle.AdjustRelativePaths = element.Value.Equals("true", StringComparison.OrdinalIgnoreCase); } } element = doc.Descendants("outputDirectory").FirstOrDefault(); if (element != null) { bundle.OutputDirectory = element.Value; } return(bundle); }
public static async Task <BundleDocument> FromFile(string fileName) { string root = ProjectHelpers.GetProjectFolder(fileName); string folder = Path.GetDirectoryName(root); if (folder == null || root == null) { return(null); } XDocument doc = null; string contents = await FileHelpers.ReadAllTextRetry(fileName); try { doc = XDocument.Parse(contents); } catch (XmlException) { return(null); } // Migrate old bundles doc = await MigrateBundle(doc, fileName, root, folder); if (doc == null) { return(null); } XElement element = null; IEnumerable <string> rawConstituents = doc.Descendants("file").Select(s => s.Value); IEnumerable <string> constituentFiles = ProjectHelpers.GetBundleConstituentFiles(rawConstituents, root, folder, fileName); if (constituentFiles.Count() > 0) { BundleDocument bundle = new BundleDocument(fileName, constituentFiles.ToArray()) { OriginalBundleAssets = new List <string>(rawConstituents) }; element = doc.Descendants("minify").FirstOrDefault(); if (element != null) { bundle.Minified = element.Value.Equals("true", StringComparison.OrdinalIgnoreCase); } element = doc.Descendants("runOnBuild").FirstOrDefault(); if (element != null) { bundle.RunOnBuild = element.Value.Equals("true", StringComparison.OrdinalIgnoreCase); } if (bundle.isCss) { element = doc.Descendants("adjustRelativePaths").FirstOrDefault(); if (element != null) { bundle.AdjustRelativePaths = element.Value.Equals("true", StringComparison.OrdinalIgnoreCase); } } element = doc.Descendants("outputDirectory").FirstOrDefault(); if (element != null) { bundle.OutputDirectory = element.Value; } return(bundle); } else { return(null); } }
private async static Threading.Task WriteBundleFile(string bundleFilePath, XmlDocument doc) { XmlNode bundleNode = doc.SelectSingleNode("//bundle"); if (bundleNode == null) { return; } XmlNode outputAttr = bundleNode.Attributes["output"]; if (outputAttr != null && (outputAttr.InnerText.Contains("/") || outputAttr.InnerText.Contains("\\"))) { Logger.ShowMessage(String.Format(CultureInfo.CurrentCulture, "The 'output' attribute should contain a file name without a path; '{0}' is not valid", outputAttr.InnerText)); return; } Dictionary <string, string> files = new Dictionary <string, string>(); // filePath must end in ".targetExtension.bundle" string extension = Path.GetExtension(Path.GetFileNameWithoutExtension(bundleFilePath)); if (string.IsNullOrEmpty(extension)) { Logger.Log("Skipping bundle file " + bundleFilePath + " without extension. Bundle files must end with the output extension, followed by '.bundle'."); return; } XmlNodeList nodes = doc.SelectNodes("//file"); foreach (XmlNode node in nodes) { string absolute; if (node.InnerText.Contains(":\\")) { absolute = node.InnerText; } else { absolute = ProjectHelpers.ToAbsoluteFilePath(node.InnerText, bundleFilePath); } if (File.Exists(absolute)) { if (!files.ContainsKey(absolute)) { files.Add(absolute, node.InnerText); } } else { _dte.ItemOperations.OpenFile(bundleFilePath); Logger.ShowMessage(String.Format(CultureInfo.CurrentCulture, "Bundle error: The file '{0}' doesn't exist", node.InnerText)); return; } } string bundleSourcePath = outputAttr != null?Path.Combine(Path.GetDirectoryName(bundleFilePath), outputAttr.InnerText) : bundleFilePath.Replace(_ext, string.Empty); StringBuilder sb = new StringBuilder(); foreach (string file in files.Keys) { //if (extension.Equals(".css", StringComparison.OrdinalIgnoreCase)) //{ // sb.AppendLine("/*#source " + files[file] + " */"); //} if (extension.Equals(".js", StringComparison.OrdinalIgnoreCase) && WESettings.Instance.JavaScript.GenerateSourceMaps) { sb.AppendLine("///#source 1 1 " + files[file]); } if (!File.Exists(file)) { continue; } await new BundleFileWatcher().AttachFileObserverEvent(file); var source = await FileHelpers.ReadAllTextRetry(file); if (extension.Equals(".css", StringComparison.OrdinalIgnoreCase)) { // If the bundle is in the same folder as the CSS, // or if does not have URLs, no need to normalize. if (Path.GetDirectoryName(file) != Path.GetDirectoryName(bundleSourcePath) && source.IndexOf("url(", StringComparison.OrdinalIgnoreCase) > 0 && WESettings.Instance.Css.AdjustRelativePaths) { source = CssUrlNormalizer.NormalizeUrls( tree: new CssParser().Parse(source, true), targetFile: bundleSourcePath, oldBasePath: file ); } } sb.AppendLine(source); } bool bundleChanged = !File.Exists(bundleSourcePath) || await FileHelpers.ReadAllTextRetry(bundleSourcePath) != sb.ToString(); if (bundleChanged) { ProjectHelpers.CheckOutFileFromSourceControl(bundleSourcePath); await FileHelpers.WriteAllTextRetry(bundleSourcePath, sb.ToString()); Logger.Log("Web Essentials: Updated bundle: " + Path.GetFileName(bundleSourcePath)); } ProjectHelpers.AddFileToProject(bundleFilePath, bundleSourcePath); if (bundleNode.Attributes["minify"] != null && bundleNode.Attributes["minify"].InnerText == "true") { await WriteMinFile(bundleSourcePath, extension, bundleChanged); } }
private async static Threading.Task MakeBundleAsync(string extension) { var items = GetSelectedItems(extension); if (items.Count() == 0) { return; } StringBuilder sb = new StringBuilder(); foreach (ProjectItem item in items) { if (!File.Exists(item.FileNames[1])) { continue; } string content = await FileHelpers.ReadAllTextRetry(item.FileNames[1]); sb.AppendLine(content); } ProjectItem firstItem = items.SkipWhile(x => x.FileNames[1] != null).FirstOrDefault(); if (firstItem == null || string.IsNullOrEmpty(firstItem.FileNames[1])) { return; } string dir = Path.GetDirectoryName(firstItem.FileNames[1]); if (!Directory.Exists(dir)) { return; } string bundleFile = Microsoft.VisualBasic.Interaction.InputBox("Specify the name of the bundle", "Web Essentials", "bundle1"); if (string.IsNullOrEmpty(bundleFile)) { return; } if (!bundleFile.EndsWith(extension + _ext, StringComparison.OrdinalIgnoreCase)) { bundleFile += extension + _ext; } string bundlePath = Path.Combine(dir, bundleFile); if (File.Exists(bundlePath)) { Logger.ShowMessage("The bundle file already exists."); return; } await Dispatcher.CurrentDispatcher.BeginInvoke(new Action(async() => await WriteBundleRecipe(bundlePath, items, Path.ChangeExtension(bundleFile, null))), // Remove the final ".bundle" extension. DispatcherPriority.ApplicationIdle, null); }
// Overridden to work around SASS bug // TODO: Remove when https://github.com/hcatlin/libsass/issues/242 is fixed protected async virtual Task <string> ReadMapFile(string sourceMapFileName) { return(await FileHelpers.ReadAllTextRetry(sourceMapFileName)); }
// A very ugly hack for a very ugly bug: https://github.com/hcatlin/libsass/issues/324 // Remove this and its caller in previous method, when it is fixed in original repo // and https://github.com/andrew/node-sass/ is released with the fix. // Overwriting all positions belonging to original/source file. private async Task <IEnumerable <CssSourceMapNode> > CorrectionsForScss(string cssFileContents) { // Sort collection for generated file. var sortedForGenerated = MapNodes.OrderBy(x => x.GeneratedLine) .ThenBy(x => x.GeneratedColumn) .ToList(); ParseItem item = null; Selector selector = null; SimpleSelector simple = null; StyleSheet styleSheet = null, cssStyleSheet = null; int start = 0, indexInCollection, targetDepth; string fileContents = null, simpleText = ""; var result = new List <CssSourceMapNode>(); var contentCollection = new HashSet <string>(); // So we don't have to read file for each map item. var parser = new CssParser(); cssStyleSheet = parser.Parse(cssFileContents, false); foreach (var node in MapNodes) { // Cache source file contents. if (!contentCollection.Contains(node.SourceFilePath)) { if (!File.Exists(node.SourceFilePath)) // Lets say someone deleted the reference file. { continue; } fileContents = await FileHelpers.ReadAllTextRetry(node.SourceFilePath); contentCollection.Add(node.SourceFilePath); styleSheet = _parser.Parse(fileContents, false); } start = cssFileContents.NthIndexOfCharInString('\n', node.GeneratedLine); start += node.GeneratedColumn; item = cssStyleSheet.ItemAfterPosition(start); if (item == null) { continue; } selector = item.FindType <Selector>(); simple = item.FindType <SimpleSelector>(); if (selector == null || simple == null) { continue; } simpleText = simple.Text; indexInCollection = sortedForGenerated.FindIndex(e => e.Equals(node));//sortedForGenerated.IndexOf(node); targetDepth = 0; for (int i = indexInCollection; i >= 0 && node.GeneratedLine == sortedForGenerated[i].GeneratedLine; targetDepth++, --i) { ; } start = fileContents.NthIndexOfCharInString('\n', node.OriginalLine); start += node.OriginalColumn; item = styleSheet.ItemAfterPosition(start); while (item.TreeDepth > targetDepth) { item = item.Parent; } // selector = item.FindType<RuleSet>().Selectors.First(); RuleSet rule; ScssRuleBlock scssRuleBlock = item as ScssRuleBlock; rule = scssRuleBlock == null ? item as RuleSet : scssRuleBlock.RuleSets.FirstOrDefault(); if (rule == null) { continue; } // Because even on the same TreeDepth, there may be mulitple ruleblocks // and the selector names may include & or other symbols which are diff // fromt he generated counterpart, here is the guess work item = rule.Children.FirstOrDefault(r => r is Selector && r.Text.Trim() == simpleText) as Selector; selector = item == null ? null : item as Selector; if (selector == null) { // One more try: look for the selector in neighboring rule blocks then skip. selector = rule.Children.Where(r => r is RuleBlock) .SelectMany(r => (r as RuleBlock).Children .Where(s => s is RuleSet) .Select(s => (s as RuleSet).Selectors.FirstOrDefault(sel => sel.Text.Trim() == simpleText))) .FirstOrDefault(); if (selector == null) { continue; } } node.OriginalLine = fileContents.Substring(0, selector.Start).Count(s => s == '\n'); node.OriginalColumn = fileContents.GetLineColumn(selector.Start, node.OriginalLine); result.Add(node); } return(result); }