in AjaxMin/MainClass.cs [955:1199]
private int ProcessOutputGroup(OutputGroup outputGroup, SwitchParser switchParser)
{
int retVal = 0;
// if there are no input groups, we'll be reading from STDIN and should output our
// headers right now.
WriteProgress();
if (outputGroup.Inputs.Count == 0)
{
// output status message so user knows input coming from stdin
WriteProgress(AjaxMin.MinifyFromStdIn);
}
else if (outputGroup.Inputs.Count == 1)
{
// minifying a single input group. Simple message to let user know what's going on
WriteProgress(AjaxMin.MinifySingleInput.FormatInvariant(outputGroup.Inputs[0].Path));
}
else
{
// combining and minifying. More complex messaging.
WriteProgress(AjaxMin.MinifyingMultipleInputs);
foreach (var input in outputGroup.Inputs)
{
WriteProgress("\t{0}", input.Path);
}
}
// combine all the source files into a single string, delimited with ///#SOURCE comments so we can track
// back to the original files. Also, add all the raw input to the echo builder.
var inputGroups = outputGroup.ReadInputGroups(switchParser.EncodingInputName);
// we are always going to build the combined raw sources so we can run some comparison calcs on them.
var rawBuilder = new StringBuilder(8192);
// for calculation purposes, we're going to want to calculate the length of
// all the raw sources, and combine them together
var sourceLength = 0L;
foreach (var inputGroup in inputGroups)
{
// add the raw source length and the raw source
// to the echo builder
sourceLength += inputGroup.RawSource.Length;
rawBuilder.Append(inputGroup.RawSource);
}
// if the crunch group has any resource strings objects, we need to add them to the back
// of the settings list
var hasCrunchSpecificResources = outputGroup.Resources.Count > 0;
// create a string builder we'll dump our output into
var outputBuilder = new StringBuilder(8192);
// we're just echoing the input -- so if this is a JS output file,
// we want to output a JS version of all resource dictionaries at the top
// of the file.
if (m_echoInput
&& outputGroup.CodeType == CodeType.JavaScript
&& switchParser.JSSettings.ResourceStrings.Count > 0)
{
foreach (var resourceStrings in switchParser.JSSettings.ResourceStrings)
{
string resourceObject = CreateJSFromResourceStrings(resourceStrings);
outputBuilder.Append(resourceObject);
}
}
switch (outputGroup.CodeType)
{
case CodeType.StyleSheet:
if (hasCrunchSpecificResources)
{
// add to the CSS list
outputGroup.ProcessResourceStrings(switchParser.CssSettings.ResourceStrings, null);
}
retVal = ProcessCssFile(inputGroups, switchParser, outputBuilder);
break;
case CodeType.JavaScript:
if (hasCrunchSpecificResources)
{
// add to the JS list
outputGroup.ProcessResourceStrings(switchParser.JSSettings.ResourceStrings, c_defaultResourceObjectName);
}
retVal = ProcessJSFile(inputGroups, switchParser, outputBuilder);
break;
default:
throw new NotSupportedException(AjaxMin.UnknownInputType);
}
// if we are pretty-printing, add a newline
if (switchParser.PrettyPrint)
{
outputBuilder.AppendLine();
}
string outputCode = outputBuilder.ToString();
// use the output group encoding. If none specified, use the default output encoding.
var encodingOutput = outputGroup.GetEncoding(switchParser.EncodingOutputName);
// now write the final output file
if (outputGroup.Path.IsNullOrWhiteSpace())
{
// no output file specified - send to STDOUT
// if the code is empty, don't bother outputting it to the console
if (!outputCode.IsNullOrWhiteSpace())
{
// however, for some reason when I set the output encoding it
// STILL doesn't call the EncoderFallback to Unicode-escape characters
// not supported by the encoding scheme. So instead we need to run the
// translation outselves. Still need to set the output encoding, though,
// so the translated bytes get displayed properly in the console.
byte[] encodedBytes = encodingOutput.GetBytes(outputCode);
// only output the size analysis if we aren't echoing the input
// and we haven't said we want to skip it.
if (!m_echoInput && !s_silentMode && !m_skipSizeComparisons)
{
// calculate the percentage saved
var percentage = Math.Round((1 - ((double)encodedBytes.Length) / sourceLength) * 100, 1);
WriteProgress(AjaxMin.SavingsMessage.FormatInvariant(
sourceLength,
encodedBytes.Length,
percentage
));
// calculate how much gzip on the unminified, combined original source might be
long gzipLength = CalculateGzipSize(encodingOutput.GetBytes(rawBuilder.ToString()));
percentage = Math.Round((1 - ((double)gzipLength) / sourceLength) * 100, 1);
WriteProgress(AjaxMin.SavingsGzipSourceMessage.FormatInvariant(gzipLength, percentage));
// calculate how much gzip on the minified output might be
gzipLength = CalculateGzipSize(encodedBytes);
percentage = Math.Round((1 - ((double)gzipLength) / sourceLength) * 100, 1);
WriteProgress(AjaxMin.SavingsGzipMessage.FormatInvariant(gzipLength, percentage));
// blank line after
WriteProgress();
}
// send to console out -- if we even want any output
if (!m_noOutput)
{
// set the console encoding
try
{
// try setting the appropriate output encoding
Console.OutputEncoding = encodingOutput;
}
catch (IOException e)
{
// sometimes they will error, in which case we'll just set it to ascii
Debug.WriteLine(e.ToString());
// see if we know what kind of fallback we need
EncoderFallback fallback = null;
if (outputGroup.CodeType == CodeType.JavaScript)
{
fallback = new JSEncoderFallback();
}
else if (outputGroup.CodeType == CodeType.StyleSheet)
{
fallback = new CssEncoderFallback();
}
if (fallback != null)
{
// try it again -- but this time if it fails, just leave the stream
// to its default encoding.
try
{
var asciiClone = (Encoding)Encoding.ASCII.Clone();
asciiClone.EncoderFallback = fallback;
Console.OutputEncoding = asciiClone;
}
catch (IOException)
{
// ignore
}
}
}
Console.Out.Write(Console.OutputEncoding.GetChars(encodedBytes));
WriteProgress();
}
}
}
else
{
retVal = this.ClobberFileAndExecuteOperation(
outputGroup.Path, (path) =>
{
// create the output file using the given encoding
using (StreamWriter outputStream = new StreamWriter(
path,
false,
encodingOutput
))
{
outputStream.Write(outputCode);
}
// only output the size analysis if there is actually some output to measure
// and we're not echoing the input
if (File.Exists(path) && !m_echoInput && !s_silentMode && !m_skipSizeComparisons)
{
// get the size of the resulting file
FileInfo crunchedFileInfo = new FileInfo(path);
long crunchedLength = crunchedFileInfo.Length;
if (crunchedLength > 0)
{
// calculate the percentage saved by minification
var percentage = Math.Round((1 - ((double)crunchedLength) / sourceLength) * 100, 1);
WriteProgress(AjaxMin.SavingsMessage.FormatInvariant(
sourceLength,
crunchedLength,
percentage));
// calculate how much gzip on the unminified, combined original source might be
long gzipLength = CalculateGzipSize(encodingOutput.GetBytes(rawBuilder.ToString()));
percentage = Math.Round((1 - ((double)gzipLength) / sourceLength) * 100, 1);
WriteProgress(AjaxMin.SavingsGzipSourceMessage.FormatInvariant(gzipLength, percentage));
// calculate how much gzip on the minified output might be
gzipLength = CalculateGzipSize(File.ReadAllBytes(path));
percentage = Math.Round((1 - ((double)gzipLength) / sourceLength) * 100, 1);
WriteProgress(AjaxMin.SavingsGzipMessage.FormatInvariant(gzipLength, percentage));
// blank line after
WriteProgress();
}
}
});
}
if (retVal == 0 && m_errorsFound)
{
// make sure we report an error
retVal = 1;
}
return retVal;
}