diff --git a/lib/surface/components/markdown.ex b/lib/surface/components/markdown.ex
index f3b5fc1..b91a198 100644
--- a/lib/surface/components/markdown.ex
+++ b/lib/surface/components/markdown.ex
@@ -13,6 +13,9 @@ defmodule Surface.Components.Markdown do
@doc "Removes the wrapping `
`, if `true`"
prop unwrap, :boolean, static: true, default: false
+ @doc "Provides the file name that will be used to obtain markdown content"
+ prop from_file, :string, default: nil
+
@doc """
Keyword list with options to be passed down to `Earmark.as_html/2`.
@@ -31,6 +34,9 @@ defmodule Surface.Components.Markdown do
unwrap = static_props[:unwrap]
class = AST.find_attribute_value(attributes, :class) || get_config(:default_class) || ""
+ filename = AST.find_attribute_value(attributes, :from_file)
+
+ content = if filename, do: File.read!(filename.value), else: content
config_opts =
case get_config(:default_opts) do
@@ -69,8 +75,7 @@ defmodule Surface.Components.Markdown do
[space] = Regex.run(~r/^\s*/, first)
lines
- |> Enum.map(fn line -> String.replace_prefix(line, space, "") end)
- |> Enum.join("\n")
+ |> Enum.map_join("\n", fn line -> String.replace_prefix(line, space, "") end)
_ ->
""
@@ -83,6 +88,12 @@ defmodule Surface.Components.Markdown do
|> handle_result!(caller, tag_line)
end
+ defp markdown_from_as_html(filename, caller, tag_line, opts) do
+ filename
+ |> Earmark.from_file!(struct(Earmark.Options, opts))
+ |> handle_result!(caller, tag_line)
+ end
+
defp handle_result!({_, html, messages}, caller, tag_line) do
{errors, warnings_and_deprecations} =
Enum.split_with(messages, fn {type, _line, _message} -> type == :error end)
diff --git a/mix.lock b/mix.lock
index 22834c5..4669be6 100644
--- a/mix.lock
+++ b/mix.lock
@@ -1,4 +1,5 @@
%{
+ "castore": {:hex, :castore, "0.1.19", "a2c3e46d62b7f3aa2e6f88541c21d7400381e53704394462b9fd4f06f6d42bb6", [:mix], [], "hexpm", "e96e0161a5dc82ef441da24d5fa74aefc40d920f3a6645d15e1f9f3e66bb2109"},
"earmark": {:hex, :earmark, "1.4.27", "b413b0379043df51475a9b22ce344e8a58a117516c735b8871e6cdd5ed0f0153", [:mix], [{:earmark_parser, "~> 1.4.26", [hex: :earmark_parser, repo: "hexpm", optional: false]}], "hexpm", "579ebe2eaf4c7e040815a73a268036bcd96e6aab8ad2b1fcd979aaeb1ea47e15"},
"earmark_parser": {:hex, :earmark_parser, "1.4.26", "f4291134583f373c7d8755566122908eb9662df4c4b63caa66a0eabe06569b0a", [:mix], [], "hexpm", "48d460899f8a0c52c5470676611c01f64f3337bad0b26ddab43648428d94aabc"},
"ex_doc": {:hex, :ex_doc, "0.28.5", "3e52a6d2130ce74d096859e477b97080c156d0926701c13870a4e1f752363279", [:mix], [{:earmark_parser, "~> 1.4.19", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "d2c4b07133113e9aa3e9ba27efb9088ba900e9e51caa383919676afdf09ab181"},
@@ -10,14 +11,15 @@
"makeup_erlang": {:hex, :makeup_erlang, "0.1.1", "3fcb7f09eb9d98dc4d208f49cc955a34218fc41ff6b84df7c75b3e6e533cc65f", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "174d0809e98a4ef0b3309256cbf97101c6ec01c4ab0b23e926a9e17df2077cbb"},
"mime": {:hex, :mime, "2.0.3", "3676436d3d1f7b81b5a2d2bd8405f412c677558c81b1c92be58c00562bb59095", [:mix], [], "hexpm", "27a30bf0db44d25eecba73755acf4068cbfe26a4372f9eb3e4ea3a45956bff6b"},
"nimble_parsec": {:hex, :nimble_parsec, "1.2.3", "244836e6e3f1200c7f30cb56733fd808744eca61fd182f731eac4af635cc6d0b", [:mix], [], "hexpm", "c8d789e39b9131acf7b99291e93dae60ab48ef14a7ee9d58c6964f59efb570b0"},
- "phoenix": {:hex, :phoenix, "1.6.12", "f8f8ac077600f84419806dd53114b2e77aedde7a502e74181a7d886355aa0643", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.0", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 1.0", [hex: :phoenix_view, repo: "hexpm", optional: false]}, {:plug, "~> 1.10", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.2", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "2d6cf5583c9c20f7103c40e6014ef802d96553b8e5d6585ad6e627bd5ddb0d12"},
+ "phoenix": {:hex, :phoenix, "1.6.15", "0a1d96bbc10747fd83525370d691953cdb6f3ccbac61aa01b4acb012474b047d", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.0", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 1.0 or ~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: false]}, {:plug, "~> 1.10", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.2", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "d70ab9fbf6b394755ea88b644d34d79d8b146e490973151f248cacd122d20672"},
"phoenix_html": {:hex, :phoenix_html, "3.2.0", "1c1219d4b6cb22ac72f12f73dc5fad6c7563104d083f711c3fcd8551a1f4ae11", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "36ec97ba56d25c0136ef1992c37957e4246b649d620958a1f9fa86165f8bc54f"},
- "phoenix_live_view": {:hex, :phoenix_live_view, "0.18.0", "8705283efbc623df6290d5f8cb233afa9bcdcfc969749ce6e313877108f65887", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6 or ~> 1.7", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.1", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "545f11c15d595595690da16c4f607417bfb1862e518c07c9f78c754ac186cd7d"},
+ "phoenix_live_view": {:hex, :phoenix_live_view, "0.18.3", "2e3d009422addf8b15c3dccc65ce53baccbe26f7cfd21d264680b5867789a9c1", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6.15 or ~> 1.7.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.1", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c8845177a866e017dcb7083365393c8f00ab061b8b6b2bda575891079dce81b2"},
"phoenix_pubsub": {:hex, :phoenix_pubsub, "2.1.1", "ba04e489ef03763bf28a17eb2eaddc2c20c6d217e2150a61e3298b0f4c2012b5", [:mix], [], "hexpm", "81367c6d1eea5878ad726be80808eb5a787a23dee699f96e72b1109c57cdd8d9"},
- "phoenix_view": {:hex, :phoenix_view, "1.1.2", "1b82764a065fb41051637872c7bd07ed2fdb6f5c3bd89684d4dca6e10115c95a", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "7ae90ad27b09091266f6adbb61e1d2516a7c3d7062c6789d46a7554ec40f3a56"},
- "plug": {:hex, :plug, "1.13.6", "187beb6b67c6cec50503e940f0434ea4692b19384d47e5fdfd701e93cadb4cc2", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "02b9c6b9955bce92c829f31d6284bf53c591ca63c4fb9ff81dfd0418667a34ff"},
+ "phoenix_template": {:hex, :phoenix_template, "1.0.0", "c57bc5044f25f007dc86ab21895688c098a9f846a8dda6bc40e2d0ddc146e38f", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "1b066f99a26fd22064c12b2600a9a6e56700f591bf7b20b418054ea38b4d4357"},
+ "phoenix_view": {:hex, :phoenix_view, "2.0.2", "6bd4d2fd595ef80d33b439ede6a19326b78f0f1d8d62b9a318e3d9c1af351098", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}], "hexpm", "a929e7230ea5c7ee0e149ffcf44ce7cf7f4b6d2bfe1752dd7c084cdff152d36f"},
+ "plug": {:hex, :plug, "1.14.0", "ba4f558468f69cbd9f6b356d25443d0b796fbdc887e03fa89001384a9cac638f", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "bf020432c7d4feb7b3af16a0c2701455cbbbb95e5b6866132cb09eb0c29adc14"},
"plug_crypto": {:hex, :plug_crypto, "1.2.3", "8f77d13aeb32bfd9e654cb68f0af517b371fb34c56c9f2b58fe3df1235c1251a", [:mix], [], "hexpm", "b5672099c6ad5c202c45f5a403f21a3411247f164e4a8fab056e5cd8a290f4a2"},
"sourceror": {:hex, :sourceror, "0.11.2", "549ce48be666421ac60cfb7f59c8752e0d393baa0b14d06271d3f6a8c1b027ab", [:mix], [], "hexpm", "9ab659118896a36be6eec68ff7b0674cba372fc8e210b1e9dc8cf2b55bb70dfb"},
- "surface": {:hex, :surface, "0.9.0", "834c2c36d1a5538cd55649cb73e5c28086e2dd87424b20b4e253ad532ad00a36", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.18.0", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:sourceror, "~> 0.11", [hex: :sourceror, repo: "hexpm", optional: false]}], "hexpm", "49bbf3ad2bde53cd45b335175166d7e36faed5369218004bf8a5fb00bf63d9e4"},
+ "surface": {:hex, :surface, "0.9.1", "6a343564b1d6c17c619ac933cec5680ffe8c68f0cd2d85f780b70f6607750a96", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.18.0", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:sourceror, "~> 0.11", [hex: :sourceror, repo: "hexpm", optional: false]}], "hexpm", "131312d35d190739d0e0f1681acb9fce6962d3f81439011a3331bad2976ca372"},
"telemetry": {:hex, :telemetry, "1.1.0", "a589817034a27eab11144ad24d5c0f9fab1f58173274b1e9bae7074af9cbee51", [:rebar3], [], "hexpm", "b727b2a1f75614774cff2d7565b64d0dfa5bd52ba517f16543e6fc7efcc0df48"},
}
diff --git a/priv/BASIC.md b/priv/BASIC.md
new file mode 100644
index 0000000..8dd2841
--- /dev/null
+++ b/priv/BASIC.md
@@ -0,0 +1,3 @@
+# Head 1
+Bold: **bold**
+Code: `code`
diff --git a/priv/EARMARK_OPTS.md b/priv/EARMARK_OPTS.md
new file mode 100644
index 0000000..a99a270
--- /dev/null
+++ b/priv/EARMARK_OPTS.md
@@ -0,0 +1,3 @@
+```elixir
+code
+```
diff --git a/priv/QUOTED.md b/priv/QUOTED.md
new file mode 100644
index 0000000..27dffb1
--- /dev/null
+++ b/priv/QUOTED.md
@@ -0,0 +1,7 @@
+```elixir
+def render(assigns) do
+ ~F"\""
+ Hello
+ "\""
+end
+```
diff --git a/test/components/markdown_test.exs b/test/components/markdown_test.exs
index 6378172..37d16fc 100644
--- a/test/components/markdown_test.exs
+++ b/test/components/markdown_test.exs
@@ -117,6 +117,116 @@ defmodule Surface.Components.MarkdownTest do
code
"""
end
+
+ test "translate markdown from a file to HTML" do
+ html =
+ render_surface do
+ ~F"""
+ <#Markdown from_file="./priv/BASIC.md" />
+ """
+ end
+
+ assert html =~ """
+
\
+
+ Head 1
+
+ Bold: bold
+ Code: code
+
+ """
+ end
+
+ test "setting the class on markdown received from a file" do
+ html =
+ render_surface do
+ ~F"""
+ <#Markdown class="markdown" from_file="./priv/BASIC.md" />
+ """
+ end
+
+ assert html =~ """
+
\
+
+ Head 1
+
+ Bold: bold
+ Code: code
+
+ """
+ end
+
+ test "setting multiple classes on markdown received from a file" do
+ html =
+ render_surface do
+ ~F"""
+ <#Markdown class="markdown small" from_file="./priv/BASIC.md" />
+ """
+ end
+
+ assert html =~ """
+
\
+
+ Head 1
+
+ Bold: bold
+ Code: code
+
+ """
+ end
+
+ test "setting unwrap removes the wrapping
of markdown received from a file" do
+ html =
+ render_surface do
+ ~F"""
+ <#Markdown unwrap from_file="./priv/BASIC.md" />
+ """
+ end
+
+ assert html == """
+
+ Head 1
+
+ Bold: bold
+ Code: code
+ """
+ end
+
+ test "translates escaped three double-quotes of markdown received from a file" do
+ html =
+ render_surface do
+ ~F"""
+ <#Markdown from_file="./priv/QUOTED.md">
+ ```elixir
+ def render(assigns) do
+ ~F"\""
+ Hello
+ "\""
+ end
+ ```
+ #Markdown>
+ """
+ end
+
+ assert html =~ """
+ ~F"""
+ Hello
+ """
+ """
+ end
+
+ test "setting opts forward options to Earmark on markdown received from a file" do
+ html =
+ render_surface do
+ ~F"""
+ <#Markdown opts={code_class_prefix: "language-"} from_file="./priv/EARMARK_OPTS.md" />
+ """
+ end
+
+ assert html =~ """
+
code
+ """
+ end
end
defmodule Surface.Components.MarkdownSyncTest do