void Txt_parseClick(object sender, EventArgs e) { var engine = new JSParser(); var result = engine.Parse(txt_input.Text); if(result.IsSome) { var sb = new System.Text.StringBuilder(); foreach(var s in result.Value) s.ToString(sb, " ", ""); txt_output.Text = sb.ToString(); } else { txt_output.Text = "Can't parse input."; } }
private static JsAst ParseCode(string code) { var parser = new JSParser(code); var ast = parser.Parse(new CodeSettings()); return ast; }
/// <summary> /// Returns a minified version of a given JavaScript. /// </summary> /// <param name="resource">JavaScript to be minified</param> /// <returns>Minified JavaScript</returns> public string Minify(string resource) { if (String.IsNullOrEmpty(resource)) { return resource; } // Reset error minificationError = null; canRecover = true; try { JSParser parser = new JSParser(resource); parser.CompilerError += parser_CompilerError; // Parse the resource Block scriptBlock = parser.Parse(null); // Get minified code if no error occurs or parser was able to recover if ((scriptBlock != null) && ((minificationError == null) || ((minificationError != null) && canRecover))) { resource = scriptBlock.ToCode(); } } catch (JScriptException ex) { minificationError = ex; canRecover = false; } if (minificationError != null) { if (LogMinifierParseError) { // Log exception to event log if allowed EventLogProvider.LogException("JS Compression", "MINIFYJS", minificationError); } if (!canRecover) { // Add error info in front of non-minified resource resource += "\r\n\r\n// Minification failed (line " + minificationError.Line.ToString() + "): " + minificationError.Message; } } return resource; }
private JsAst ParseOneFile(JsAst ast, JSParser parser) { if (parser != null) { try { ast = parser.Parse(_codeSettings); } catch (Exception e) { if (e.IsCriticalException()) { throw; } Debug.Assert(false, String.Format("Failure in JavaScript parser: {0}", e.ToString())); } } return ast; }
public static void Prepare(IJsProjectEntry entry, TextReader sourceUnit) { var parser = new JSParser(sourceUnit.ReadToEnd()); var ast = parser.Parse(new CodeSettings()); entry.UpdateTree(ast, null); }
public bool CanExecuteText(string text) { var errorSink = new ReplErrorSink(text); var parser = new JSParser(text, errorSink); parser.Parse(new CodeSettings()); return !errorSink.Unterminated; }
private string Parse(JSParser parser, CodeSettings settings, string source) { var block = parser.Parse(source, settings); return(OutputVisitor.Apply(block, settings)); }
private Block GetParsedArrowFunctionCode(CodeSettings settings) { var parser = new JSParser(); return(parser.Parse(source, settings)); }
/// <summary> /// Produces a code minifiction of JS content by using the Microsoft Ajax JS Minifier /// </summary> /// <param name="content">JS content</param> /// <param name="isInlineCode">Flag whether the content is inline code</param> /// <param name="encoding">Text encoding</param> /// <returns>Minification result</returns> public CodeMinificationResult Minify(string content, bool isInlineCode, Encoding encoding) { if (string.IsNullOrWhiteSpace(content)) { return(new CodeMinificationResult(string.Empty)); } string newContent = string.Empty; var errors = new List <MinificationErrorInfo>(); var warnings = new List <MinificationErrorInfo>(); lock (_minificationSynchronizer) { if (_errorReporter == null) { _errorReporter = new MsAjaxErrorReporter(_settings.WarningLevel); } JSParser originalJsParser = isInlineCode ? _originalInlineJsParser : _originalEmbeddedJsParser; if (originalJsParser == null) { originalJsParser = CreateOriginalJsParserInstance(_settings, isInlineCode); if (isInlineCode) { _originalInlineJsParser = originalJsParser; } else { _originalEmbeddedJsParser = originalJsParser; } } originalJsParser.CompilerError += _errorReporter.ParseErrorHandler; var stringBuilderPool = AdvancedStringBuilderPool.Shared; StringBuilder contentBuilder = stringBuilderPool.Rent(content.Length); var documentContext = new DocumentContext(content) { FileContext = string.Empty }; try { using (var stringWriter = new StringWriter(contentBuilder, CultureInfo.InvariantCulture)) { // Parse the input Block scriptBlock = originalJsParser.Parse(documentContext); if (scriptBlock != null) { // Use normal output visitor OutputVisitor.Apply(stringWriter, scriptBlock, originalJsParser.Settings); } } newContent = contentBuilder.ToString(); } finally { originalJsParser.CompilerError -= _errorReporter.ParseErrorHandler; stringBuilderPool.Return(contentBuilder); errors.AddRange(_errorReporter.Errors); warnings.AddRange(_errorReporter.Warnings); _errorReporter.Clear(); } } return(new CodeMinificationResult(newContent, errors, warnings)); }
public void NoErrors() { // get the source code in the file specified by the first column string sourceCode; var fileName = TestContext.DataRow[0].ToString(); var filePath = Path.Combine(TestContext.DeploymentDirectory, @"Dll\Input\Frameworks", fileName); Assert.IsTrue(File.Exists(filePath), "Input file must exist"); Trace.Write("Reading source file: "); Trace.WriteLine(filePath); using (var reader = new StreamReader(filePath)) { sourceCode = reader.ReadToEnd(); } // run the framework file through a parser with standard settings (except for multi-line mode) // there should be only the errors specified in the other columns in the CSV file (if any) var errorCount = 0; var parser = new JSParser(); parser.CompilerError += (sender, ea) => { if (ea.Error.IsError) { Trace.WriteLine(ea.ToString()); ++errorCount; } }; var block = parser.Parse(new DocumentContext(sourceCode) { FileContext = filePath }); //, new CodeSettings() { OutputMode = OutputMode.MultipleLines }); var minifiedCode = OutputVisitor.Apply(block, parser.Settings); // save the results in Output folder var outputPath = Path.Combine(TestContext.DeploymentDirectory, @"Dll\Output\Frameworks", fileName); Trace.Write("Output path: "); Trace.WriteLine(outputPath); Directory.CreateDirectory(Path.GetDirectoryName(outputPath)); using (var writer = new StreamWriter(outputPath, false, Encoding.UTF8)) { writer.Write(minifiedCode); } // report if there were any errors, then reset the count. if (errorCount > 0) { Trace.Write("Original minification produced "); Trace.Write(errorCount); Trace.WriteLine(" errors!"); errorCount = 0; } // now run the output through another parser with no minify // settings -- there should DEFINITELY be no errors this time. parser = new JSParser(); parser.CompilerError += (sender, ea) => { if (ea.Error.IsError) { Trace.WriteLine(ea.Error.ToString()); ++errorCount; } }; block = parser.Parse(minifiedCode, new CodeSettings() { MinifyCode = false }); Assert.IsTrue(errorCount == 0, "Parsing minified " + fileName + " produces errors!"); }
public void SourceChange() { const string FileName = "SourceChange.js"; const string OriginalFileContext = "NULL"; // get the source code string source; var inputPath = Path.Combine(InputFolder, FileName); Trace.Write("Source: "); Trace.WriteLine(inputPath); using (var reader = new StreamReader(inputPath)) { source = reader.ReadToEnd(); } Trace.WriteLine(source); Trace.WriteLine(""); Trace.WriteLine("-----------------------"); Trace.WriteLine(""); // get the expected results string expected; var expectedPath = new FileInfo(Path.Combine(ExpectedFolder, FileName)); Trace.Write("Expected: "); Trace.WriteLine(inputPath); using (var reader = new StreamReader(expectedPath.FullName)) { expected = reader.ReadToEnd(); } Trace.WriteLine(expected); Trace.WriteLine(""); Trace.WriteLine("-----------------------"); Trace.WriteLine(""); // parse the source, keeping track of the errors var errors = new List <UglifyError>(); var parser = new JSParser(); parser.CompilerError += (sender, ea) => { errors.Add(ea.Error); }; var settings = new CodeSettings() { LocalRenaming = LocalRenaming.KeepAll }; var block = parser.Parse(new DocumentContext(source) { FileContext = OriginalFileContext }, settings); var minified = OutputVisitor.Apply(block, parser.Settings); // write the output so we can diagnose later if we need to if (!Directory.Exists(OutputFolder)) { Directory.CreateDirectory(OutputFolder); } var actualPath = new FileInfo(Path.Combine(OutputFolder, FileName)); Trace.Write("Actual: "); Trace.WriteLine(actualPath); using (var writer = new StreamWriter(actualPath.FullName, false, Encoding.UTF8)) { writer.Write(minified); } Trace.WriteLine(minified); Trace.WriteLine(""); Trace.WriteLine("-----------------------"); Trace.WriteLine(""); Trace.WriteLine("Output Comparison:"); Trace.WriteLine(string.Format("odd.exe \"{0}\" \"{1}\"", expectedPath.FullName, actualPath.FullName)); Trace.WriteLine(""); // and compare them -- they should be equal Assert.IsTrue(string.CompareOrdinal(minified, expected) == 0, "actual is not the expected"); var expectedErrors = new[] { new { FileContext = "anonfunc.js", StartLine = 2, EndLine = 2, StartColumn = 20, EndColumn = 21, ErrorCode = "JS1010" }, new { FileContext = "anonfunc.js", StartLine = 5, EndLine = 5, StartColumn = 3, EndColumn = 4, ErrorCode = "JS1195" }, new { FileContext = "addclass.js", StartLine = 2, EndLine = 2, StartColumn = 8, EndColumn = 14, ErrorCode = "JS1135" }, new { FileContext = "addclass.js", StartLine = 10, EndLine = 10, StartColumn = 42, EndColumn = 48, ErrorCode = "JS1135" }, }; // now, the errors should be the same -- in particular we are looking for the line/column // numbers and source path. they should be what got reset by the ///#SOURCE comments, not the // real values from the source file. Trace.WriteLine("Errors:"); foreach (var error in errors) { Trace.WriteLine(error.ToString()); var foundIt = false; foreach (var expectedError in expectedErrors) { if (expectedError.StartLine == error.StartLine && expectedError.EndLine == error.EndLine && expectedError.StartColumn == error.StartColumn && expectedError.EndColumn == error.EndColumn && string.CompareOrdinal(expectedError.FileContext, error.File) == 0 && string.CompareOrdinal(expectedError.ErrorCode, error.ErrorCode) == 0) { foundIt = true; break; } } Assert.IsTrue(foundIt, "Unexpected error"); } Trace.WriteLine(""); }
private 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> /// Crunched JS string passed to it, returning crunched string. /// The ErrorList property will be set with any errors found during the minification process. /// </summary> /// <param name="source">source Javascript</param> /// <param name="codeSettings">code minification settings</param> /// <returns>minified Javascript</returns> public string MinifyJavaScript(string source, CodeSettings codeSettings) { // default is an empty string var crunched = string.Empty; // reset the errors builder m_errorList = new List <ContextError>(); // create the parser and hook the engine error event var parser = new JSParser(); parser.CompilerError += OnJavaScriptError; var sb = StringBuilderPool.Acquire(); try { var preprocessOnly = codeSettings != null && codeSettings.PreprocessOnly; using (var stringWriter = new StringWriter(sb, CultureInfo.InvariantCulture)) { if (preprocessOnly) { parser.EchoWriter = stringWriter; } // parse the input var scriptBlock = parser.Parse(new DocumentContext(source) { FileContext = this.FileName }, codeSettings); if (scriptBlock != null && !preprocessOnly) { // we'll return the crunched code if (codeSettings != null && codeSettings.Format == JavaScriptFormat.JSON) { // we're going to use a different output visitor -- one // that specifically returns valid JSON. if (!JSONOutputVisitor.Apply(stringWriter, scriptBlock, codeSettings)) { m_errorList.Add(new ContextError() { Severity = 0, File = this.FileName, Message = CommonStrings.InvalidJSONOutput, }); } } else { // just use the normal output visitor OutputVisitor.Apply(stringWriter, scriptBlock, codeSettings); // if we are asking for a symbols map, give it a chance to output a little something // to the minified file. if (codeSettings != null && codeSettings.SymbolsMap != null) { codeSettings.SymbolsMap.EndFile(stringWriter, codeSettings.LineTerminator); } } } } crunched = sb.ToString(); } catch (Exception e) { m_errorList.Add(new ContextError() { Severity = 0, File = this.FileName, Message = e.Message, }); throw; } finally { sb.Release(); } return(crunched); }
/// <summary> /// Returns a minified version of a given JavaScript. /// </summary> /// <param name="resource">JavaScript to be minified</param> /// <returns>Minified JavaScript</returns> public string Minify(string resource) { try { JSParser parser = new JSParser(resource, new string[0]); // Parse the resource string parsed = parser.Parse(null).ToCode(); if (!String.IsNullOrEmpty(parsed)) { resource = parsed; } } catch (Exception ex) { // Parse throws null reference exception on some ocasions EventLogProvider.LogException("JS Compression", "MINIFYJS", ex); resource = "// Minification failed: " + ex.Message + "\r\n\r\n" + resource; } return resource; }
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"); }