From c2fed89b7cf44c4e4b6454211c5d2f054715d34f Mon Sep 17 00:00:00 2001 From: Tharindu Abeydeera Date: Wed, 23 Nov 2022 13:28:35 +0530 Subject: [PATCH 1/2] Performace improvement recommendation to use map_join --- lib/surface/components/markdown.ex | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/surface/components/markdown.ex b/lib/surface/components/markdown.ex index f3b5fc1..0fdc6c2 100644 --- a/lib/surface/components/markdown.ex +++ b/lib/surface/components/markdown.ex @@ -69,8 +69,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) _ -> "" From f972a035ea474da34637b8efa11f66605f3bbd82 Mon Sep 17 00:00:00 2001 From: Tharindu Abeydeera Date: Mon, 28 Nov 2022 12:06:16 +0530 Subject: [PATCH 2/2] Option to read markdown from a file when needed --- lib/surface/components/markdown.ex | 12 ++++ mix.lock | 12 ++-- priv/BASIC.md | 3 + priv/EARMARK_OPTS.md | 3 + priv/QUOTED.md | 7 ++ test/components/markdown_test.exs | 110 +++++++++++++++++++++++++++++ 6 files changed, 142 insertions(+), 5 deletions(-) create mode 100644 priv/BASIC.md create mode 100644 priv/EARMARK_OPTS.md create mode 100644 priv/QUOTED.md diff --git a/lib/surface/components/markdown.ex b/lib/surface/components/markdown.ex index 0fdc6c2..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 @@ -82,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 + ``` + + """ + 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