dashbitco / nimble_publisher

A minimal filesystem-based publishing engine with Markdown support and code highlighting

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

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 a src starting with /, it calls Routes.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 the id 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