/// <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, JsSettings codeSettings) { // default is an empty string var crunched = string.Empty; // reset the errors builder m_errorList = new List<MinifierError>(); // create the parser from the source string. // pass null for the assumed globals array var parser = new JsParser(source); // file context is a property on the parser parser.FileContext = FileName; // hook the engine error event parser.CompilerError += OnJavaScriptError; try { var preprocessOnly = codeSettings != null && codeSettings.PreprocessOnly; var sb = new StringBuilder(); using (var stringWriter = new StringWriter(sb, CultureInfo.InvariantCulture)) { if (preprocessOnly) { parser.EchoWriter = stringWriter; } // parse the input var scriptBlock = parser.Parse(codeSettings); if (scriptBlock != null && !preprocessOnly) { // we'll return the crunched code if (codeSettings != null && codeSettings.Format == JsFormat.JSON) { // we're going to use a different output visitor -- one // that specifically returns valid JSON. if (!JsonOutputVisitor.Apply(stringWriter, scriptBlock)) { m_errorList.Add(new MinifierError( true, 0, null, null, null, this.FileName, 0, 0, 0, 0, JScript.InvalidJSONOutput)); } } else { // just use the normal output visitor JsOutputVisitor.Apply(stringWriter, scriptBlock, codeSettings); } } } crunched = sb.ToString(); } catch (Exception e) { m_errorList.Add(new MinifierError( true, 0, null, null, null, this.FileName, 0, 0, 0, 0, e.Message)); throw; } return crunched; }
private Parsed ParseExpressionFunction() { var parsed = Parsed.False; if (CurrentTokenType == TokenType.Function && string.Compare(CurrentTokenText, "expression(", StringComparison.OrdinalIgnoreCase) == 0) { Append(CurrentTokenText.ToLowerInvariant()); NextToken(); // for now, just echo out everything up to the matching closing paren, // taking into account that there will probably be other nested paren pairs. // The content of the expression is JavaScript, so we'd really // need a full-blown JS-parser to crunch it properly. Kinda scary. // Start the parenLevel at 0 because the "expression(" token contains the first paren. var jsBuilder = new StringBuilder(); int parenLevel = 0; while (!m_scanner.EndOfFile && (CurrentTokenType != TokenType.Character || CurrentTokenText != ")" || parenLevel > 0)) { if (CurrentTokenType == TokenType.Function) { // the function token INCLUDES the opening parenthesis, // so up the paren level whenever we find a function. // AND this includes the actual expression( token -- so we'll // hit this branch at the beginning. Make sure the parenLevel // is initialized to take that into account ++parenLevel; } else if (CurrentTokenType == TokenType.Character) { switch (CurrentTokenText) { case "(": // start a nested paren ++parenLevel; break; case ")": // end a nested paren // (we know it's nested because if it wasn't, we wouldn't // have entered the loop) --parenLevel; break; } } jsBuilder.Append(CurrentTokenText); NextToken(); } // create a JSParser object with the source we found, crunch it, and send // the minified script to the output var expressionCode = jsBuilder.ToString(); if (Settings.MinifyExpressions) { // we want to minify the javascript expressions. // create a JSParser object from the code we parsed. JsParser jsParser = new JsParser(expressionCode); // copy the file context jsParser.FileContext = this.FileContext; // hook the error handler and set the "contains errors" flag to false. // the handler will set the value to true if it encounters any errors var containsErrors = false; jsParser.CompilerError += (sender, ea) => { ReportError(0, CssErrorCode.ExpressionError, ea.Error.Message); containsErrors = true; }; // parse the source as an expression using our common JS settings JsBlock block = jsParser.Parse(m_jsSettings); // if we got back a parsed block and there were no errors, output the minified code. // if we didn't get back the block, or if there were any errors at all, just output // the raw expression source. if (block != null && !containsErrors) { Append(block.ToCode()); } else { Append(expressionCode); } } else { // we don't want to minify expression code for some reason. // just output the code exactly as we parsed it Append(expressionCode); } if (CurrentTokenType == TokenType.Character && CurrentTokenText == ")") { AppendCurrent(); SkipSpace(); parsed = Parsed.True; } else { ReportError(0, CssErrorCode.ExpectedClosingParenthesis, CurrentTokenText); } } return parsed; }