Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,8 @@ jobs:
ruby-version: ${{ matrix.ruby-version }}
bundler-cache: true

- name: Run rubocop
run: bundle exec rubocop

- name: Run tests
run: bundle exec rspec
5 changes: 5 additions & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
require:
- standard

inherit_gem:
standard: config/base.yml
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ gemspec
group :development, :test do
gem "rspec", "~> 3.12"
gem "rake", "~> 13.0"
gem "standard", "~> 1.54"
end
20 changes: 10 additions & 10 deletions gfm_to_blockkit.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,21 @@
require_relative "lib/gfm_to_blockkit/version"

Gem::Specification.new do |spec|
spec.name = "gfm_to_blockkit"
spec.version = GfmToBlockkit::VERSION
spec.authors = ["Wistia"]
spec.email = ["dev@wistia.com"]
spec.name = "gfm_to_blockkit"
spec.version = GfmToBlockkit::VERSION
spec.authors = ["Wistia"]
spec.email = ["dev@wistia.com"]

spec.summary = "Convert GitHub-Flavored Markdown to Slack Block Kit blocks"
spec.description = "AST-based converter from GitHub-Flavored Markdown (GFM) to Slack Block Kit block arrays, using commonmarker for parsing. Produces structured Block Kit JSON ready for the Slack API."
spec.homepage = "https://github.com/wistia/gfm_to_blockkit"
spec.license = "MIT"
spec.summary = "Convert GitHub-Flavored Markdown to Slack Block Kit blocks"
spec.description = "AST-based converter from GitHub-Flavored Markdown (GFM) to Slack Block Kit block arrays, using commonmarker for parsing. Produces structured Block Kit JSON ready for the Slack API."
spec.homepage = "https://github.com/wistia/gfm_to_blockkit"
spec.license = "MIT"

spec.required_ruby_version = ">= 3.2"

spec.metadata["homepage_uri"] = spec.homepage
spec.metadata["homepage_uri"] = spec.homepage
spec.metadata["source_code_uri"] = spec.homepage
spec.metadata["changelog_uri"] = "#{spec.homepage}/blob/main/CHANGELOG.md"
spec.metadata["changelog_uri"] = "#{spec.homepage}/blob/main/CHANGELOG.md"

spec.files = Dir["lib/**/*.rb", "LICENSE.txt", "README.md", "CHANGELOG.md"]
spec.require_paths = ["lib"]
Expand Down
2 changes: 1 addition & 1 deletion lib/gfm_to_blockkit/converter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ def convert_unknown(node)
text = @context.mrkdwn_renderer.render(node)
return [] if text.strip.empty?
TextSplitter.split(text).map do |chunk|
{ type: "section", text: { type: "mrkdwn", text: chunk } }
{type: "section", text: {type: "mrkdwn", text: chunk}}
end
end
end
Expand Down
4 changes: 2 additions & 2 deletions lib/gfm_to_blockkit/converters/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ def render_child_as_elements(node)
when :paragraph
rich_text_renderer.render(node)
when :code_block
[{ type: "text", text: node.string_content.chomp, style: { code: true } }]
[{type: "text", text: node.string_content.chomp, style: {code: true}}]
when :heading
[{ type: "text", text: plain_text_from(node), style: { bold: true } }]
[{type: "text", text: plain_text_from(node), style: {bold: true}}]
else
rendered = rich_text_renderer.render(node)
rendered.empty? ? nil : rendered
Expand Down
16 changes: 8 additions & 8 deletions lib/gfm_to_blockkit/converters/block_quote.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@ def convert(node)
# Remove trailing newline element
elements.pop if trailing_newline?(elements.last)

[{ type: "rich_text", elements: [{ type: "rich_text_quote", elements: elements }] }]
[{type: "rich_text", elements: [{type: "rich_text_quote", elements: elements}]}]
end

# Public so List converter can render blockquotes inside list items
# without reaching into private methods.
def collect_elements(node, depth = 0)
elements = []
prefix = depth > 0 ? "> " * depth : nil
prefix = (depth > 0) ? "> " * depth : nil

node.each do |child|
case child.type
Expand All @@ -39,9 +39,9 @@ def collect_elements(node, depth = 0)
private

def append_with_prefix(elements, rendered, prefix)
elements << { type: "text", text: prefix } if prefix
elements << {type: "text", text: prefix} if prefix
elements.concat(rendered)
elements << { type: "text", text: "\n" }
elements << {type: "text", text: "\n"}
end

def trailing_newline?(element)
Expand All @@ -54,20 +54,20 @@ def render_list_as_text(list_node, indent: 0)
prefix_indent = " " * indent

list_node.each do |item|
bullet = list_node.list_type == :ordered ? "#{counter}. " : "\u2022 "
elements << { type: "text", text: "#{prefix_indent}#{bullet}" }
bullet = (list_node.list_type == :ordered) ? "#{counter}. " : "\u2022 "
elements << {type: "text", text: "#{prefix_indent}#{bullet}"}

item.each do |child|
case child.type
when :paragraph
elements.concat(rich_text_renderer.render(child))
when :list
elements << { type: "text", text: "\n" }
elements << {type: "text", text: "\n"}
elements.concat(render_list_as_text(child, indent: indent + 1))
end
end

elements << { type: "text", text: "\n" }
elements << {type: "text", text: "\n"}
counter += 1
end

Expand Down
4 changes: 2 additions & 2 deletions lib/gfm_to_blockkit/converters/code_block.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ def convert(node)

preformatted = {
type: "rich_text_preformatted",
elements: [{ type: "text", text: content }],
elements: [{type: "text", text: content}],
border: 0
}

Expand All @@ -21,7 +21,7 @@ def convert(node)
preformatted[:language] = lang unless lang.empty?
end

[{ type: "rich_text", elements: [preformatted] }]
[{type: "rich_text", elements: [preformatted]}]
end
end
end
Expand Down
6 changes: 5 additions & 1 deletion lib/gfm_to_blockkit/converters/footnote_definition.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@ class FootnoteDefinition < Base
handles :footnote_definition

def convert(node)
html = node.to_html rescue ""
html = begin
node.to_html
rescue
""
end
label = html[/id="fn-(\w+)"/, 1] || "?"

content_parts = []
Expand Down
2 changes: 1 addition & 1 deletion lib/gfm_to_blockkit/converters/heading.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def convert(node)

[{
type: "header",
text: { type: "plain_text", text: text, emoji: true }
text: {type: "plain_text", text: text, emoji: true}
}]
end
end
Expand Down
8 changes: 6 additions & 2 deletions lib/gfm_to_blockkit/converters/html_block.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,18 @@ class HtmlBlock < Base
handles :html_block

def convert(node)
content = (node.to_commonmark rescue "").chomp
content = begin
node.to_commonmark
rescue
""
end.chomp
return [] if content.strip.empty?

[{
type: "rich_text",
elements: [{
type: "rich_text_preformatted",
elements: [{ type: "text", text: content }],
elements: [{type: "text", text: content}],
border: 0
}]
}]
Expand Down
2 changes: 1 addition & 1 deletion lib/gfm_to_blockkit/converters/image.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def convert_node(node)

title = node.title
if title && !title.strip.empty?
block[:title] = { type: "plain_text", text: title }
block[:title] = {type: "plain_text", text: title}
end

[block]
Expand Down
22 changes: 13 additions & 9 deletions lib/gfm_to_blockkit/converters/list.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ def convert(node)
return [] if list_elements.empty?

merged = merge_adjacent(list_elements)
[{ type: "rich_text", elements: merged }]
[{type: "rich_text", elements: merged}]
end

private

def collect_items(list_node, indent:)
style = list_node.list_type == :ordered ? "ordered" : "bullet"
style = (list_node.list_type == :ordered) ? "ordered" : "bullet"
offset = (style == "ordered" && list_node.list_start > 1) ? list_node.list_start - 1 : nil
elements = []

Expand All @@ -38,7 +38,7 @@ def collect_items(list_node, indent:)
type: "rich_text_list",
style: style,
indent: indent,
elements: [{ type: "rich_text_section", elements: section_elements }]
elements: [{type: "rich_text_section", elements: section_elements}]
}
list_el[:offset] = offset if offset
elements << list_el
Expand Down Expand Up @@ -66,8 +66,8 @@ def render_task_paragraph(child, item_node)
inline_elements = rich_text_renderer.render(child)
checked = task_item_checked?(item_node)
emoji_name = checked ? "white_check_mark" : "white_large_square"
inline_elements.unshift({ type: "text", text: " " })
inline_elements.unshift({ type: "emoji", name: emoji_name })
inline_elements.unshift({type: "text", text: " "})
inline_elements.unshift({type: "emoji", name: emoji_name})
inline_elements
end

Expand All @@ -76,9 +76,9 @@ def merge_adjacent(elements)
elements.each do |el|
prev = merged.last
if prev &&
prev[:type] == "rich_text_list" &&
prev[:style] == el[:style] &&
prev[:indent] == el[:indent]
prev[:type] == "rich_text_list" &&
prev[:style] == el[:style] &&
prev[:indent] == el[:indent]
prev[:elements].concat(el[:elements])
else
merged << el.dup
Expand All @@ -88,7 +88,11 @@ def merge_adjacent(elements)
end

def task_item_checked?(item_node)
html = item_node.to_html rescue ""
html = begin
item_node.to_html
rescue
""
end
html.include?("checked")
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/gfm_to_blockkit/converters/paragraph.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def convert(node)
return [] if text.strip.empty?

TextSplitter.split(text).map do |chunk|
{ type: "section", text: { type: "mrkdwn", text: chunk } }
{type: "section", text: {type: "mrkdwn", text: chunk}}
end
end

Expand Down
4 changes: 2 additions & 2 deletions lib/gfm_to_blockkit/converters/table/native.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@ def convert(node)
body = rows[1..] || []

columns = header.map do |cell|
{ type: "text", text: { type: "plain_text", text: cell } }
{type: "text", text: {type: "plain_text", text: cell}}
end

table_rows = body.map do |row|
{
type: "table_row",
cells: row.map { |cell| { type: "plain_text", text: cell } }
cells: row.map { |cell| {type: "plain_text", text: cell} }
}
end

Expand Down
24 changes: 14 additions & 10 deletions lib/gfm_to_blockkit/converters/table/preformatted.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def convert(node)
type: "rich_text",
elements: [{
type: "rich_text_preformatted",
elements: [{ type: "text", text: ascii }],
elements: [{type: "text", text: ascii}],
border: 0
}]
}]
Expand Down Expand Up @@ -56,11 +56,15 @@ def extract_cell_text(cell_node)
end

def extract_alignments(node, col_count)
html = node.to_html rescue ""
html = begin
node.to_html
rescue
""
end
aligns = html.scan(/<th[^>]*?(?:align="(\w+)")?[^>]*>/).map do |match|
case match[0]
when "center" then :center
when "right" then :right
when "right" then :right
else :left
end
end
Expand Down Expand Up @@ -88,27 +92,27 @@ def format_row(cells, widths, alignments)
cell = cells[i] || ""
align_cell(cell, width, alignments[i] || :left)
end
"| #{formatted.join(' | ')} |"
"| #{formatted.join(" | ")} |"
end

def separator_row(widths, alignments)
parts = widths.each_with_index.map do |w, i|
dashes = "-" * [w, 1].max
case alignments[i]
when :left then ":#{dashes}-"
when :right then "-#{dashes}:"
when :left then ":#{dashes}-"
when :right then "-#{dashes}:"
when :center then ":#{dashes}:"
else "-#{dashes}-"
else "-#{dashes}-"
end
end
"|#{parts.join('|')}|"
"|#{parts.join("|")}|"
end

def align_cell(text, width, alignment)
case alignment
when :right then text.rjust(width)
when :right then text.rjust(width)
when :center then text.center(width)
else text.ljust(width)
else text.ljust(width)
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/gfm_to_blockkit/converters/thematic_break.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ class ThematicBreak < Base
handles :thematic_break

def convert(_node)
[{ type: "divider" }]
[{type: "divider"}]
end
end
end
Expand Down
26 changes: 15 additions & 11 deletions lib/gfm_to_blockkit/renderers/mrkdwn.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,16 @@ def render(node)

def render_inline(node)
case node.type
when :text then escape_mrkdwn(node.string_content)
when :emph then "_#{render_children(node)}_"
when :strong then "*#{render_children(node)}*"
when :strikethrough then "~#{render_children(node)}~"
when :code then "`#{node.string_content}`"
when :link then render_link(node)
when :image then render_image(node)
when :softbreak then "\n"
when :linebreak then "\n"
when :html_inline then ""
when :text then escape_mrkdwn(node.string_content)
when :emph then "_#{render_children(node)}_"
when :strong then "*#{render_children(node)}*"
when :strikethrough then "~#{render_children(node)}~"
when :code then "`#{node.string_content}`"
when :link then render_link(node)
when :image then render_image(node)
when :softbreak then "\n"
when :linebreak then "\n"
when :html_inline then ""
when :footnote_reference then render_footnote_reference(node)
else
render_children(node)
Expand Down Expand Up @@ -53,7 +53,11 @@ def render_image(node)
end

def render_footnote_reference(node)
html = node.to_html rescue ""
html = begin
node.to_html
rescue
""
end
num = html[/fnref-(\w+)/, 1] || "?"
"[^#{num}]"
end
Expand Down
Loading
Loading