private static void ReadAllFilesWithContext(FileReadBuilder fileReadBuilder, string folderPath, string searchPattern, CodeType codeType, Encoding encoding) { // get all the files in the folder path that match the search pattern foreach (var filePath in Directory.GetFiles(folderPath, searchPattern)) { // read each one into the string builder with context ReadFileWithContext(fileReadBuilder, filePath, codeType, encoding); } }
private static void ReadFileWithContext(FileReadBuilder fileReadBuilder, string filePath, CodeType codeType, Encoding encoding) { if (fileReadBuilder.Length > 0) { // start a new line so any previous single-line comments are terminated. fileReadBuilder.AnnotateLine(); } // if the previous file didn't end in a semicolon, add one now. // it doesn't hurt to have an extra semicolon in JavaScript, and our CSS Parser has been // tweaked to ignore extraneous semicolons as well. if (!fileReadBuilder.EndsInSemicolon && codeType != CodeType.StyleSheet) { fileReadBuilder.Annotate(';'); } // output a special comment that NUglify will pick up so any errors will // have the proper file context. The CSS parser has been tweaked to look for // this comment as well. if (codeType == CodeType.StyleSheet) { fileReadBuilder.Annotate("/*/#source 1 1 "); } else { fileReadBuilder.Annotate("///#source 1 1 "); } fileReadBuilder.Annotate(filePath); if (codeType == CodeType.StyleSheet) { fileReadBuilder.Annotate(" */"); } fileReadBuilder.AnnotateLine(); // now read all the file source and add it to the combined input. // it doesn't matter which encoder fallback we use -- we'll be DECODING, and we always use a simple replacement for that. // so just ask for a JS encoding here. using (var reader = new StreamReader(filePath, encoding)) { // read all the content, add it to the builder, and set the semicolon flag var fileContent = reader.ReadToEnd(); fileReadBuilder.AppendContent(fileContent); fileReadBuilder.EndsInSemicolon = s_endsInSemicolon.IsMatch(fileContent); } }
private static void ReadFile(InputFile inputFile, FileReadBuilder fileReadBuilder, CodeType codeType, string defaultEncodingName) { if (inputFile != null) { var encoding = inputFile.GetEncoding(defaultEncodingName); if (File.Exists(inputFile.Path)) { // file exists -- read it into the string builder with the appropriate context comments added ReadFileWithContext(fileReadBuilder, inputFile.Path, codeType, encoding); } else if (Directory.Exists(inputFile.Path)) { // right now, just look for .JS for JavaScript and .CSS for Stylesheets. // if we don't know what type, ask for everything! var searchPattern = codeType == CodeType.JavaScript ? "*.js" : codeType == CodeType.StyleSheet ? "*.css" : "*.*"; ReadAllFilesWithContext(fileReadBuilder, inputFile.Path, searchPattern, codeType, encoding); } } }
/// <summary> /// Walk the list of inputs for a given output group, and create a list of input groups /// consisting of the source and origin. Consecutive project inputs are concatenated together, /// and external inputs are kept separate. /// </summary> /// <param name="outputGroup">output group</param> /// <param name="defaultEncodingName">default encoding name to use if none specified</param> /// <param name="rawInputBuilder">output the raw source to this builder, no added comments</param> /// <param name="sourceLength">length of the raw source</param> /// <returns>list of input groups consisting of source code and origin</returns> public static IList <InputGroup> ReadInputGroups(this OutputGroup outputGroup, string defaultEncodingName) { var inputGroups = new List <InputGroup>(); if (outputGroup != null) { if (outputGroup.Inputs.Count == 0) { try { // try setting the input encoding - sometimes this fails! Console.InputEncoding = GetInputEncoding(defaultEncodingName); } catch (IOException e) { // error setting the encoding input; just use whatever the default is System.Diagnostics.Debug.WriteLine(e.ToString()); } var sourceComment = outputGroup.CodeType == CodeType.StyleSheet ? "/*/#SOURCE 1 1 stdin */\r\n" : "///#SOURCE 1 1 stdin\r\n"; // read from stdin, append to the builder, then create a single project input group // from the build code. var sourceCode = Console.In.ReadToEnd(); inputGroups.Add(new InputGroup { RawSource = sourceCode, Source = sourceComment + sourceCode, Origin = SourceOrigin.Project }); } else { // start off saying the previous item ends in a semicolon so we don't add one before the first file var fileReadBuilder = new FileReadBuilder { EndsInSemicolon = true }; for (var ndx = 0; ndx < outputGroup.Inputs.Count; ++ndx) { var inputFile = outputGroup.Inputs[ndx]; if (inputFile.Origin == SourceOrigin.External) { // we found an external input file. // if we were building up project input files, dump what we have into a new group and clear it out if (fileReadBuilder.Length > 0) { inputGroups.Add(new InputGroup { Source = fileReadBuilder.ToString(), RawSource = fileReadBuilder.ToRawString(), Origin = SourceOrigin.Project }); } // read the content of the external file, passing in false for whether the previous file // ended in a semicolon so that one will ALWAYS get prepended. Add a new input group for the // content by itself, and then always reset the semicolon flag to false so that the NEXT code // will be separated from the external code with another semicolon. fileReadBuilder.Clear(); ReadFile(inputFile, fileReadBuilder, outputGroup.CodeType, defaultEncodingName); inputGroups.Add(new InputGroup { Source = fileReadBuilder.ToString(), RawSource = fileReadBuilder.ToRawString(), Origin = SourceOrigin.External }); fileReadBuilder.Clear(); fileReadBuilder.EndsInSemicolon = false; } else { // read the project input file into the group builder, with context. ReadFile(inputFile, fileReadBuilder, outputGroup.CodeType, defaultEncodingName); } } // if there is anything left, add it as a new input group now if (fileReadBuilder.Length > 0) { inputGroups.Add(new InputGroup { Source = fileReadBuilder.ToString(), RawSource = fileReadBuilder.ToRawString(), Origin = SourceOrigin.Project }); } } } return(inputGroups); }