private bool m_preprocessOnly; // = false; #endregion #region file processing private int ProcessJSFile(string sourceFileName, ResourceStrings resourceStrings, StringBuilder outputBuilder, ref bool lastEndedSemicolon, ref long sourceLength) { int retVal = 0; // read our chunk of code string source; if (sourceFileName.Length > 0) { using (StreamReader reader = new StreamReader(sourceFileName, m_encodingInput)) { WriteProgress( StringMgr.GetString("CrunchingFile", Path.GetFileName(sourceFileName)) ); source = reader.ReadToEnd(); } } else { WriteProgress(StringMgr.GetString("CrunchingStdIn")); try { // try setting the input encoding Console.InputEncoding = m_encodingInput; } catch (IOException e) { // error setting the encoding input; just use whatever the default is Debug.WriteLine(e.ToString()); } source = Console.In.ReadToEnd(); if (m_analyze) { // calculate the actual number of bytes read using the input encoding // and the string that we just read and // add the number of bytes read into the input length. sourceLength += Console.InputEncoding.GetByteCount(source); } else { // don't bother calculating the actual bytes -- the number of characters // is sufficient if we're not doing the analysis sourceLength += source.Length; } } // add the input length to the running total sourceLength += source.Length; // create the a parser object for our chunk of code JSParser parser = new JSParser(source); // set up the file context for the parser parser.FileContext = sourceFileName; // hook the engine events parser.CompilerError += OnCompilerError; parser.UndefinedReference += OnUndefinedReference; // put the resource strings object into the parser parser.ResourceStrings = resourceStrings; // set our flags CodeSettings settings = new CodeSettings(); settings.ManualRenamesProperties = m_renameProperties; settings.CollapseToLiteral = m_collapseToLiteral; settings.CombineDuplicateLiterals = m_combineDuplicateLiterals; settings.EvalLiteralExpressions = m_evalLiteralExpressions; settings.EvalTreatment = m_evalTreatment; settings.IndentSize = m_indentSize; settings.InlineSafeStrings = m_safeForInline; settings.LocalRenaming = m_localRenaming; settings.MacSafariQuirks = m_macSafariQuirks; settings.OutputMode = (m_prettyPrint ? OutputMode.MultipleLines : OutputMode.SingleLine); settings.PreserveFunctionNames = m_preserveFunctionNames; settings.RemoveFunctionExpressionNames = m_removeFunctionExpressionNames; settings.RemoveUnneededCode = m_removeUnneededCode; settings.StripDebugStatements = m_stripDebugStatements; settings.AllowEmbeddedAspNetBlocks = m_allowAspNet; settings.SetKnownGlobalNames(m_globals == null ? null : m_globals.ToArray()); settings.SetNoAutoRename(m_noAutoRename == null ? null : m_noAutoRename.ToArray()); settings.IgnoreConditionalCompilation = m_ignoreConditionalCompilation; // if there are defined preprocessor names if (m_defines != null && m_defines.Count > 0) { // set the list of defined names to our array of names settings.SetPreprocessorDefines(m_defines.ToArray()); } // if there are rename entries... if (m_renameMap != null && m_renameMap.Count > 0) { // add each of them to the parser foreach (var sourceName in m_renameMap.Keys) { settings.AddRenamePair(sourceName, m_renameMap[sourceName]); } } // cast the kill switch numeric value to the appropriate TreeModifications enumeration settings.KillSwitch = (TreeModifications)m_killSwitch; string resultingCode = null; if (m_preprocessOnly) { // we only want to preprocess the code. Call that api on the parser resultingCode = parser.PreprocessOnly(settings); } else { Block scriptBlock = parser.Parse(settings); if (scriptBlock != null) { if (m_analyze) { // output our report CreateReport(parser.GlobalScope); } // crunch the output and write it to debug stream resultingCode = scriptBlock.ToCode(); } else { // no code? WriteProgress(StringMgr.GetString("NoParsedCode")); } } if (!string.IsNullOrEmpty(resultingCode)) { // always output the crunched code to debug stream System.Diagnostics.Debug.WriteLine(resultingCode); // if the last block of code didn't end in a semi-colon, // then we need to add one now if (!lastEndedSemicolon) { outputBuilder.Append(';'); } // we'll output either the crunched code (normal) or // the raw source if we're just echoing the input string outputCode = (m_echoInput ? source : resultingCode); // send the output code to the output stream outputBuilder.Append(outputCode); // check if this string ended in a semi-colon so we'll know if // we need to add one between this code and the next block (if any) lastEndedSemicolon = (outputCode[outputCode.Length - 1] == ';'); } else { // resulting code is null or empty Debug.WriteLine(StringMgr.GetString("OutputEmpty")); } return(retVal); }
/// <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); }