private void MinifyMarkup(BlockBuilder block) { var codeGenerator = new MarkupCodeGenerator(); var previousIsWhiteSpace = true; var previousTokenEndsWithBlockElement = true; var insideScript = false; for (int i = 0; i < block.Children.Count; i++) { var node = block.Children[i]; var span = node as Span; if (span == null) { // When we have a dynamic markup, we can't know if the last char will be whitespace // => to make it work in all cases, we won't minifiy whitespace just after code. previousIsWhiteSpace = false; previousTokenEndsWithBlockElement = false; var section = node as Block; if ((section != null) && (section.Type == BlockType.Section)) { // Sections are special as they force us to recurse the minification block.Children[i] = MinifySectionBlock(section); previousIsWhiteSpace = false; previousTokenEndsWithBlockElement = false; } continue; } // There may be several HTML tokens just one after the other. // => we concatenate everything in a single token to minify everyting in a single scan // (we is better for Javascript minification). var sb = new StringBuilder(); sb.Append(span.Content); if (i < block.Children.Count - 1) { var markup = block.Children[i + 1] as Span; while ((markup != null) && (markup.Kind == SpanKind.Markup) && ((markup.Next == null) || ((markup.Next != null) && ((markup.Next.Kind == SpanKind.Markup) || ((markup.Next.Kind != SpanKind.Markup) && !markup.Content.EndsWith("\"")))))) { block.Children.RemoveAt(i + 1); sb.Append(markup.Content); markup = i + 1 < block.Children.Count ? block.Children[i + 1] as Span : null; } } var content = sb.ToString(); if (string.IsNullOrEmpty(content)) { // Nothing to minify block.Children.RemoveAt(i); continue; } content = _minifier.Minify(content, previousIsWhiteSpace, previousTokenEndsWithBlockElement, insideScript); _minifier.AnalyseContent(content, ref previousIsWhiteSpace, ref previousTokenEndsWithBlockElement, ref insideScript); // We replace the content with the minified markup // and then let the CSharp/VB generator do their jobs. var builder = new SpanBuilder() { CodeGenerator = codeGenerator, EditHandler = span.EditHandler, Kind = span.Kind, Start = span.Start }; var symbol = new MarkupSymbol() { Content = content }; builder.Accept(symbol); span.ReplaceWith(builder); } }
private static Block RebuildCodeGenerators(Block block) { var builder = new BlockBuilder(block); var isDynamic = builder.CodeGenerator is DynamicAttributeBlockCodeGenerator; // We don't want any attribute specific logic here, null out the block code generator. if (isDynamic || builder.CodeGenerator is AttributeBlockCodeGenerator) { builder.CodeGenerator = BlockCodeGenerator.Null; } for (var i = 0; i < builder.Children.Count; i++) { var child = builder.Children[i]; if (child.IsBlock) { // The child is a block, recurse down into the block to rebuild its children builder.Children[i] = RebuildCodeGenerators((Block)child); } else { var childSpan = (Span)child; ISpanCodeGenerator newCodeGenerator = null; var literalGenerator = childSpan.CodeGenerator as LiteralAttributeCodeGenerator; if (literalGenerator != null) { if (literalGenerator.ValueGenerator == null || literalGenerator.ValueGenerator.Value == null) { newCodeGenerator = new MarkupCodeGenerator(); } else { newCodeGenerator = literalGenerator.ValueGenerator.Value; } } else if (isDynamic && childSpan.CodeGenerator == SpanCodeGenerator.Null) { // Usually the dynamic code generator handles rendering the null code generators underneath // it. This doesn't make sense in terms of tag helpers though, we need to change null code // generators to markup code generators. newCodeGenerator = new MarkupCodeGenerator(); } // If we have a new code generator we'll need to re-build the child if (newCodeGenerator != null) { var childSpanBuilder = new SpanBuilder(childSpan) { CodeGenerator = newCodeGenerator }; builder.Children[i] = childSpanBuilder.Build(); } } } return(builder.Build()); }