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);
        }
Example #2
0
        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);
                }
            }
        }
Example #5
0
        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);
            }
        }
Example #9
0
        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);
        }
Example #10
0
        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");
        }
Example #11
0
        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);
            }
        }
Example #12
0
        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);
Example #14
0
 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();
 }
Example #15
0
        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);
        }
Example #16
0
        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);
            }
        }
Example #17
0
        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);
        }
Example #18
0
        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();
                }
            }
        }
Example #19
0
        /// <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);
                }
            }
        }
Example #20
0
        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);
            }
        }