Module: MarkdownComposer::MarkdownRenderer

Defined in:
lib/markdown_composer/markdown_renderer.rb

Class Method Summary collapse

Class Method Details



18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/markdown_composer/markdown_renderer.rb', line 18

def apply_link_modes(html, modes, diagnostics: Diagnostics.new)
  modes = Array(modes).map(&:to_s)
  return html if html.to_s.empty? || modes.empty?

  require "nokogiri"
  fragment = Nokogiri::HTML5.fragment(html)
  fragment.css("a[href]").each do |link|
    href = link["href"].to_s
    next if href.empty? || href.start_with?("#")

    merge_rel(link, "nofollow") if modes.include?("nofollow")
    if modes.include?("target_blank")
      link["target"] = "_blank"
      merge_rel(link, "noopener")
      merge_rel(link, "noreferrer")
    end
  end
  fragment.to_html
rescue LoadError, StandardError => e
  diagnostics.warn("transform.html_link_postprocess_failed", "HTML link transform skipped: #{e.message}", path: "transform")
  html
end

.escape(text) ⇒ Object



91
92
93
# File 'lib/markdown_composer/markdown_renderer.rb', line 91

def escape(text)
  text.gsub("&", "&amp;").gsub("<", "&lt;").gsub(">", "&gt;")
end

.escape_attribute(text) ⇒ Object



95
96
97
# File 'lib/markdown_composer/markdown_renderer.rb', line 95

def escape_attribute(text)
  escape(text.to_s).gsub('"', "&quot;")
end

.fallback_html(markdown) ⇒ Object



49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/markdown_composer/markdown_renderer.rb', line 49

def fallback_html(markdown)
  html = []
  list_items = []
  markdown.lines.each do |line|
    case line
    when /\A\s*[-*+]\s+(.+)/
      list_items << "<li>#{render_inline(Regexp.last_match(1).strip)}</li>"
    when /\A(\#{1,6})\s+(.+)/
      flush_list(html, list_items)
      level = Regexp.last_match(1).length
      html << "<h#{level}>#{render_inline(Regexp.last_match(2).strip)}</h#{level}>"
    when /\A\s*$/
      flush_list(html, list_items)
    else
      flush_list(html, list_items)
      html << "<p>#{render_inline(line.strip)}</p>"
    end
  end
  flush_list(html, list_items)
  html.join("\n")
end

.flush_list(html, list_items) ⇒ Object



84
85
86
87
88
89
# File 'lib/markdown_composer/markdown_renderer.rb', line 84

def flush_list(html, list_items)
  return if list_items.empty?

  html << "<ul>#{list_items.join}</ul>"
  list_items.clear
end

.merge_rel(link, value) ⇒ Object



99
100
101
102
103
# File 'lib/markdown_composer/markdown_renderer.rb', line 99

def merge_rel(link, value)
  rel_values = link["rel"].to_s.split(/\s+/).reject(&:empty?)
  rel_values << value unless rel_values.include?(value)
  link["rel"] = rel_values.join(" ")
end

.render_inline(text) ⇒ Object



71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/markdown_composer/markdown_renderer.rb', line 71

def render_inline(text)
  rendered = +""
  last_index = 0
  text.to_s.to_enum(:scan, /(?<!!)\[([^\]]+)\]\(([^)\s]+)\)/).each do
    match = Regexp.last_match
    rendered << escape(text.to_s[last_index...match.begin(0)].to_s)
    rendered << "<a href=\"#{escape_attribute(match[2])}\">#{escape(match[1])}</a>"
    last_index = match.end(0)
  end
  rendered << escape(text.to_s[last_index..].to_s)
  rendered
end

.render_with_commonmarker(markdown) ⇒ Object



41
42
43
44
45
46
47
# File 'lib/markdown_composer/markdown_renderer.rb', line 41

def render_with_commonmarker(markdown)
  return CommonMarker.render_html(markdown) if defined?(CommonMarker) && CommonMarker.respond_to?(:render_html)
  return Commonmarker.to_html(markdown) if defined?(Commonmarker) && Commonmarker.respond_to?(:to_html)
  return CommonMarker.render_doc(markdown).to_html if defined?(CommonMarker) && CommonMarker.respond_to?(:render_doc)

  raise "unsupported CommonMarker API"
end

.to_html(markdown, diagnostics: Diagnostics.new) ⇒ Object



7
8
9
10
11
12
13
14
15
16
# File 'lib/markdown_composer/markdown_renderer.rb', line 7

def to_html(markdown, diagnostics: Diagnostics.new)
  begin
    require "commonmarker"
    return render_with_commonmarker(markdown)
  rescue LoadError, StandardError
    diagnostics.warn("output.html_fallback", "CommonMarker HTML rendering unavailable; using simple fallback", path: "output")
  end

  fallback_html(markdown)
end