Example #1
0
        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)));
        }
Example #2
0
        /// <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;
                    }
                }
            }
        }
Example #3
0
        public static string Minify(TextSource js)
        {
            var min = new JSMin();

            return(min.Minify(js));
        }