From d3996467b2e81faa9725a5f84407695e5245a59b Mon Sep 17 00:00:00 2001 From: Ross Kaffenberger Date: Fri, 3 May 2024 22:28:25 -0400 Subject: [PATCH] Reimplement phlex-markdown as markdown base component --- Gemfile | 2 +- Gemfile.lock | 10 +- app/views/components/markdown/base.rb | 93 ++++++++++++++++- config/application_wasm.rb | 1 - spec/views/components/markdown/base_spec.rb | 109 ++++++++++++++++++++ 5 files changed, 201 insertions(+), 14 deletions(-) create mode 100644 spec/views/components/markdown/base_spec.rb diff --git a/Gemfile b/Gemfile index 6c7fbed5..1ed6cfa4 100644 --- a/Gemfile +++ b/Gemfile @@ -32,7 +32,7 @@ gem "inline_svg" # Embed SVGs in Rails views and style them with CSS [https://gi gem "rouge", group: [:default, :wasm] # Pure Ruby syntaix highlighter [https://github.com/rouge-ruby/rouge gem "sitepress-rails", group: [:default, :wasm] # Static site generator for Rails [https://sitepress.cc/getting-started/rails] gem "phlex-rails", group: [:default, :wasm] # An object-oriented alternative to ActionView for Ruby on Rails. [https://github.com/phlex-ruby/phlex-rails] -gem "phlex-markdown", github: "phlex-ruby/phlex-markdown", group: [:default, :wasm] # A markdown component for Phlex [https://github.com/phlex-ruby/phlex-markdown] +gem "markly" gem "bootsnap", require: false # Reduces boot times through caching; required in config/boot.rb [https://github.com/Shopify/bootsnap] diff --git a/Gemfile.lock b/Gemfile.lock index 401eeb79..9cbcaf32 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,11 +1,3 @@ -GIT - remote: https://github.com/phlex-ruby/phlex-markdown.git - revision: ee1e2763fc842563577003a269982cfc4ac79006 - specs: - phlex-markdown (0.3.0) - markly (~> 0.7) - phlex (>= 0.5) - GIT remote: https://github.com/rubycdp/cuprite revision: 9e2788fac70a9d250364ced47a50b77e18fe31a5 @@ -542,8 +534,8 @@ DEPENDENCIES letter_opener litestream mail_interceptor + markly mission_control-jobs - phlex-markdown! phlex-rails puma (>= 5.0) rack-mini-profiler diff --git a/app/views/components/markdown/base.rb b/app/views/components/markdown/base.rb index 6bbfc372..6bcca4c4 100644 --- a/app/views/components/markdown/base.rb +++ b/app/views/components/markdown/base.rb @@ -1,12 +1,99 @@ -class Markdown::Base < Phlex::Markdown +# frozen_string_literal: true + +require "phlex" +require "markly" + +class Markdown::Base < Phlex::HTML def initialize(content, flags: Markly::DEFAULT) - super(content) + @content = content @flags = flags end - attr_reader :flags + def template + visit(doc) + end + + private def doc Markly.parse(@content, flags: @flags) end + + def visit(node) + return if node.nil? + + case node.type + in :document + visit_children(node) + in :softbreak + whitespace + visit_children(node) + in :text + plain(node.string_content) + in :header + case node.header_level + in 1 then h1 { visit_children(node) } + in 2 then h2 { visit_children(node) } + in 3 then h3 { visit_children(node) } + in 4 then h4 { visit_children(node) } + in 5 then h5 { visit_children(node) } + in 6 then h6 { visit_children(node) } + end + in :paragraph + grandparent = node.parent&.parent + + if grandparent&.type == :list && grandparent&.list_tight + visit_children(node) + else + p { visit_children(node) } + end + in :link + a(href: node.url, title: node.title) { visit_children(node) } + in :image + img( + src: node.url, + alt: node.each.first.string_content, + title: node.title + ) + in :emph + em { visit_children(node) } + in :strong + strong { visit_children(node) } + in :list + case node.list_type + in :ordered_list then ol { visit_children(node) } + in :bullet_list then ul { visit_children(node) } + end + in :list_item + li { visit_children(node) } + in :code + inline_code do |**attributes| + code(**attributes) { plain(node.string_content) } + end + in :code_block + code_block(node.string_content, node.fence_info) do |**attributes| + pre(**attributes) do + code(class: "language-#{node.fence_info}") do + plain(node.string_content) + end + end + end + in :hrule + hr + in :blockquote + blockquote { visit_children(node) } + end + end + + def inline_code(**attributes) + yield(**attributes) + end + + def code_block(code, language, **attributes) + yield(**attributes) + end + + def visit_children(node) + node.each { |c| visit(c) } + end end diff --git a/config/application_wasm.rb b/config/application_wasm.rb index 487e8dbc..284a5b9f 100644 --- a/config/application_wasm.rb +++ b/config/application_wasm.rb @@ -27,7 +27,6 @@ require "flipper" require "sitepress-rails" require "phlex-rails" -require "phlex/markdown" require "inline_svg" module Joy diff --git a/spec/views/components/markdown/base_spec.rb b/spec/views/components/markdown/base_spec.rb new file mode 100644 index 00000000..4222ae8b --- /dev/null +++ b/spec/views/components/markdown/base_spec.rb @@ -0,0 +1,109 @@ +# frozen_string_literal: true + +require "rails_helper" + +RSpec.describe Markdown::Base do + it "supports multiple headings" do + output = md <<~MD + # 1 + ## 2 + ### 3 + #### 4 + ##### 5 + ###### 6 + MD + + expect(output).to be == + "

1

2

3

4

5
6
" + end + + it "supports ordered lists" do + output = md <<~MD + 1. One + 2. Two + 3. Three + MD + + expect(output).to be == + "
  1. One
  2. Two
  3. Three
" + end + + it "supports unordered lists" do + output = md <<~MD + - One + - Two + - Three + MD + + expect(output).to be == + "" + end + + it "supports inline code" do + output = md "Some `code` here" + expect(output).to be == "

Some code here

" + end + + it "supports block code" do + output = md <<~MD + ```ruby + def foo + bar + end + ``` + MD + + expect(output).to be == + %(
def foo\n  bar\nend\n
) + end + + it "supports paragraphs" do + output = md "A\n\nB" + expect(output).to be == "

A

B

" + end + + it "supports links" do + output = md "[Hello](world 'title')" + expect(output).to be == %(

Hello

) + end + + it "supports emphasis" do + output = md "*Hello*" + expect(output).to be == "

Hello

" + end + + it "supports strong" do + output = md "**Hello**" + expect(output).to be == "

Hello

" + end + + it "supports blockquotes" do + output = md "> Hello" + expect(output).to be == "

Hello

" + end + + it "supports horizontal rules" do + output = md "---" + expect(output).to be == "
" + end + + it "supports images" do + output = md "![alt](src 'title')" + expect(output).to be == %(

alt

) + end + + it "supports softbreaks in content as spaces" do + output = md <<~MD + One + Two + + Three + MD + + expect(output).to be == "

One Two

Three

" + end + + def md(content) + Markdown::Base.new(content).call + end +end