public override bool Execute() { if (Manifests != null && Manifests.Length > 0) { // create the project default settings // with a default warning level of maxvalue so everything is reported unless otherwise changed // with a -warn switch var projectDefaultSettings = new UglifyCommandParser { WarningLevel = int.MaxValue }; if (!ProjectDefaultSwitches.IsNullOrWhiteSpace()) { projectDefaultSettings.Parse(ProjectDefaultSwitches); } // each task item represents an ajaxmin manifest file: an XML file that // has settings and one or more output files, each comprised of one or more // input files. To execute this process, we will read the XML manifest and // execute NUglify for each output group. // won't bother executing NUglify is the file time for the output file // is greater than all its inputs. foreach (var taskItem in Manifests) { ProcessManifest(taskItem, projectDefaultSettings); } } // we succeeded if there have been no errors logged return(!Log.HasLoggedErrors); }
void CreateReport(GlobalScope globalScope, UglifyCommandParser uglifyCommandParser) { string reportText; using (var writer = new StringWriter(CultureInfo.InvariantCulture)) { using (IScopeReport scopeReport = CreateScopeReport(uglifyCommandParser)) { scopeReport.CreateReport(writer, globalScope, uglifyCommandParser.JSSettings.MinifyCode); } reportText = writer.ToString(); } if (!string.IsNullOrEmpty(reportText)) { if (string.IsNullOrEmpty(uglifyCommandParser.ReportPath)) { // no report path specified; send to console WriteProgress(reportText); WriteProgress(); } else { // report path specified -- write to the file. // don't append; use UTF-8 as the output format. // let any exceptions bubble up. using (var writer = new StreamWriter(uglifyCommandParser.ReportPath, false, new UTF8Encoding(false))) { writer.Write(reportText); } } } }
protected static UglifyCommandParser ParseConfigSettings(string arguments, UglifyCommandParser defaults) { // clone the default switch settings, parse the arguments on top of the clone, // and then return the clone. var switchParser = defaults.IfNotNull(d => d.Clone(), new UglifyCommandParser()); switchParser.Parse(arguments); return(switchParser); }
void ProcessManifest(ITaskItem taskItem, UglifyCommandParser projectDefaultSettings) { // save the manifest folder - paths within the manifest will be relative to it // if there are no InputFolder or OutputFolder values var manifestFolder = Path.GetDirectoryName(taskItem.ItemSpec); var manifestModifiedTime = File.GetLastWriteTimeUtc(taskItem.ItemSpec); // process the XML file into objects Manifest manifest = null; try { // read the manifest in manifest = ManifestUtilities.ReadManifestFile(taskItem.ItemSpec); // if an input folder was specified and it exists, use that as the root // for all input files. Otherwise use the manifest folder path. var inputFolder = (this.InputFolder.IsNullOrWhiteSpace() || !Directory.Exists(this.InputFolder)) ? manifestFolder : this.InputFolder; // validate and normalize all paths. manifest.ValidateAndNormalize(inputFolder, this.OutputFolder, this.ThrowInputMissingErrors); } catch (FileNotFoundException ex) { Log.LogError(ex.Message + ex.FileName.IfNotNull(s => " " + s).IfNullOrWhiteSpace(string.Empty)); } catch (XmlException ex) { Log.LogError(ex.Message); } if (manifest != null) { // create the default settings for this configuration, if there are any, based on // the project default settings. var defaultSettings = ParseConfigSettings(manifest.GetConfigArguments(this.Configuration), projectDefaultSettings); // for each output group foreach (var outputGroup in manifest.Outputs) { // get the file info for the output file. It should already be normalized. var outputFileInfo = new FileInfo(outputGroup.Path); // the symbol map is an OPTIONAL output, so if we don't want one, we ignore it. // but if we do, we need to check for its existence and filetimes, just like // the regular output file var symbolsFileInfo = outputGroup.SymbolMap.IfNotNull(sm => new FileInfo(sm.Path)); ProcessOutputGroup(outputGroup, outputFileInfo, symbolsFileInfo, defaultSettings, manifestModifiedTime); } } }
static IScopeReport CreateScopeReport(UglifyCommandParser uglifyCommandParser) { // check the switch parser for a report format. // At this time we only have two: XML or DEFAULT. If it's XML, use // the XML report; all other values use the default report. // No error checking at this time. if (string.CompareOrdinal(uglifyCommandParser.ReportFormat, "XML") == 0) { return(new XmlScopeReport()); } return(new DefaultScopeReport()); }
void ProcessStylesheet(IList <InputGroup> inputGroups, UglifyCommandParser uglifyCommandParser, string outputPath, Encoding encoding) { var outputBuilder = new StringBuilder(8192); foreach (var inputGroup in inputGroups) { // create and setup parser var parser = new CssParser(); parser.Settings = uglifyCommandParser.CssSettings; parser.JSSettings = uglifyCommandParser.JSSettings; parser.CssError += (sender, ea) => { // if the input group is not project, then only report sev-0 errors // regardless, don't show any errors that have a severity lower (greater numeric value) // than the warning level specified. if ((inputGroup.Origin == SourceOrigin.Project || ea.Error.Severity == 0) && ea.Error.Severity <= uglifyCommandParser.WarningLevel) { LogContextError(ea.Error); } }; // minify input outputBuilder.Append(parser.Parse(inputGroup.Source)); } // write output if (!Log.HasLoggedErrors) { if (!FileWriteOperation(outputPath, uglifyCommandParser.Clobber, () => { using (var writer = new StreamWriter(outputPath, false, encoding)) { writer.Write(outputBuilder.ToString()); } return(true); })) { // could not write file Log.LogError(Strings.CouldNotWriteOutputFile, outputPath); } } else { Log.LogWarning(Strings.DidNotMinify, outputPath, Strings.ThereWereErrors); } }
void GenerateOutputFiles(OutputGroup outputGroup, FileInfo outputFileInfo, UglifyCommandParser uglifyCommandParser) { // create combined input source var inputGroups = outputGroup.ReadInputGroups(uglifyCommandParser.EncodingInputName); if (inputGroups.Count > 0) { switch (outputGroup.CodeType) { case CodeType.JavaScript: // call the virtual function to generate the JavaScript output file from the inputs GenerateJavaScript(outputGroup, inputGroups, uglifyCommandParser, outputFileInfo.FullName, outputGroup.GetEncoding(uglifyCommandParser.EncodingOutputName)); break; case CodeType.StyleSheet: // call the virtual function to generate the stylesheet output file from the inputs GenerateStyleSheet(outputGroup, inputGroups, uglifyCommandParser, outputFileInfo.FullName, outputGroup.GetEncoding(uglifyCommandParser.EncodingOutputName)); break; case CodeType.Unknown: Log.LogError(Strings.UnknownCodeType); break; } } else { // no input! write an empty output file if (!FileWriteOperation(outputFileInfo.FullName, uglifyCommandParser.Clobber, () => { using (var stream = outputFileInfo.Create()) { // write nothing; just create the empty file return(true); } })) { // could not write file Log.LogError(Strings.CouldNotWriteOutputFile, outputFileInfo.FullName); } } }
/// <summary> /// Process an output group. Override this method if the task doesn't want to check the input file times against /// the output file times (or existence) and call the GenerateOutput methods. /// </summary> /// <param name="outputGroup">the OutputGroup being processed</param> /// <param name="outputFileInfo">FileInfo for the desired output file</param> /// <param name="symbolsFileInfo">FileInfo for the optional desired symbol file</param> /// <param name="defaultSettings">default settings for this output group</param> /// <param name="manifestModifiedTime">modified time for the manifest</param> protected virtual void ProcessOutputGroup(OutputGroup outputGroup, FileInfo outputFileInfo, FileInfo symbolsFileInfo, UglifyCommandParser defaultSettings, DateTime manifestModifiedTime) { // check the file times -- if any of the inputs are newer than any output (or if any outputs don't exist), // then generate the output files if (AnyInputsAreNewerThanOutputs(outputGroup, outputFileInfo, symbolsFileInfo, manifestModifiedTime)) { // get the settings to use -- take the configuration for this output group // and apply them over the default settings var settings = ParseConfigSettings(outputGroup.GetConfigArguments(this.Configuration), defaultSettings); GenerateOutputFiles(outputGroup, outputFileInfo, settings); } else { // none of the inputs are newer than the output -- we're good. Log.LogMessage(Strings.SkippedOutputFile, outputFileInfo.IfNotNull(fi => fi.Name) ?? string.Empty); } }
int ProcessJSFile(IList <InputGroup> inputGroups, UglifyCommandParser uglifyCommandParser, StringBuilder outputBuilder) { var returnCode = 0; var settings = uglifyCommandParser.JSSettings; var currentSourceOrigin = SourceOrigin.Project; // blank line before WriteProgress(); // create our parser object and hook up some events var parser = new JSParser(); parser.UndefinedReference += OnUndefinedReference; parser.CompilerError += (sender, ea) => { var error = ea.Error; if (currentSourceOrigin == SourceOrigin.Project || error.Severity == 0) { // ignore severity values greater than our severity level // also ignore errors that are in our ignore list (if any) if (error.Severity <= uglifyCommandParser.WarningLevel) { // we found an error m_errorsFound = true; // write the error out WriteError(error.ToString()); } } }; // output visitor requires a text writer, so make one from the string builder using (var writer = new StringWriter(outputBuilder, CultureInfo.InvariantCulture)) { var outputIndex = 0; var originalTermSetting = settings.TermSemicolons; for (var inputGroupIndex = 0; inputGroupIndex < inputGroups.Count; ++inputGroupIndex) { var inputGroup = inputGroups[inputGroupIndex]; currentSourceOrigin = inputGroup.Origin; // for all but the last item, we want the term-semicolons setting to be true. // but for the last entry, set it back to its original value settings.TermSemicolons = inputGroupIndex < inputGroups.Count - 1 ? true : originalTermSetting; // if this is preprocess-only or echo-input, then set up the writer as the echo writer for the parser if (settings.PreprocessOnly || m_echoInput) { parser.EchoWriter = writer; if (inputGroupIndex > 0) { // separate subsequent input groups with an appropriate line terminator writer.Write(settings.LineTerminator); writer.Write(';'); writer.Write(settings.LineTerminator); } } else { // not a preprocess-only or echo - make sure the echo writer is null parser.EchoWriter = null; } // parse the input code. Don't use a source context because it should already be // in the source using ///#SOURCE comments as we assembled the input groups. var scriptBlock = parser.Parse(inputGroup.Source, settings); if (m_errorsFound) { WriteProgress(); } if (m_outputTimer) { OutputTimingPoints(parser, inputGroupIndex, inputGroups.Count); } if (!settings.PreprocessOnly && !m_echoInput) { if (scriptBlock != null) { if (outputIndex++ > 0) { // separate subsequent input groups with an appropriate line terminator writer.Write(settings.LineTerminator); } // crunch the output and write it to debug stream, but make sure // the settings we use to output THIS chunk are correct if (settings.Format == JavaScriptFormat.JSON) { if (!JsonOutputVisitor.Apply(writer, scriptBlock, settings)) { returnCode = 1; } } else { OutputVisitor.Apply(writer, scriptBlock, settings); } } else { // no code? WriteProgress(NUglify.NoParsedCode); } } } // give the symbols map a chance to write something at the bottom of the source file // (and if this isn't preprocess-only or echo) if (settings.SymbolsMap != null && !settings.PreprocessOnly && !m_echoInput) { settings.SymbolsMap.EndFile(writer, settings.LineTerminator); } } if (uglifyCommandParser.AnalyzeMode) { // blank line before WriteProgress(); // output our report CreateReport(parser.GlobalScope, uglifyCommandParser); } return(returnCode); }
public void RunErrorTest(string settingsSwitches, params JSError[] expectedErrorArray) { // open the stack trace for this call StackTrace stackTrace = new StackTrace(); string testClass = null; string testName = null; // save the name of the current method (RunTest) string currentMethodName = MethodInfo.GetCurrentMethod().Name; // loop from the previous frame up until we get a method name that is not the // same as the current method name for (int ndx = 1; ndx < stackTrace.FrameCount; ++ndx) { // get the frame StackFrame stackFrame = stackTrace.GetFrame(ndx); // we have different entry points with the same name -- we're interested // in the first one that ISN'T the same name as our method MethodBase methodBase = stackFrame.GetMethod(); if (methodBase.Name != currentMethodName) { // the calling method's name is the test name - we use this as-is for the output file name // and we use any portion before an underscore as the input file testName = methodBase.Name; // get the method's class - we use this as the subfolder under input/output/expected testClass = methodBase.DeclaringType.Name; break; } } // we definitely should be able to find a function on the stack frame that // has a different name than this function, but just in case... Debug.Assert(testName != null && testClass != null, "Couldn't locate calling stack frame"); // the input file is the portion of the test name before the underscore (if any) string inputFile = testName.Split('_')[0]; // get the input and output paths string inputPath = GetJsPath( InputFolder, testClass, inputFile, false); Assert.IsTrue(File.Exists(inputPath), "Input File does not exist: {0}", inputPath); var outputPath = GetJsPath( m_outputFolder, testClass, testName, false); if (File.Exists(outputPath)) { // if it exists already, delete it File.Delete(outputPath); } else { // otherwise make sure the directory exists Directory.CreateDirectory(Path.GetDirectoryName(outputPath)); } /*int expectedErrorCode = (int)(0x800A0000 + (int)expectedError); * Trace.WriteLine(string.Empty); * Trace.WriteLine(string.Format("Expecting error 0x{0:X}", expectedErrorCode));*/ // if we were passed a string containing command-line settings... var switchParser = new UglifyCommandParser(); if (!string.IsNullOrEmpty(settingsSwitches)) { // parse the string now switchParser.Parse(settingsSwitches); } // read the input JS string jsSource; using (var reader = new StreamReader(inputPath, GetJSEncoding(switchParser.EncodingInputName))) { jsSource = reader.ReadToEnd(); } Trace.Write("INPUT FILE: "); Trace.WriteLine(inputPath); Trace.WriteLine(jsSource); var testPassed = true; var expectedErrorList = new List <JSError>(expectedErrorArray); var errorList = new List <UglifyError>(); var parser = new JSParser(); parser.CompilerError += (source, e) => { errorList.Add(e.Error); }; var sb = new StringBuilder(); using (var writer = new StringWriter(sb)) { if (switchParser.JSSettings.PreprocessOnly) { parser.EchoWriter = writer; } // normal -- just run it through the parser var block = parser.Parse(new DocumentContext(jsSource) { FileContext = inputPath }, switchParser.JSSettings); if (!switchParser.JSSettings.PreprocessOnly) { // look at the settings for the proper output visitor if (switchParser.JSSettings.Format == JavaScriptFormat.JSON) { { if (!JsonOutputVisitor.Apply(writer, block, switchParser.JSSettings)) { Trace.WriteLine("JSON OUTPUT ERRORS!"); } } } else { OutputVisitor.Apply(writer, block, switchParser.JSSettings); } } } var crunchedCode = sb.ToString(); // output the crunched code using the proper output encoding using (var outputStream = new StreamWriter(outputPath, false, GetJSEncoding(switchParser.EncodingOutputName))) { outputStream.Write(crunchedCode); } Trace.WriteLine(string.Empty); Trace.WriteLine("---ERRORS---"); foreach (var err in errorList) { Trace.WriteLine(((JSError)err.ErrorNumber).ToString()); } Trace.WriteLine(string.Empty); Trace.Indent(); foreach (var err in errorList) { // log the error Trace.WriteLine(string.Empty); Trace.WriteLine(string.Format("Error {0} at Line {1}, Column {2}: {3}", err.ErrorCode, err.StartLine, err.StartColumn, err.Message)); Trace.Indent(); Trace.WriteLine(err.Message); int index = expectedErrorList.IndexOf((JSError)err.ErrorNumber); if (index >= 0) { // expected error -- remove it from the list so we can tell what we're missing later expectedErrorList.RemoveAt(index); } else { // unexpected error testPassed = false; Trace.WriteLine("UNEXPECTED"); } Trace.Unindent(); } Trace.Unindent(); // the list should be empty now -- if it isn't, then there was an expected error that didn't happen if (expectedErrorList.Count > 0) { testPassed = false; Trace.WriteLine(string.Empty); Trace.WriteLine("---MISSING ERRORS---"); Trace.Indent(); foreach (JSError jsError in expectedErrorList) { Trace.WriteLine(jsError.ToString()); } Trace.Unindent(); } if (!testPassed) { Trace.WriteLine(""); Trace.WriteLine("UNEXPECTED ERROR RESULTS"); } // compute the path to the expected file string expectedPath = GetJsPath( m_expectedFolder, testClass, testName, false); Trace.WriteLine(string.Empty); Trace.WriteLine("odd \"" + expectedPath + "\" \"" + outputPath + "\""); Trace.WriteLine(string.Empty); Trace.WriteLine("---Expected Code---"); TraceFileContents(expectedPath); Trace.WriteLine(string.Empty); Trace.WriteLine("---Resulting Code---"); TraceFileContents(outputPath); AssertCompareTextFiles(outputPath, expectedPath); Assert.IsTrue(testPassed, "Test failed"); }
protected override void GenerateJavaScript(OutputGroup outputGroup, IList <InputGroup> inputGroups, UglifyCommandParser uglifyCommandParser, string outputPath, Encoding outputEncoding) { if (uglifyCommandParser == null) { throw new ArgumentNullException("uglifyCommandParser"); } try { var settings = uglifyCommandParser.JSSettings; // process the resources for this output group into the settings list // if there are any to be processed if (outputGroup != null && settings != null && outputGroup.Resources.IfNotNull(rs => rs.Count > 0)) { outputGroup.ProcessResourceStrings(settings.ResourceStrings, null); } // then process the javascript output group ProcessJavaScript( inputGroups, uglifyCommandParser, outputPath, outputGroup.IfNotNull(og => og.SymbolMap), outputEncoding); } catch (ArgumentException ex) { // processing the resource strings could throw this exception Log.LogError(ex.Message); } }
public void ToArguments() { var testData = new ArgumentsData[] { new ArgumentsData() { CommandLine = null, Arguments = new string[0] }, new ArgumentsData() { CommandLine = "", Arguments = new string[0] }, new ArgumentsData() { CommandLine = " ", Arguments = new string[0] }, new ArgumentsData() { CommandLine = "-ei:utf-8 -eo:utf-8 -warn:4 /G:jQuery -p -z", Arguments = new string[] { "-ei:utf-8", "-eo:utf-8", "-warn:4", "/G:jQuery", "-p", "-z" } }, new ArgumentsData() { CommandLine = "\"foo bar.js\" -out \"c:\\test folder\\foo bar min.js\" ", Arguments = new string[] { "foo bar.js", "-out", "c:\\test folder\\foo bar min.js" } }, new ArgumentsData() { CommandLine = "foo\"bar\"ack", Arguments = new string[] { "foobarack" } }, new ArgumentsData() { CommandLine = "foo \"\"\"\" bar", Arguments = new string[] { "foo", "\"", "bar" } }, new ArgumentsData() { CommandLine = "now \" is the time \" for", Arguments = new string[] { "now", " is the time ", "for" } }, new ArgumentsData() { CommandLine = "now \" is \"\"the\"\" time \" for", Arguments = new string[] { "now", " is \"the\" time ", "for" } }, new ArgumentsData() { CommandLine = "now \"\" \" is \"\"the\"\" time \" for", Arguments = new string[] { "now", "", " is \"the\" time ", "for" } }, }; var ndxTest = 0; foreach (var test in testData) { Trace.Write(string.Format("Parsing test {0}, command line: ", ++ndxTest)); Trace.WriteLine(test.CommandLine ?? "<null pointer>"); var argsActual = UglifyCommandParser.ToArguments(test.CommandLine); var argsExpected = test.Arguments; // assume succesful unless proven otherwise var success = true; Assert.IsTrue(argsActual.Length == argsExpected.Length, "Parsed arguments length {0} not equal to expected arguments length {1}", argsActual.Length, argsExpected.Length); Trace.WriteLine(string.Format(" {0} arguments", argsActual.Length)); for (var ndxArg = 0; ndxArg < argsActual.Length; ++ndxArg) { var theSame = string.CompareOrdinal(argsActual[ndxArg], argsExpected[ndxArg]) == 0; Trace.WriteLine(string.Format(" {0}: {1} {3} {2}", ndxArg + 1, argsActual[ndxArg], argsExpected[ndxArg], theSame ? "==" : "!=")); success = theSame ? success : false; } Assert.IsTrue(success, "TEST {0} FAILED!", ndxTest); } }
protected abstract void GenerateStyleSheet(OutputGroup outputGroup, IList <InputGroup> inputGroups, UglifyCommandParser uglifyCommandParser, string outputPath, Encoding outputEncoding);
protected override void GenerateStyleSheet(OutputGroup outputGroup, IList <InputGroup> inputGroups, UglifyCommandParser uglifyCommandParser, string outputPath, Encoding outputEncoding) { // shouldn't get called because we override the ProcessOutputGroup method throw new NotImplementedException(); }
protected override void GenerateStyleSheet(OutputGroup outputGroup, IList <InputGroup> inputGroups, UglifyCommandParser uglifyCommandParser, string outputPath, Encoding outputEncoding) { if (uglifyCommandParser == null) { throw new ArgumentNullException("uglifyCommandParser"); } ProcessStylesheet( inputGroups, uglifyCommandParser, outputPath, outputEncoding); }
protected override void GenerateStyleSheet(OutputGroup outputGroup, IList <InputGroup> inputGroups, UglifyCommandParser uglifyCommandParser, string outputPath, Encoding outputEncoding) { if (!FileWriteOperation(outputPath, uglifyCommandParser.IfNotNull(p => p.Clobber), () => { // create the output file, clobbering any existing content using (var writer = new StreamWriter(outputPath, false, outputEncoding)) { if (inputGroups != null && inputGroups.Count > 0) { // for each input file, copy to the output, separating them with a newline (don't need a semicolon like JavaScript does) var addSeparator = false; foreach (var inputGroup in inputGroups) { if (addSeparator) { writer.WriteLine(); } else { addSeparator = true; } writer.Write(inputGroup.Source); } } } return(true); })) { // could not write file Log.LogError(Strings.CouldNotWriteOutputFile, outputPath); } }
private int ProcessCssFile(IList <InputGroup> inputGroups, UglifyCommandParser uglifyCommandParser, StringBuilder outputBuilder) { var retVal = 0; // blank line before WriteProgress(); // we can share the same parser object var parser = new CssParser(); parser.Settings = uglifyCommandParser.CssSettings; parser.JSSettings = uglifyCommandParser.JSSettings; using (var writer = new StringWriter(outputBuilder, CultureInfo.InvariantCulture)) { // if we are echoing the input, then set the settings echo writer to the output stream // otherwise make sure it's null if (this.m_echoInput) { parser.EchoWriter = writer; } else { parser.EchoWriter = null; } var ndx = 0; foreach (var inputGroup in inputGroups) { // process input source... parser.CssError += (sender, ea) => { var error = ea.Error; if (inputGroup.Origin == SourceOrigin.Project || error.Severity == 0) { // ignore severity values greater than our severity level if (error.Severity <= uglifyCommandParser.WarningLevel) { // we found an error m_errorsFound = true; WriteError(error.ToString()); } } }; if (m_echoInput && ndx > 0) { writer.Write(uglifyCommandParser.CssSettings.LineTerminator); } // if we want to time this, start a stopwatch now Stopwatch stopwatch = null; if (m_outputTimer) { stopwatch = new Stopwatch(); stopwatch.Start(); } // crunch the source and output to the string builder we were passed var crunchedStyles = parser.Parse(inputGroup.Source); if (stopwatch != null) { var ticks = stopwatch.ElapsedTicks; stopwatch.Stop(); // frequency is ticks per second, so if we divide by 1000.0, then we will have a // double-precision value indicating the ticks per millisecond. Divide this into the // number of ticks we measure, and we'll get the milliseconds in double-precision. var frequency = Stopwatch.Frequency / 1000.0; var timerMessage = string.Format(CultureInfo.CurrentCulture, NUglify.TimerFormat, 0, ticks / frequency); Debug.WriteLine(timerMessage); Debug.WriteLine(string.Empty); WriteProgress(timerMessage); WriteProgress(); } // if there is output, send it where it needs to go if (!string.IsNullOrEmpty(crunchedStyles)) { if (!m_echoInput) { if (ndx++ > 0) { // separate input group outputs with an appropriate newline writer.Write(uglifyCommandParser.CssSettings.LineTerminator); } writer.Write(crunchedStyles); } } } } return(retVal); }
void ProcessJavaScript(IList <InputGroup> inputGroups, UglifyCommandParser uglifyCommandParser, string outputPath, SymbolMap symbolMap, Encoding outputEncoding) { var settings = uglifyCommandParser.JSSettings; TextWriter mapWriter = null; ISourceMap sourceMap = null; try { // if we want a symbols map, we need to set it up now if (symbolMap != null && !settings.PreprocessOnly) { // if we specified the path, use it. Otherwise just use the output path with // ".map" appended to the end. Eg: output.js => output.js.map var symbolMapPath = symbolMap.Path.IsNullOrWhiteSpace() ? outputPath + ".map" : symbolMap.Path; // create the map writer and the source map implementation. // look at the Name attribute and implement the proper one. // the encoding needs to be UTF-8 WITHOUT a BOM or it won't work. if (!FileWriteOperation(symbolMapPath, uglifyCommandParser.Clobber, () => { mapWriter = new StreamWriter(symbolMapPath, false, new UTF8Encoding(false)); sourceMap = SourceMapFactory.Create(mapWriter, symbolMap.Name); if (sourceMap != null) { // if we get here, the symbol map now owns the stream and we can null it out so // we don't double-close it mapWriter = null; settings.SymbolsMap = sourceMap; // copy some property values sourceMap.SourceRoot = symbolMap.SourceRoot.IfNullOrWhiteSpace(null); sourceMap.SafeHeader = symbolMap.SafeHeader.GetValueOrDefault(false); // start the package sourceMap.StartPackage(outputPath, symbolMapPath); } return(true); })) { // could not write file Log.LogError(Strings.CouldNotWriteOutputFile, symbolMapPath); } } // save the original term settings. We'll make sure to set this back again // for the last item in the group, but we'll make sure it's TRUE for all the others. var originalTermSetting = settings.TermSemicolons; var currentSourceOrigin = SourceOrigin.Project; var parser = new JSParser(); parser.CompilerError += (sender, ea) => { // if the input group isn't project, then we only want to report sev-0 errors. // regardless, don't show any errors that have a severity lower (greater numeric value) // than the warning level specified. if ((currentSourceOrigin == SourceOrigin.Project || ea.Error.Severity == 0) && ea.Error.Severity <= uglifyCommandParser.WarningLevel) { LogContextError(ea.Error); } }; var outputBuilder = new StringBuilder(8192); using (var writer = new StringWriter(outputBuilder, CultureInfo.InvariantCulture)) { for (var inputGroupIndex = 0; inputGroupIndex < inputGroups.Count; ++inputGroupIndex) { var inputGroup = inputGroups[inputGroupIndex]; currentSourceOrigin = inputGroup.Origin; // for all but the last item, we want the term-semicolons setting to be true. // but for the last entry, set it back to its original value settings.TermSemicolons = inputGroupIndex < inputGroups.Count - 1 ? true : originalTermSetting; if (settings.PreprocessOnly) { parser.EchoWriter = writer; if (inputGroupIndex > 0) { // not the first group, so output the appropriate newline // sequence before we output the group. writer.Write(settings.LineTerminator); } } else { // not preprocess-only, so make sure the echo writer is null parser.EchoWriter = null; } // parse the input var block = parser.Parse(inputGroup.Source, settings); if (block != null && !settings.PreprocessOnly) { if (inputGroupIndex > 0) { // not the first group, so output the appropriate newline // sequence before we output the group. writer.Write(settings.LineTerminator); } // minify the AST to the output if (settings.Format == JavaScriptFormat.JSON) { if (!JsonOutputVisitor.Apply(writer, block, settings)) { Log.LogError(Strings.InvalidJSONOutput); } } else { OutputVisitor.Apply(writer, block, settings); } } } } // write output if (!Log.HasLoggedErrors) { if (!FileWriteOperation(outputPath, uglifyCommandParser.Clobber, () => { using (var writer = new StreamWriter(outputPath, false, outputEncoding)) { // write the combined minified code writer.Write(outputBuilder.ToString()); if (!settings.PreprocessOnly) { // give the map (if any) a chance to add something settings.SymbolsMap.IfNotNull(m => m.EndFile( writer, settings.LineTerminator)); } } return(true); })) { // could not write file Log.LogError(Strings.CouldNotWriteOutputFile, outputPath); } } else { Log.LogWarning(Strings.DidNotMinify, outputPath, Strings.ThereWereErrors); if (File.Exists(outputPath)) { File.Delete(outputPath); } } } finally { if (sourceMap != null) { mapWriter = null; settings.SymbolsMap = null; sourceMap.EndPackage(); sourceMap.Dispose(); } if (mapWriter != null) { mapWriter.Close(); } } }
/// <summary> /// Process an output group by deleting the output files if they exist. /// </summary> /// <param name="outputGroup">the OutputGroup being processed</param> /// <param name="outputFileInfo">FileInfo for the desired output file</param> /// <param name="symbolsFileInfo">FileInfo for the optional desired symbol file</param> /// <param name="defaultSettings">default settings for this output group</param> /// <param name="manifestModifiedTime">modified time for the manifest</param> protected override void ProcessOutputGroup(OutputGroup outputGroup, FileInfo outputFileInfo, FileInfo symbolsFileInfo, UglifyCommandParser defaultSettings, DateTime manifestModifiedTime) { // get the settings to use -- take the configuration for this output group // and apply them over the default settings var settings = ParseConfigSettings(outputGroup.GetConfigArguments(this.Configuration), defaultSettings); // we really only care about the clobber setting -- if the file is read-only, don't bother deleting it // unless we have the clobber setting. If the current setting is Preserve, then we want to change it to // Auto because it makes no sense to not delete a non-readonly file during a "clean" var clobber = settings.Clobber == ExistingFileTreatment.Preserve ? ExistingFileTreatment.Auto : settings.Clobber; // we don't care about the inputs, we just want to delete the outputs and be done if (outputFileInfo != null) { if (!FileWriteOperation(outputFileInfo.FullName, clobber, () => { outputFileInfo.IfNotNull(fi => { if (fi.Exists) { Log.LogMessage(MessageImportance.Normal, Strings.DeletingFile, fi.FullName); fi.Delete(); } }); return(true); })) { // can't delete the file - not an error; just informational Log.LogMessage(MessageImportance.Normal, Strings.CouldNotDeleteOutputFile, outputFileInfo.FullName); } } if (symbolsFileInfo != null) { if (!FileWriteOperation(symbolsFileInfo.FullName, clobber, () => { symbolsFileInfo.IfNotNull(fi => { if (fi.Exists) { Log.LogMessage(MessageImportance.Normal, Strings.DeletingFile, fi.FullName); fi.Delete(); } }); return(true); })) { // can't delete the file - not an error; just informational Log.LogMessage(MessageImportance.Normal, Strings.CouldNotDeleteOutputFile, symbolsFileInfo.FullName); } } }
public void ToSettings() { var testData = new ArgumentsSettings[] { new ArgumentsSettings() { CommandLine = "-warn:4 -ei:utf-8 -enc:out utf-8 /g:jQuery,$,Msn -p", JSSettings = new CodeSettings() { OutputMode = OutputMode.MultipleLines, LocalRenaming = LocalRenaming.KeepAll, KnownGlobalNamesList = "jQuery,$,Msn", MinifyCode = false, KillSwitch = -2 }, CssSettings = new CssSettings() { OutputMode = OutputMode.MultipleLines, KillSwitch = -2 }, WarningLevel = 4, EncodingInputName = "utf-8", EncodingOutputName = "utf-8" }, new ArgumentsSettings() { CommandLine = "-minify:false -rename:none", JSSettings = new CodeSettings() { MinifyCode = false, LocalRenaming = LocalRenaming.KeepAll }, CssSettings = null, WarningLevel = 0 }, new ArgumentsSettings() { CommandLine = "-define:foo,bar,ack,gag,42", JSSettings = new CodeSettings() { PreprocessorDefineList = "foo,bar,ack,gag" }, CssSettings = new CssSettings() { PreprocessorDefineList = "foo,bar,ack,gag" } }, new ArgumentsSettings() { CommandLine = "-ignore:foo,bar,ack,gag", JSSettings = new CodeSettings() { IgnoreErrorList = "foo,bar,ack,gag" }, CssSettings = new CssSettings() { IgnoreErrorList = "foo,bar,ack,gag" } }, new ArgumentsSettings() { CommandLine = "/aspnet:T -pretty:8 -term:Yes", JSSettings = new CodeSettings() { AllowEmbeddedAspNetBlocks = true, TermSemicolons = true, LocalRenaming = LocalRenaming.KeepAll, OutputMode = OutputMode.MultipleLines, IndentSize = 8, MinifyCode = false, KillSwitch = -2 }, CssSettings = new CssSettings() { AllowEmbeddedAspNetBlocks = true, TermSemicolons = true, OutputMode = OutputMode.MultipleLines, IndentSize = 8, KillSwitch = -2 } }, new ArgumentsSettings() { CommandLine = "/aspnet:F -minify:1 -kill:0 -pretty:0 -term:N", JSSettings = new CodeSettings() { AllowEmbeddedAspNetBlocks = false, TermSemicolons = false, LocalRenaming = LocalRenaming.KeepAll, OutputMode = OutputMode.MultipleLines, IndentSize = 0 }, CssSettings = new CssSettings() { AllowEmbeddedAspNetBlocks = false, TermSemicolons = false, OutputMode = OutputMode.MultipleLines, IndentSize = 0 } }, new ArgumentsSettings() { CommandLine = "-expr:minify -colors:hex -comments:All", JSSettings = null, CssSettings = new CssSettings() { MinifyExpressions = true, ColorNames = CssColor.Hex, CommentMode = CssComment.All } }, new ArgumentsSettings() { CommandLine = "-expr:raw -colors:MAJOR /comments:HaCkS", JSSettings = null, CssSettings = new CssSettings() { MinifyExpressions = false, ColorNames = CssColor.Major, CommentMode = CssComment.Hacks } }, new ArgumentsSettings() { CommandLine = "-cc:1 -comments:None -debug:true -inline:yes -literals:keep -literals:evAL -mac:Y -minify:T -new:Keep -reorder:yes -unused:remove -rename:all", JSSettings = new CodeSettings() { IgnoreConditionalCompilation = false, PreserveImportantComments = false, StripDebugStatements = false, InlineSafeStrings = true, EvalLiteralExpressions = true, MacSafariQuirks = true, MinifyCode = true, CollapseToLiteral = false, ReorderScopeDeclarations = true, RemoveUnneededCode = true, LocalRenaming = LocalRenaming.CrunchAll, PreprocessorDefineList = "debug" }, CssSettings = null }, new ArgumentsSettings() { CommandLine = "-cc:0 -comments:ImportanT -debug:false -inline:no /literals:combine -literals:NoEval -mac:N -minify:F -new:Collapse -reorder:N -unused:keep -rename:localization", JSSettings = new CodeSettings() { IgnoreConditionalCompilation = true, PreserveImportantComments = true, StripDebugStatements = true, InlineSafeStrings = false, EvalLiteralExpressions = false, MacSafariQuirks = false, MinifyCode = false, CollapseToLiteral = true, ReorderScopeDeclarations = false, RemoveUnneededCode = false, LocalRenaming = LocalRenaming.KeepLocalizationVars }, CssSettings = null }, new ArgumentsSettings() { CommandLine = "–debug:,", JSSettings = new CodeSettings() { StripDebugStatements = false, DebugLookupList = "", PreprocessorDefineList = "debug" }, CssSettings = null }, new ArgumentsSettings() { CommandLine = "-debug:N,", JSSettings = new CodeSettings() { StripDebugStatements = true, DebugLookupList = "" }, CssSettings = null }, new ArgumentsSettings() { CommandLine = "-debug:N,Foo,Bar,Ack.Gag.Barf,14,Name.42.First", JSSettings = new CodeSettings() { StripDebugStatements = true, DebugLookupList = "Foo,Bar,Ack.Gag.Barf" }, CssSettings = null }, new ArgumentsSettings() { CommandLine = "-global:foo,bar,ack,gag,212", JSSettings = new CodeSettings() { KnownGlobalNamesList = "foo,bar,ack,gag" }, CssSettings = null }, new ArgumentsSettings() { CommandLine = "-norename:foo,bar,ack,gag,105 -rename:NoProps", JSSettings = new CodeSettings() { NoAutoRenameList = "foo,bar,ack,gag", ManualRenamesProperties = false }, CssSettings = null }, new ArgumentsSettings() { CommandLine = "-rename:foo=bar,ack=gag,105=106", JSSettings = new CodeSettings() { RenamePairs = "foo=bar,ack=gag", ManualRenamesProperties = true }, CssSettings = null }, new ArgumentsSettings() { CommandLine = "–fnames:lock -evals:ignore", JSSettings = new CodeSettings() { PreserveFunctionNames = true, RemoveFunctionExpressionNames = false, EvalTreatment = EvalTreatment.Ignore }, CssSettings = null }, new ArgumentsSettings() { CommandLine = "-fnames:keep -evals:immediate", JSSettings = new CodeSettings() { PreserveFunctionNames = false, RemoveFunctionExpressionNames = false, EvalTreatment = EvalTreatment.MakeImmediateSafe }, CssSettings = null }, new ArgumentsSettings() { CommandLine = "-fnames:onlyref -evals:safeall", JSSettings = new CodeSettings() { PreserveFunctionNames = false, RemoveFunctionExpressionNames = true, EvalTreatment = EvalTreatment.MakeAllSafe }, CssSettings = null }, new ArgumentsSettings() { CommandLine = "–kill:-1", JSSettings = new CodeSettings() { KillSwitch = -1 }, CssSettings = new CssSettings() { KillSwitch = -1, CommentMode = CssComment.None } }, new ArgumentsSettings() { CommandLine = "–kill:0x1", JSSettings = new CodeSettings() { KillSwitch = 1 }, CssSettings = new CssSettings() { KillSwitch = 1, CommentMode = CssComment.None } }, new ArgumentsSettings() { CommandLine = "-kill:2", JSSettings = new CodeSettings() { KillSwitch = 2 }, CssSettings = new CssSettings() { KillSwitch = 2 } }, new ArgumentsSettings() { CommandLine = "-kill:0xDAB0 -cc:BOOYAH! -warn:-1", JSSettings = new CodeSettings() { KillSwitch = 0xdab0 }, CssSettings = new CssSettings() { KillSwitch = 55984 }, WarningLevel = 0 }, new ArgumentsSettings() { CommandLine = "-enc:in ascii -EO:big5 -warn", JSSettings = null, CssSettings = null, EncodingInputName = "ascii", EncodingOutputName = "big5", WarningLevel = int.MaxValue }, new ArgumentsSettings() { CommandLine = "-css /js", JSSettings = new CodeSettings(), CssSettings = new CssSettings() }, new ArgumentsSettings() { CommandLine = "-rename:All -pretty:WHAM", JSSettings = new CodeSettings() { OutputMode = OutputMode.MultipleLines, IndentSize = 4, KillSwitch = -16777218 }, CssSettings = new CssSettings() { OutputMode = OutputMode.MultipleLines, IndentSize = 4, KillSwitch = -2 } }, new ArgumentsSettings() { CommandLine = "-rename:All -pretty:-10", JSSettings = new CodeSettings() { OutputMode = OutputMode.MultipleLines, IndentSize = 4, KillSwitch = -16777218 }, CssSettings = new CssSettings() { OutputMode = OutputMode.MultipleLines, IndentSize = 4, KillSwitch = -2 } }, new ArgumentsSettings() { CommandLine = "–rename:foo=bar,foo=ack", JSSettings = new CodeSettings() { RenamePairs = "foo=bar" }, CssSettings = null }, new ArgumentsSettings() { CommandLine = "–d -h -j -k -m", JSSettings = new CodeSettings(), CssSettings = null }, new ArgumentsSettings() { CommandLine = "-l -z -hCL", JSSettings = new CodeSettings() { CollapseToLiteral = false, TermSemicolons = true, LocalRenaming = LocalRenaming.KeepLocalizationVars }, CssSettings = null }, new ArgumentsSettings() { CommandLine = "/HC", JSSettings = new CodeSettings() { }, CssSettings = null }, new ArgumentsSettings() { CommandLine = "-define:debug", JSSettings = new CodeSettings() { StripDebugStatements = false, PreprocessorDefineList = "debug" }, CssSettings = null }, new ArgumentsSettings() { CommandLine = "-define:debug -debug:0", JSSettings = new CodeSettings() { StripDebugStatements = true }, CssSettings = null }, new ArgumentsSettings() { CommandLine = "-debug:0 -define:debug", JSSettings = new CodeSettings() { StripDebugStatements = false, PreprocessorDefineList = "debug" }, CssSettings = null }, new ArgumentsSettings() { CommandLine = "-define:foo=bar,bob,ack=gag,BaT=CrAzY", JSSettings = new CodeSettings() { PreprocessorDefineList = "foo=bar,bob,ack=gag,BaT=CrAzY" }, CssSettings = new CssSettings() { PreprocessorDefineList = "foo=bar,bob,ack=gag,BaT=CrAzY" } }, new ArgumentsSettings() { CommandLine = "-define:foo=", JSSettings = null, CssSettings = new CssSettings() { PreprocessorDefineList = "foo" } }, new ArgumentsSettings() { CommandLine = "-define:configuration=Debug -define:debug=Y,ralph=driver", JSSettings = new CodeSettings() { StripDebugStatements = false, PreprocessorDefineList = "configuration=Debug,debug=Y,ralph=driver" }, CssSettings = new CssSettings() { PreprocessorDefineList = "configuration=Debug,debug=Y,ralph=driver" } }, new ArgumentsSettings() { CommandLine = "-define:once=first -define:OnCE=Last", JSSettings = new CodeSettings() { PreprocessorDefineList = "once=Last" }, CssSettings = new CssSettings() { PreprocessorDefineList = "once=Last" } }, }; var ndxTest = 0; foreach (var test in testData) { Trace.Write(string.Format("Settings test {0}, command line: ", ++ndxTest)); Trace.WriteLine(test.CommandLine ?? "<null pointer>"); // parse the command line var switchParser = new UglifyCommandParser(); switchParser.Parse(test.CommandLine); // assume succesful unless proven otherwise var success = true; // test the top-level properties if (switchParser.WarningLevel == test.WarningLevel) { Trace.WriteLine(string.Format("\tParsed warning level {0} matches expectations", switchParser.WarningLevel)); } else { Trace.WriteLine(string.Format("\tFAIL: Parsed warning level is {0}, expected is {1}", switchParser.WarningLevel, test.WarningLevel)); success = false; } if (string.CompareOrdinal(switchParser.EncodingInputName, test.EncodingInputName) == 0) { Trace.WriteLine(string.Format("\tParsed input encoding {0} matches expectations", switchParser.EncodingInputName)); } else { Trace.WriteLine(string.Format("\tFAIL: Parsed input encoding is {0}, expected is {1}", switchParser.EncodingInputName, test.EncodingInputName)); success = false; } if (string.CompareOrdinal(switchParser.EncodingOutputName, test.EncodingOutputName) == 0) { Trace.WriteLine(string.Format("\tParsed output encoding {0} matches expectations", switchParser.EncodingOutputName)); } else { Trace.WriteLine(string.Format("\tFAIL: Parsed output encoding is {0}, expected is {1}", switchParser.EncodingOutputName, test.EncodingOutputName)); success = false; } // if we care about the JS settings.... if (test.JSSettings != null) { var jsSuccess = CheckSettings(switchParser.JSSettings, test.JSSettings); if (!jsSuccess) { success = false; } } // if we care about the CSS settings.... if (test.CssSettings != null) { var cssSuccess = CheckSettings(switchParser.CssSettings, test.CssSettings); if (!cssSuccess) { success = false; } } Assert.IsTrue(success, "\t****TEST {0} FAILED!", ndxTest); } }