diff --git a/lib/tableau.ex b/lib/tableau.ex index e696dda..8912f41 100644 --- a/lib/tableau.ex +++ b/lib/tableau.ex @@ -10,6 +10,7 @@ defmodule Tableau do * `:converters` - mapping of file extensions to converter module. Defaults to `[md: Tableau.MDExConverter]` * `:markdown` - keyword * `:mdex` - keyword - Options to pass to `MDEx.to_html/2` + * `:plugins` - module list - MDEx plugin modules to attach to the processing pipeline * `:slug` - keyword - Options to pass to `Slug.slugify/2` ### Example @@ -41,7 +42,8 @@ defmodule Tableau do footnotes: true ], render: [unsafe: true], - syntax_highlight: [formatter: {:html_inline, theme: "neovim_dark"}] + syntax_highlight: [formatter: {:html_inline, theme: "neovim_dark"}], + plugins: [MDExGFM] ] ] ``` @@ -61,9 +63,5 @@ defmodule Tableau do Will use the globally configured options, but you can also pass it overrides. """ - def markdown(content, overrides \\ []) do - {:ok, config} = Tableau.Config.get() - - MDEx.to_html!(content, Keyword.merge(config.markdown[:mdex], overrides)) - end + defdelegate markdown(content, overrides \\ []), to: Tableau.MDExConverter end diff --git a/lib/tableau/converters/mdex_converter.ex b/lib/tableau/converters/mdex_converter.ex index 597e773..0f0eb87 100644 --- a/lib/tableau/converters/mdex_converter.ex +++ b/lib/tableau/converters/mdex_converter.ex @@ -1,8 +1,45 @@ defmodule Tableau.MDExConverter do @moduledoc """ - Converter to parse markdown content with `MDEx` + Converter to parse markdown content with `MDEx` with support for MDEx plugins. """ + + @doc """ + Convert markdown content to HTML using `MDEx.to_html!/2`. + + Will use the globally configured options, but you can also pass it overrides. + """ + def markdown(content, overrides \\ []) do + {:ok, config} = Tableau.Config.get() + + {plugins, mdex_config} = resolve_plugins(config, overrides) + + render!(content, mdex_config, plugins) + end + def convert(_filepath, _front_matter, body, %{site: %{config: config}}) do - MDEx.to_html!(body, config.markdown[:mdex]) + {plugins, mdex_config} = resolve_plugins(config) + + render!(body, mdex_config, plugins) + end + + defp resolve_plugins(config, overrides \\ []) do + config.markdown[:mdex] + |> Keyword.merge(overrides, fn + :plugins, left, right -> List.wrap(right) ++ List.wrap(left) + _, _, v -> v + end) + |> Keyword.pop(:plugins, []) + end + + defp render!(content, mdex_config, plugins) do + mdex_config + |> Keyword.put(:markdown, content) + |> MDEx.new() + |> attach_plugins(plugins) + |> MDEx.to_html!() + end + + defp attach_plugins(mdex, plugins) do + Enum.reduce(plugins, mdex, fn mod, mdex -> mod.attach(mdex) end) end end diff --git a/mix.exs b/mix.exs index 3dd4126..8dc5ce9 100644 --- a/mix.exs +++ b/mix.exs @@ -37,7 +37,7 @@ defmodule Tableau.MixProject do {:date_time_parser, "~> 1.2"}, {:html_entities, "~> 0.5.2"}, {:libgraph, "~> 0.16.0"}, - {:mdex, "~> 0.9.0"}, + {:mdex, "~> 0.10.0"}, {:schematic, "~> 0.5.1"}, {:slugify, "~> 1.3"}, {:tz, "~> 0.28.1"}, diff --git a/mix.lock b/mix.lock index 13b0d81..8477d74 100644 --- a/mix.lock +++ b/mix.lock @@ -1,7 +1,7 @@ %{ - "autumn": {:hex, :autumn, "0.5.5", "05cda4e2b79957c8540eb0184f1ac00fba187a6dabd8461e78c40f9fc8417f2d", [:mix], [{:nimble_options, "~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:rustler, "~> 0.29", [hex: :rustler, repo: "hexpm", optional: false]}, {:rustler_precompiled, "~> 0.6", [hex: :rustler_precompiled, repo: "hexpm", optional: false]}], "hexpm", "49e40b50e16fc49dcbf0bd4071b32e5f64755403c8073490aff2a536d442df36"}, + "autumn": {:hex, :autumn, "0.5.7", "f6bfdc30d3f8d5e82ba5648489db7a7b6b7479d7be07a8288d4db2437434e26d", [:mix], [{:nimble_options, "~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:rustler, "~> 0.29", [hex: :rustler, repo: "hexpm", optional: true]}, {:rustler_precompiled, "~> 0.6", [hex: :rustler_precompiled, repo: "hexpm", optional: false]}], "hexpm", "d272bfddeeea863420a8eb994d42af219ca5391191dd765bf045fbacf56a28d1"}, "bandit": {:hex, :bandit, "1.5.7", "6856b1e1df4f2b0cb3df1377eab7891bec2da6a7fd69dc78594ad3e152363a50", [:mix], [{:hpax, "~> 1.0.0", [hex: :hpax, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:thousand_island, "~> 1.0", [hex: :thousand_island, repo: "hexpm", optional: false]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "f2dd92ae87d2cbea2fa9aa1652db157b6cba6c405cb44d4f6dd87abba41371cd"}, - "castore": {:hex, :castore, "1.0.15", "8aa930c890fe18b6fe0a0cff27b27d0d4d231867897bd23ea772dee561f032a3", [:mix], [], "hexpm", "96ce4c69d7d5d7a0761420ef743e2f4096253931a3ba69e5ff8ef1844fe446d3"}, + "castore": {:hex, :castore, "1.0.17", "4f9770d2d45fbd91dcf6bd404cf64e7e58fed04fadda0923dc32acca0badffa2", [:mix], [], "hexpm", "12d24b9d80b910dd3953e165636d68f147a31db945d2dcb9365e441f8b5351e5"}, "date_time_parser": {:hex, :date_time_parser, "1.2.0", "3d5a816b91967f51e0f94dcb16a34b2cb780f22cd48931779e81d72f7d3eadb1", [:mix], [{:kday, "~> 1.0", [hex: :kday, repo: "hexpm", optional: false]}], "hexpm", "0cf09ada9f42c0b3bfba02dc0ea2e4b4d2f543d9d2bf99b831a29e6b4a4160e5"}, "earmark_parser": {:hex, :earmark_parser, "1.4.44", "f20830dd6b5c77afe2b063777ddbbff09f9759396500cdbe7523efd58d7a339c", [:mix], [], "hexpm", "4778ac752b4701a5599215f7030989c989ffdc4f6df457c5f36938cc2d2a2750"}, "ex_doc": {:hex, :ex_doc, "0.37.3", "f7816881a443cd77872b7d6118e8a55f547f49903aef8747dbcb345a75b462f9", [:mix], [{:earmark_parser, "~> 1.4.42", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "e6aebca7156e7c29b5da4daa17f6361205b2ae5f26e5c7d8ca0d3f7e18972233"}, @@ -15,14 +15,14 @@ "makeup": {:hex, :makeup, "1.2.1", "e90ac1c65589ef354378def3ba19d401e739ee7ee06fb47f94c687016e3713d1", [:mix], [{:nimble_parsec, "~> 1.4", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "d36484867b0bae0fea568d10131197a4c2e47056a6fbe84922bf6ba71c8d17ce"}, "makeup_elixir": {:hex, :makeup_elixir, "1.0.1", "e928a4f984e795e41e3abd27bfc09f51db16ab8ba1aebdba2b3a575437efafc2", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "7284900d412a3e5cfd97fdaed4f5ed389b8f2b4cb49efc0eb3bd10e2febf9507"}, "makeup_erlang": {:hex, :makeup_erlang, "1.0.2", "03e1804074b3aa64d5fad7aa64601ed0fb395337b982d9bcf04029d68d51b6a7", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "af33ff7ef368d5893e4a267933e7744e46ce3cf1f61e2dccf53a111ed3aa3727"}, - "mdex": {:hex, :mdex, "0.9.2", "d70e0a9116105733d7999cbbfcb52823b6e6a4b02174205f0d0815cb1ed68dd4", [:mix], [{:autumn, ">= 0.5.4", [hex: :autumn, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:nimble_options, "~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:rustler, "~> 0.32", [hex: :rustler, repo: "hexpm", optional: false]}, {:rustler_precompiled, "~> 0.7", [hex: :rustler_precompiled, repo: "hexpm", optional: false]}], "hexpm", "d5a3371ec939141d971ed500bdbf3a23a22a91df80753356b256ec13dfadfd13"}, + "mdex": {:hex, :mdex, "0.10.0", "eae4d3bd4c0b77d6d959146a2d6faaec045686548ad1468630130095dbd93def", [:mix], [{:autumn, ">= 0.5.4", [hex: :autumn, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:nimble_options, "~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}, {:rustler, "~> 0.32", [hex: :rustler, repo: "hexpm", optional: false]}, {:rustler_precompiled, "~> 0.7", [hex: :rustler_precompiled, repo: "hexpm", optional: false]}], "hexpm", "6ad76e32056c44027fe985da7da506e033b07037896d1f130f7d5c332b0d0ac0"}, "mime": {:hex, :mime, "2.0.6", "8f18486773d9b15f95f4f4f1e39b710045fa1de891fada4516559967276e4dc2", [:mix], [], "hexpm", "c9945363a6b26d747389aac3643f8e0e09d30499a138ad64fe8fd1d13d9b153e"}, "nimble_options": {:hex, :nimble_options, "1.1.1", "e3a492d54d85fc3fd7c5baf411d9d2852922f66e69476317787a7b2bb000a61b", [:mix], [], "hexpm", "821b2470ca9442c4b6984882fe9bb0389371b8ddec4d45a9504f00a66f650b44"}, "nimble_parsec": {:hex, :nimble_parsec, "1.4.2", "8efba0122db06df95bfaa78f791344a89352ba04baedd3849593bfce4d0dc1c6", [:mix], [], "hexpm", "4b21398942dda052b403bbe1da991ccd03a053668d147d53fb8c4e0efe09c973"}, "plug": {:hex, :plug, "1.16.1", "40c74619c12f82736d2214557dedec2e9762029b2438d6d175c5074c933edc9d", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "a13ff6b9006b03d7e33874945b2755253841b238c34071ed85b0e86057f8cddc"}, "plug_crypto": {:hex, :plug_crypto, "2.1.0", "f44309c2b06d249c27c8d3f65cfe08158ade08418cf540fd4f72d4d6863abb7b", [:mix], [], "hexpm", "131216a4b030b8f8ce0f26038bc4421ae60e4bb95c5cf5395e1421437824c4fa"}, "rustler": {:hex, :rustler, "0.37.1", "721434020c7f6f8e1cdc57f44f75c490435b01de96384f8ccb96043f12e8a7e0", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "24547e9b8640cf00e6a2071acb710f3e12ce0346692e45098d84d45cdb54fd79"}, - "rustler_precompiled": {:hex, :rustler_precompiled, "0.8.3", "4e741024b0b097fe783add06e53ae9a6f23ddc78df1010f215df0c02915ef5a8", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:rustler, "~> 0.23", [hex: :rustler, repo: "hexpm", optional: true]}], "hexpm", "c23f5f33cb6608542de4d04faf0f0291458c352a4648e4d28d17ee1098cddcc4"}, + "rustler_precompiled": {:hex, :rustler_precompiled, "0.8.4", "700a878312acfac79fb6c572bb8b57f5aae05fe1cf70d34b5974850bbf2c05bf", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:rustler, "~> 0.23", [hex: :rustler, repo: "hexpm", optional: true]}], "hexpm", "3b33d99b540b15f142ba47944f7a163a25069f6d608783c321029bc1ffb09514"}, "schematic": {:hex, :schematic, "0.5.1", "be4b2c03115d5a593459c11a7249a6fbb45855947d9653e9250455dcd7df1d42", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "02f913c97e6e04ccdaa02004679a7a16bb16fe0449583ad647e296d8e8961546"}, "slugify": {:hex, :slugify, "1.3.1", "0d3b8b7e5c1eeaa960e44dce94382bee34a39b3ea239293e457a9c5b47cc6fd3", [:mix], [], "hexpm", "cb090bbeb056b312da3125e681d98933a360a70d327820e4b7f91645c4d8be76"}, "styler": {:hex, :styler, "1.1.1", "ccb55763316915b5de532bf14c587c211ddc86bc749ac676e74dfacd3894cc0d", [:mix], [], "hexpm", "80ce12fb862e13d998589eea7c1932f4e6ce9d6ded2182cb322f8f9b2b8d3632"},