I’m looking to provide Markdown support for an app, but I want the default markdown to have custom class names and id placed inline. For example:
<h1 class="fw1 headline red">...</h1>
I’m using the library Earmark for the work.
From reading the docs it looks like I can achieve this goal. This is how I set it up, would like some feedback:
defmodule Portal.Blog.Post
#..omit a lot of code
defp parse_attr(:body, value) do
value
|> Earmark.as_ast()
|> Portal.Blog.Parser.parsing()
|> Earmark.as_html!()
end
end
I have a module that will parse the body of a post
• Take the value from the body of a blog post
• Pass it to Earmark.as_ast
, that returns a list where each item is an HTML node (See Floki)
{tag_name, attributes, children_nodes}
# example
{"p", [{"class", "headline"}], ["Floki"]}
The results from Earmark.as_ast
is an {:ok, results, options} and we pass that
to my custom parser.
Portal.Blog.Parser.parsing() will take the list of results:
• map over each item
• pattern match using parse()
• In this example I’m only interested in p, h3 and h1 tags
• every other tag gets a pass through
• Then after each parse() its passed into Earmark.Transform.transform()
which convert it into a string
• Then take the list of strings and concat into one
defmodule Portal.Blog.Parser do
def parsing({:ok, results, _option}) do
Enum.map(results, fn(item) ->
parse(item)
|> Earmark.Transform.transform()
end)
|> Enum.join("")
end
# This follows a similar structure to Floki library
# link here
# {tag_name, attributes, children_nodes}
def parse({"p", _attributes, children_nodes }) do
{"p", [{"class", "fw5 blue"}], children_nodes }
end
def parse({"h3", _attributes, children_nodes }) do
{"h3", [{"class", "f1 fw1 lh-copy"}], children_nodes }
end
def parse({"h1", _attributes, children_nodes }) do
{"h1", [{"class", "fw6"}], children_nodes }
end
def parse({"blockquote", _attributes, children_nodes }) do
{"blockquote", [{"class", "bg-silver"}], children_nodes }
end
def parse(item) do
item
end
end
Back to my defmodule Portal.Blog.Post:
We then pass the results of Portal.Blog.Parser.parsing()
to Earmark.as_html!()
|> Portal.Blog.Parser.parsing()
|> Earmark.as_html!()
Everything works.
And I think this is how to solve it.
My h1, h3 and p tags all have the correct class names.
The only issue I get is warnings like this:
<no file>:14: warning: Failed to find closing <pre>
<no file>:29: warning: Failed to find closing <pre>
<no file>:1: warning: Failed to find closing <p>
<no file>:15: warning: Failed to find closing <pre>
<no file>:1: warning: Failed to find closing <p>
Looking for feedback if this is how someone would approach this, or is there a better way of solving this.
Thanks