public string Compile(IEnumerable<string> files)
        {
            var blocks = new List<Block>();
            // ReSharper disable once LoopCanBeConvertedToQuery
            foreach (string file in files)
            {
                var parser = new JSParser(File.ReadAllText(file)) { FileContext = file };
                var block = parser.Parse(new CodeSettings
                {
                    EvalTreatment = EvalTreatment.MakeImmediateSafe,
                    PreserveImportantComments = false
                });
                if (block != null)
                {
                    blocks.Add(block);
                }
            }

            Block fst = blocks[0];
            for (int i = 1; i < blocks.Count; i++)
            {
                fst.Append(blocks[i]);
            }

            string sequenceCode = fst.ToCode();
            var minifier = new Minifier();
            string compiled = minifier.MinifyJavaScript(
                sequenceCode,
                new CodeSettings
                {
                    EvalTreatment = EvalTreatment.MakeImmediateSafe,
                    PreserveImportantComments = false
                });
            return compiled;
        }
示例#2
0
        string InsertModulePathIntoDefineCall(string moduleScript, string modulePath)
        {
            var parser = new JSParser(moduleScript);
            var sourceTree = parser.Parse(new CodeSettings());
            sourceTree.Accept(new ModulePathInserter(modulePath));

            return sourceTree.ToCode();
        }
示例#3
0
 public static bool JavaScriptContainsTopLevelVariable(string javaScriptSource, string variableName)
 {
     var parser = new JSParser(javaScriptSource);
     var tree = parser.Parse(new CodeSettings());
     var finder = new TopLevelVariableFinder(variableName);
     tree.Accept(finder);
     return finder.found;
 }
示例#4
0
        /// <summary>
        /// In addition to combining, also minifies the given Javascript.
        /// </summary>
        /// <returns>The combined and minified Javascript code for this bundle.</returns>
        public override string Combine()
        {
            var source = base.Combine();
            var result = string.Empty;
            var errorLines = string.Empty;
            var hasError = false;

            try
            {
                var jsParser = new JSParser(source);

                var settings = new CodeSettings()
                {
                    CombineDuplicateLiterals = true,
                    OutputMode = OutputMode.SingleLine,
                    RemoveUnneededCode = true,
                    TermSemicolons = false,
                    PreserveImportantComments = false,
                };

                jsParser.CompilerError += delegate(object sender, JScriptExceptionEventArgs args)
                {
                    // The 0 severity means errors.
                    // We can safely ignore the rest.
                    if (args.Error.Severity == 0)
                    {
                        hasError = true;
                        errorLines += string.Format("\r\n/* Javascript parse error when processing the bundle.\r\nStart: line {0} column {1}, end: line {2} column {3}.\r\nError message: {4} */",
                            args.Error.StartLine,
                            args.Error.StartColumn,
                            args.Error.EndLine,
                            args.Error.EndColumn,
                            args.Error.Message);
                    }
                };
                jsParser.UndefinedReference += delegate(object sender, UndefinedReferenceEventArgs args)
                {
                    // Let's just ignore undefined references.
                };

                var block = jsParser.Parse(settings);
                result = block.ToCode();
            }
            catch (Exception exc)
            {
                hasError = true;
                Logger.WriteException(exc);
            }

            // If there were errors, use the non-minified version and append the errors to the bottom,
            // so that the portal builder can debug it.
            if (hasError)
                result = source + "\r\n\r\n" + errorLines;

            return result;
        }
示例#5
0
        static void Main(string[] args)
        {
            var input = File.ReadAllText("input.js");

            Console.WriteLine("--- Raw ---");
            Console.WriteLine(input);
            Console.WriteLine("\r\n");

            Console.WriteLine("--- Minified ---");
            Console.WriteLine(new Minifier().MinifyJavaScript(input));
            Console.WriteLine("\r\n");

            Console.WriteLine("--- AST ---");
            var parser = new JSParser();
            parser.CompilerError += (_, ea) => Console.WriteLine(ea.Error);

            var functions = parser.Parse(input);
            var functionContext = parser.Parse("var functionContext = {};");

            new ObjectLiteralVisitor(functions).Visit(functionContext);

            OutputVisitor.Apply(Console.Out, functionContext, new CodeSettings() { MinifyCode = false, OutputMode = OutputMode.MultipleLines });
        }
示例#6
0
 public static string InsertModulePathIntoDefineCall(string moduleScript, string modulePath)
 {
     var inserter = new ModulePathInserter();
     var parser = new JSParser(moduleScript);
     var sourceTree = parser.Parse(new CodeSettings { MinifyCode = false });
     sourceTree.Accept(inserter);
     if (inserter.insertionIndex > 0)
     {
         return moduleScript.Insert(inserter.insertionIndex, "\"" + modulePath + "\",");
     }
     else
     {
         return moduleScript;
     }
 }
示例#7
0
        public override CombinedFileResult Parse(params string[] files)
        {
            var combined = CombineFiles(files);

            var settings = new CodeSettings {

            };

            var parser = new JSParser(combined);

            var block = parser.Parse(settings);
            var result = block.ToCode();

            var key = GenerateKey(result);

            return new CombinedFileResult {
                Content = result,
                Key = key,
            };
        }
示例#8
0
 Block ParseJavaScript(string source)
 {
     this.ErrorList = new List<ContextError>();
     JSParser jSParser = new JSParser();
     jSParser.CompilerError += new EventHandler<ContextErrorEventArgs>(this.OnJavaScriptError);
     try
     {
         var block = jSParser.Parse(source, CodeSettings);
         return block;
     }
     catch (Exception ex)
     {
         this.ErrorList.Add(new ContextError
         {
             Severity = 0,
             //File = this.FileName,
             Message = ex.Message
         });
         throw;
     }
 }
示例#9
0
        private Parsed ParseFunction()
        {
            Parsed parsed = Parsed.False;
            if (CurrentTokenType == TokenType.Function)
            {
                bool crunchedRGB = false;
                if (CurrentTokenText == "rgb(")
                {
                    // rgb function parsing
                    bool useRGB = false;
                    // converting to #rrggbb or #rgb IF we don't find any significant comments!
                    // skip any space or comments
                    int[] rgb = new int[3];

                    // we're going to be building up the rgb function just in case we need it
                    StringBuilder sbRGB = new StringBuilder();
                    sbRGB.Append("rgb(");

                    string comments = NextSignificantToken();
                    if (comments.Length > 0)
                    {
                        // add the comments
                        sbRGB.Append(comments);
                        // and signal that we need to use the RGB function because of them
                        useRGB = true;
                    }
                    for (int ndx = 0; ndx < 3; ++ndx)
                    {
                        // if this isn't the first number, we better find a comma separator
                        if (ndx > 0)
                        {
                            if (CurrentTokenType != TokenType.Character
                              || CurrentTokenText != ",")
                            {
                                ReportError(0, StringEnum.ExpectedComma, CurrentTokenText);
                            }
                            // add it to the rgb string builder
                            sbRGB.Append(',');

                            // skip to the next significant
                            comments = NextSignificantToken();
                            if (comments.Length > 0)
                            {
                                // add the comments
                                sbRGB.Append(comments);
                                // and signal that we need to use the RGB function because of them
                                useRGB = true;
                            }
                        }

                        // although we ALLOW negative numbers here, we'll trim them
                        // later. But in the mean time, save a negation flag.
                        bool negateNumber = false;
                        if (CurrentTokenType == TokenType.Character
                          && CurrentTokenText == "-")
                        {
                            negateNumber = true;
                            comments = NextSignificantToken();
                            if (comments.Length > 0)
                            {
                                // add the comments
                                sbRGB.Append(comments);
                                // and signal that we need to use the RGB function because of them
                                useRGB = true;
                            }
                        }

                        if (CurrentTokenType != TokenType.Number
                          && CurrentTokenType != TokenType.Percentage)
                        {
                            ReportError(0, StringEnum.ExpectedRgbNumberOrPercentage, CurrentTokenText);
                        }

                        // we might adjust the value, so save the token text
                        string tokenText = CurrentTokenText;
                        if (CurrentTokenType == TokenType.Number)
                        {
                            // get the number value
                            float numberValue = System.Convert.ToSingle(tokenText, CultureInfo.InvariantCulture) * (negateNumber ? -1 : 1);
                            // make sure it's between 0 and 255
                            if (numberValue < 0)
                            {
                                tokenText = "0";
                                rgb[ndx] = 0;
                            }
                            else if (numberValue > 255)
                            {
                                tokenText = "255";
                                rgb[ndx] = 255;
                            }
                            else
                            {
                                rgb[ndx] = System.Convert.ToInt32(numberValue);
                            }
                        }
                        else
                        {
                            // percentage
                            float percentageValue = System.Convert.ToSingle(tokenText.Substring(0, tokenText.Length - 1), CultureInfo.InvariantCulture)
                            * (negateNumber ? -1 : 1);
                            if (percentageValue < 0)
                            {
                                tokenText = "0%";
                                rgb[ndx] = 0;
                            }
                            else if (percentageValue > 100)
                            {
                                tokenText = "100%";
                                rgb[ndx] = 255;
                            }
                            else
                            {
                                rgb[ndx] = System.Convert.ToInt32(percentageValue * 255 / 100);
                            }
                        }

                        // add the number to the rgb string builder
                        sbRGB.Append(tokenText);

                        // skip to the next significant
                        comments = NextSignificantToken();
                        if (comments.Length > 0)
                        {
                            // add the comments
                            sbRGB.Append(comments);
                            // and signal that we need to use the RGB function because of them
                            useRGB = true;
                        }
                    }

                    if (useRGB)
                    {
                        // something prevented us from collapsing the rgb function
                        // just output the rgb function we've been building up
                        Append(sbRGB.ToString());
                    }
                    else
                    {
                        // we can collapse it to either #rrggbb or #rgb
                        // calculate the full hex string and crunch it
                        string fullCode = string.Format(CultureInfo.InvariantCulture, "#{0:x2}{1:x2}{2:x2}", rgb[0], rgb[1], rgb[2]);
                        string hexString = CrunchHexColor(fullCode, Settings.ColorNames);
                        Append(hexString);

                        // set the flag so we know we don't want to add the closing paren
                        crunchedRGB = true;
                    }
                }
                else if (CurrentTokenText == "expression(")
                {
                    AppendCurrent();
                    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
                        jsParser.CompilerError += OnScriptError;
                        m_expressionContainsErrors = false;

                        // parse the source with default settings
                        Block block = jsParser.Parse(null);

                        // 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 && !m_expressionContainsErrors)
                        {
                            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);
                    }
                }
                else
                {
                    // generic function parsing
                    AppendCurrent();
                    SkipSpace();

                    if (ParseFunctionParameters() == Parsed.False)
                    {
                        ReportError(0, StringEnum.ExpectedExpression, CurrentTokenText);
                    }
                }

                if (CurrentTokenType == TokenType.Character
                  && CurrentTokenText == ")")
                {
                    if (!crunchedRGB)
                    {
                        Append(')');
                    }
                    SkipSpace();
                }
                else
                {
                    ReportError(0, StringEnum.UnexpectedToken, CurrentTokenText);
                }
                parsed = Parsed.True;
            }
            return parsed;
        }
		private void InnerMinify(IAsset asset, JSParser jsParser)
		{
			string newContent;
			string assetUrl = asset.Url;

			var documentContext = new DocumentContext(asset.Content)
			{
				FileContext = assetUrl
			};

			jsParser.CompilerError += ParserErrorHandler;

			try
			{
				var stringBuilder = new StringBuilder();

				using (var stringWriter = new StringWriter(stringBuilder, CultureInfo.InvariantCulture))
				{
					Block block = jsParser.Parse(documentContext);
					if (block != null)
					{
						if (_jsParserConfiguration.Format == JavaScriptFormat.JSON)
						{
							// Use a JSON output visitor
							if (!JSONOutputVisitor.Apply(stringWriter, block, _jsParserConfiguration))
							{
								throw new MicrosoftAjaxParsingException(Strings.Minifiers_InvalidJsonOutput);
							}
						}
						else
						{
							// Use normal output visitor
							OutputVisitor.Apply(stringWriter, block, _jsParserConfiguration);
						}
					}
				}

				newContent = stringBuilder.ToString();
			}
			catch (MicrosoftAjaxParsingException e)
			{
				throw new AssetMinificationException(
					string.Format(CoreStrings.Minifiers_MinificationSyntaxError,
						CODE_TYPE, assetUrl, MINIFIER_NAME, e.Message), e);
			}
			catch (Exception e)
			{
				throw new AssetMinificationException(
					string.Format(CoreStrings.Minifiers_MinificationFailed,
						CODE_TYPE, assetUrl, MINIFIER_NAME, e.Message), e);
			}
			finally
			{
				jsParser.CompilerError -= ParserErrorHandler;
			}

			asset.Content = newContent;
			asset.Minified = true;
		}
示例#11
0
 static Block ParseJavaScript(IAsset asset)
 {
     var source = asset.OpenStream().ReadToEnd();
     var parser = new JSParser(source);
     return parser.Parse(new CodeSettings());
 }
        /// <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 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
            {
                if (codeSettings != null && codeSettings.PreprocessOnly)
                {
                    // just run through the preprocessor only
                    crunched = parser.PreprocessOnly(codeSettings);
                }
                else
                {
                    // parse the input
                    var scriptBlock = parser.Parse(codeSettings);
                    if (scriptBlock != null)
                    {
                        // 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.
                            var sb = new StringBuilder();
                            using (var stringWriter = new StringWriter(sb, CultureInfo.InvariantCulture))
                            {
                                if (!JSONOutputVisitor.Apply(stringWriter, scriptBlock))
                                {
                                    m_errorList.Add(new ContextError(
                                        true,
                                        0,
                                        null,
                                        null,
                                        null,
                                        this.FileName,
                                        0,
                                        0,
                                        0,
                                        0,
                                        JScript.InvalidJSONOutput));
                                }
                            }

                            crunched = sb.ToString();
                        }
                        else
                        {
                            // just use the normal output visitor
                            crunched = scriptBlock.ToCode();
                        }
                    }
                }
            }
            catch (Exception e)
            {
                m_errorList.Add(new ContextError(
                    true,
                    0,
                    null,
                    null,
                    null,
                    this.FileName,
                    0,
                    0,
                    0,
                    0,
                    e.Message));
                throw;
            }

            return crunched;
        }
示例#13
0
            public string RunTest(string sourceCode)
            {
                JSParser jsParser = new JSParser(sourceCode);
                jsParser.CompilerError += OnCompilerError;

                // kick off the parsing
                CodeSettings codeSettings = new CodeSettings();
                Block programBlock = jsParser.Parse(codeSettings);

                // return the crunched code
                return programBlock.ToCode();
            }
        /// <summary>
        /// Produces code minifiction of JS content by using
        /// 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;

            var errorReporter = new MsAjaxJsErrorReporter();
            var errors = new List<MinificationErrorInfo>();
            var warnings = new List<MinificationErrorInfo>();

            var jsParserConfiguration = isInlineCode ? GetInlineJsParserSettings() : GetEmbeddedJsParserSettings();

            var jsParser = new JSParser
            {
                Settings = jsParserConfiguration
            };
            jsParser.CompilerError += errorReporter.JsMinificationErrorHandler;

            try
            {
                var stringBuilder = new StringBuilder();

                using (var stringWriter = new StringWriter(stringBuilder, CultureInfo.InvariantCulture))
                {
                    Block block = jsParser.Parse(content);
                    if (block != null)
                    {
                        if (jsParserConfiguration.Format == JavaScriptFormat.JSON)
                        {
                            // Use a JSON output visitor
                            if (!JSONOutputVisitor.Apply(stringWriter, block, jsParserConfiguration))
                            {
                                errors.Add(new MinificationErrorInfo(MsAjaxStrings.ErrorMessage_InvalidJsonOutput));
                            }
                        }
                        else
                        {
                            // Use normal output visitor
                            OutputVisitor.Apply(stringWriter, block, jsParserConfiguration);
                        }
                    }
                }

                newContent = stringBuilder.ToString();
            }
            finally
            {
                jsParser.CompilerError -= errorReporter.JsMinificationErrorHandler;
            }

            errors.AddRange(errorReporter.Errors);
            warnings.AddRange(errorReporter.Warnings);

            return new CodeMinificationResult(newContent, errors, warnings);
        }
示例#15
0
        private void RunTest(CodeSettings settings)
        {
            var source = GetSource(".js");
            var expected = GetExpected(".js");

            settings = settings ?? new CodeSettings() { MinifyCode = false };

            if (source.Length == expected.Length)
            {
                for (var ndx = 0; ndx < source.Length; ++ndx)
                {
                    Trace.WriteLine("");
                    Trace.WriteLine("----------------------------------------------------------------------------");
                    Trace.WriteLine("");

                    // parse the source into an AST
                    var parser = new JSParser(source[ndx]);
                    var block = parser.Parse(settings);

                    // there should only be one statement in the block
                    if (block.Count == 1)
                    {
                        var expression = block[0];

                        // create the logical-not visitor on the expression
                        var logicalNot = new Microsoft.Ajax.Utilities.LogicalNot(expression, parser);

                        // get the original code
                        var original = expression.ToCode();

                        Trace.Write("ORIGINAL EXPRESSION:    ");
                        Trace.WriteLine(original);

                        // get the measured delta
                        var measuredDelta = logicalNot.Measure();

                        // perform the logical-not operation
                        logicalNot.Apply();

                        // get the resulting code -- should still be only one statement in the block
                        var notted = block[0].ToCode();

                        Trace.Write("LOGICAL-NOT EXPRESSION: ");
                        Trace.WriteLine(notted);

                        Trace.Write("EXPECTED EXPRESSION:    ");
                        Trace.WriteLine(expected[ndx]);

                        Trace.Write("DELTA: ");
                        Trace.WriteLine(measuredDelta);

                        // what's the actual difference
                        var actualDelta = notted.Length - original.Length;
                        Assert.IsTrue(actualDelta == measuredDelta,
                            "Measurement was off; calculated {0} but was actually {1}",
                            measuredDelta,
                            actualDelta);

                        Assert.IsTrue(string.CompareOrdinal(expected[ndx], notted) == 0, "Expected output is not the same!!!!");
                    }
                    else
                    {
                        Assert.Fail(string.Format("Source line {0} parsed to more than one statement!", ndx + 1));
                    }
                }
            }
            else
            {
                Assert.Fail("Input and Expected files have different number of lines!");
            }
        }