Repository: gjtorikian/html-pipeline (Ruby)

Issue:
convert_filter only executed when node_filters are present
Hey hey, I'm playing around with `html-pipeline` and noticed that the following snippet does not run the `convert_filter`:

```rb
require "html_pipeline/convert_filter/markdown_filter"
require "selma"
markdown = "1. Foo\n2. Bar"
result = HTMLPipeline.new(convert_filter: HTMLPipeline::ConvertFilter::MarkdownFilter.new).call(markdown)
{
    :output => "1. Foo\n2. Bar"
}
```

vs. when defining *any* node_filter like so:

```rb
require "html_pipeline/convert_filter/markdown_filter"
require "html_pipeline/node_filter/https_filter"
require "selma"
markdown = "1. Foo\n2. Bar"
result = HTMLPipeline.new(
  convert_filter: HTMLPipeline::ConvertFilter::MarkdownFilter.new,
  node_filters: [HTMLPipeline::NodeFilter::HttpsFilter.new],
).call(markdown)
{
    :output => "<ol>\n<li>Foo</li>\n<li>Bar</li>\n</ol>"
}
```

AFAIU the `html`-result will not be used unless any `@node_filters` are present. See https://github.com/gjtorikian/html-pipeline/blob/74766e75a4c65a3d071faea84fc630d3fe217ab1/lib/html_pipeline.rb#L168-L179


Discussion:
Just let me know if you like to have a PR for that – only if this is unintended behaviour of course.

In practice this isn't an issue for me since I'll be using some `node_filters`.
Hey @grekko, I'm in the process of updating to v3 and just encountered the same issue. I was wondering if this was an intentional behavior. In my case, I only need the `convert_filter`.
Completely unintentional—thank you for the report. Will be fixed monetarily in 3.0.2!



Patch:
diff --git a/lib/html_pipeline.rb b/lib/html_pipeline.rb
index 8f2afe1f..015ae610 100644
--- a/lib/html_pipeline.rb
+++ b/lib/html_pipeline.rb
@@ -114,10 +114,10 @@ class << self
   def initialize(text_filters: [], convert_filter: nil, sanitization_config: SanitizationFilter::DEFAULT_CONFIG, node_filters: [], default_context: {}, result_class: Hash)
     raise ArgumentError, "default_context cannot be nil" if default_context.nil?
 
-    @text_filters = text_filters.flatten.freeze
+    @text_filters = text_filters.flatten.freeze || []
     validate_filters(@text_filters, HTMLPipeline::TextFilter)
 
-    @node_filters = node_filters.flatten.freeze
+    @node_filters = node_filters.flatten.freeze || []
     validate_filters(@node_filters, HTMLPipeline::NodeFilter)
 
     @convert_filter = convert_filter
@@ -151,33 +151,46 @@ def call(text, context: {}, result: {})
     context = context.freeze
     result ||= {}
 
-    payload = default_payload({
-      text_filters: @text_filters.map(&:name),
-      context: context,
-      result: result,
-    })
-    instrument("call_text_filters.html_pipeline", payload) do
-      result[:output] =
-        @text_filters.inject(text) do |doc, filter|
-          perform_filter(filter, doc, context: context, result: result)
-        end
+    if @text_filters.any?
+      payload = default_payload({
+        text_filters: @text_filters.map(&:name),
+        context: context,
+        result: result,
+      })
+      instrument("call_text_filters.html_pipeline", payload) do
+        result[:output] =
+          @text_filters.inject(text) do |doc, filter|
+            perform_filter(filter, doc, context: context, result: result)
+          end
+      end
     end
 
-    text = result[:output]
+    text = result[:output] || text
 
-    html = @convert_filter.call(text) unless @convert_filter.nil?
+    html = if @convert_filter.nil?
+      text
+    else
+      instrument("call_convert_filter.html_pipeline", payload) do
+        html = @convert_filter.call(text)
+      end
+    end
 
     unless @node_filters.empty?
-      payload = default_payload({
-        node_filters: @node_filters.map { |f| f.class.name },
-        context: context,
-        result: result,
-      })
       instrument("call_node_filters.html_pipeline", payload) do
         result[:output] = Selma::Rewriter.new(sanitizer: @sanitization_config, handlers: @node_filters).rewrite(html)
+        html = result[:output]
+        payload = default_payload({
+          node_filters: @node_filters.map { |f| f.class.name },
+          context: context,
+          result: result,
+        })
       end
     end
 
+    instrument("html_pipeline.sanitization", payload) do
+      result[:output] = Selma::Rewriter.new(sanitizer: @sanitization_config, handlers: @node_filters).rewrite(html)
+    end
+
     result = result.merge(@node_filters.collect(&:result).reduce({}, :merge))
     @node_filters.each(&:reset!)
 
@@ -195,6 +208,7 @@ def perform_filter(filter, doc, context: {}, result: {})
       context: context,
       result: result,
     })
+
     instrument("call_filter.html_pipeline", payload) do
       filter.call(doc, context: context, result: result)
     end
diff --git a/lib/html_pipeline/version.rb b/lib/html_pipeline/version.rb
index 3d2f1699..21f3f56b 100644
--- a/lib/html_pipeline/version.rb
+++ b/lib/html_pipeline/version.rb
@@ -1,5 +1,5 @@
 # frozen_string_literal: true
 
 class HTMLPipeline
-  VERSION = "3.0.1"
+  VERSION = "3.0.2"
 end


Test patch:
diff --git a/test/html_pipeline/convert_filter/markdown_filter_test.rb b/test/html_pipeline/convert_filter/markdown_filter_test.rb
index e4809e97..49126f5a 100644
--- a/test/html_pipeline/convert_filter/markdown_filter_test.rb
+++ b/test/html_pipeline/convert_filter/markdown_filter_test.rb
@@ -162,4 +162,11 @@ def test_not_convert_newlines_in_lists
       @gfm.call("* foo\n* bar", context: { markdown: options }),
     )
   end
+
+  def test_works_without_node_filters
+    markdown = "1. Foo\n2. Bar"
+    result = HTMLPipeline.new(convert_filter: HTMLPipeline::ConvertFilter::MarkdownFilter.new).call(markdown)[:output]
+
+    assert_equal("<ol>\n<li>Foo</li>\n<li>Bar</li>\n</ol>", result)
+  end
 end