public static string CompressJS(Stream stream) { var jsMin = new JSMin(); if (!stream.CanRead) { throw new InvalidOperationException("Cannot read input stream"); } if (stream.CanSeek) { stream.Position = 0; } return(jsMin.Minify(new StreamReader(stream))); }
/// <summary> /// Compress HTML by removing unnecessary whitespace and comments /// </summary> /// <param name="reader">Stream of <see cref="HtmlNode"/> to minify</param> /// <param name="settings">Settings to control how the HTML is compressed</param> /// <returns>Stream of minified <see cref="HtmlNode"/></returns> public static IEnumerable <HtmlNode> Minify(this IEnumerable <HtmlNode> reader, HtmlMinifySettings settings = null) { settings = settings ?? HtmlMinifySettings.ReadOnlyDefault; var state = MinifyState.LastCharWasSpace; var tagState = ContainingTag.None; int trimStart; int trimEnd; StringBuilder builder = null; var jsMin = new JSMin(); foreach (var node in reader) { if (node.Type == HtmlTokenType.Comment) { // Ignore comments, unless they are conditional if (node.Value.StartsWith("[if") || node.Value.EndsWith("endif]")) { yield return(node); } } else if (node.Type == HtmlTokenType.Text) { if (node.Value == "" || node.Value == null) { // do nothing } else if (tagState == ContainingTag.WhitespacePreserve) { yield return(node); state = MinifyState.LastCharWasSpace; } else if (tagState == ContainingTag.Script) { if (builder == null) { builder = Pool.NewStringBuilder(); } builder.Append(node.Value); } else { TrimIndices(node.Value, out trimStart, out trimEnd); if (trimEnd < 0) { // Do nothing for an empty or null string } else if (trimEnd < trimStart) { if (state == MinifyState.Compressed) { state = MinifyState.SpaceNeeded; } } else { if (state == MinifyState.SpaceNeeded && trimStart == 0) { yield return(new HtmlText(node.Position, " ")); state = MinifyState.LastCharWasSpace; } if (state == MinifyState.LastCharWasSpace || state == MinifyState.InlineStartAfterSpace || trimStart == 0) { yield return(new HtmlText(node.Position, GetCompressedString(node.Value, trimStart, trimEnd))); } else { yield return(new HtmlText(node.Position, GetCompressedString(node.Value, trimStart - 1, trimEnd))); } if (trimEnd < node.Value.Length - 1) { state = MinifyState.SpaceNeeded; } else { state = MinifyState.Compressed; } } } } else { if (state == MinifyState.SpaceNeeded) { if (node.Type == HtmlTokenType.EndTag && !settings.InlineElements.Contains(node.Value)) { state = MinifyState.LastCharWasSpace; } else { // Inline elements can render spaces, otherwise spaces shouldn't be rendered between elements if ((node.Type == HtmlTokenType.StartTag || node.Type == HtmlTokenType.EndTag) && settings.InlineElements.Contains(node.Value)) { yield return(new HtmlText(node.Position, " ")); } if (settings.PreserveSurroundingSpaceTags.Contains(node.Value)) { state = MinifyState.Compressed; } else { state = MinifyState.LastCharWasSpace; } } } if (node.Type == HtmlTokenType.EndTag && node.Value == "script" && builder != null) { yield return(new HtmlText(node.Position, Js.Minify(new TextSource(builder)))); builder.ToPool(); builder = null; } var tag = node as HtmlStartTag; if (tag != null) { if (tag.Attributes.Any(a => a.Key == "style" || a.Key == "class")) { var newTag = new HtmlStartTag(tag.Position, tag.Value); foreach (var attr in tag.Attributes) { if (attr.Key == "style") { newTag.Add(attr.Key, TrimStyleString(attr.Value)); } else if (attr.Key == "class") { newTag.Add(attr.Key, GetCompressedString(attr.Value)); } else { newTag.Attributes.Add(attr); } } newTag.IsSelfClosing = tag.IsSelfClosing; yield return(newTag); } else { yield return(node); } if (state == MinifyState.LastCharWasSpace && settings.InlineElements.Contains(node.Value)) { if (HtmlTextWriter.VoidElements.Contains(node.Value)) { state = MinifyState.Compressed; } else { state = MinifyState.InlineStartAfterSpace; } } } else { yield return(node); if (state == MinifyState.InlineStartAfterSpace) { state = MinifyState.Compressed; } } if (node.Type == HtmlTokenType.StartTag && settings.PreserveInnerSpaceTags.Contains(node.Value)) { tagState = ContainingTag.WhitespacePreserve; } else if (tag?.Value == "script") { var type = tag["type"]; if (string.IsNullOrEmpty(type)) { type = "application/javascript"; } if (settings.ScriptTypesToCompress.Contains(type)) { tagState = ContainingTag.Script; } else { tagState = ContainingTag.WhitespacePreserve; } } else if (node.Type == HtmlTokenType.EndTag && (settings.PreserveInnerSpaceTags.Contains(node.Value) || node.Value == "script")) { tagState = ContainingTag.None; } } } }
public static string Minify(TextSource js) { var min = new JSMin(); return(min.Minify(js)); }