Allow transformation of Earmark AST before build step?
jtormey opened this issue · comments
First off, love this library! It's lightweight but does exactly what I need it to.
There are a few use cases that would be nice to have, which I think would involve using Earmark's transformations feature. I'm curious if you think it's worth exposing this as part of the article compilation (before the build
step is called).
For example:
- Using Phoenix's
Routes
router helper to dynamically generate image paths. I could see a transformation where, for every image that has asrc
starting with/
, it callsRoutes.static_path(MyApp.Endpoint, img_path)
, to get the correct static path in all envs. - Reading the contents of
h1, h2, h3, h4, h5, h6
tags and serializing it to theid
element attribute, which would allow linking to sections of the article by anchor tag.
This could probably be implemented in build
as well, by parsing, mapping, and re-generating the article body
(after Earmark has already processed it). Though not sure that's as nice as Nimble supporting it. Happy to look into opening a PR with docs / examples if this makes sense!
Hi! We support earmark_options and you should be able to use the postprocessor
option to achieve what you want. :)
Amazing! Can't believe I missed that, thanks @josevalim!
Notes for anyone looking for this in the future:
Requires Earmark v1.4.14 and latest NimblePublisher (unreleased).
Add :earmark_options
to your Nimble config:
use NimblePublisher,
build: MyApp.Blog.Post,
from: "priv/posts/**/*.md",
as: :posts,
earmark_options: %Earmark.Options{postprocessor: &MyApp.Blog.Post.postprocess/1},
highlighters: [:makeup_elixir]
To implement anchor tags for header elements:
defmodule MyApp.Blog.Post do
# ... skip
def postprocess({h, [], [text], %{}}) when h in ["h1", "h2", "h3", "h4", "h5", "h6"] do
anchor_id = text |> String.downcase() |> String.replace(~r/[^a-z]+/, "-") |> String.trim("-")
{h, [{"id", anchor_id}], [text], %{}}
end
def postprocess(value), do: value
end
Unfortunately, it doesn't seem like image path processing with Routes.static_url/2
is possible quite yet due to restrictions with router helper availability during compilation (see: phoenixframework/phoenix#2841).
If this ever changes, the implementation of this transform would look something like:
def postprocess({"img", [{"src", "/" <> _ = path} | attrs], [], %{}}) do
{"img", [{"src", Routes.static_path(MyAppWeb.Endpoint, path)} | attrs], [], %{}}
end