This is a second take for my previous blog post filter for , where I used a literate Haskell program to convert my raw blog posts in Markdown format to a different Markdown format for cross-posting to different blogging platforms. I used has a feature called of and , the creator of environment in my case. Our only additional dependency is pandoc-lua-engine:
{
##...
ghc = pkgs.haskellPackages.ghcWithPackages (hpkgs: [
hpkgs.markdown-unlit
hpkgs.pandoc
hpkgs.pandoc-lua-engine
]);
thisShell = pkgs.mkShell {
buildInputs = [
## ...
ghc
## ...
];
NIX_GHC = "${ghc}/bin/ghc";
NIX_GHCPKG = "${ghc}/bin/ghc-pkg";
NIX_GHC_DOCDIR = "${ghc}/share/doc/ghc/html";
NIX_GHC_LIBDIR = "${ghc}/lib/ghc-9.6.5/lib";
};
# ...
}
The Implementation
For sanity, we need some language extensions:
{-# LANGUAGE OverloadedStrings #-}
For added pleasure, we need some imports:
import System.Environment (getArgs)
import qualified Data.Text as T
import qualified Data.Text.IO as TIO
import qualified Text.Pandoc as P
import qualified Text.Pandoc.Lua as P.Lua
Let's get the entrypoint function ready:
main :: IO ()
main = do
path <- head <$> getArgs
iTxt <- TIO.readFile path
oTxt <- convert "./static/assets/media/posts/executable-blog-post-pandoc-filters/filter.lua" iTxt
TIO.putStrLn oTxt
Our workhorse function is finally getting more complicated, thank God:
- Mark the input as .
- Apply a Lua filter to the AST to process the links and images.
Now, let's re-implement our workhorse function:
convert :: FilePath -> T.Text -> IO T.Text
convert lua txt = P.runIOorExplode $ do
md <- P.readCommonMark readerOptions txt
fmd <- P.Lua.applyFilter P.def ["commonmark"] lua md
P.writeCommonMark writerOptions fmd
where
readerOptions =
P.def
{ P.readerExtensions = P.enableExtension P.Ext_yaml_metadata_block $ P.getDefaultExtensions "commonmark"
, P.readerStripComments = True
}
writerOptions =
P.def
{ P.writerExtensions = P.getDefaultExtensions "commonmark"
, P.writerWrapText = P.WrapNone
}
That's it as far as Haskell goes. Now, we need to write our Lua filter. I where my program can find it.
We are done with the program. We can run our blog post via runhaskell on our blog post. But first, like last time, we need to symlink our Markdown file (.md) with a literate Haskell file extension (.lhs) so that GHC is not upset:
ln -sr \
content/posts/2024-08-11_executable-blog-post-pandoc-filters.md \
content/posts/2024-08-11_executable-blog-post-pandoc-filters.lhs
Then, we can run the blog post on the blog post itself:
runhaskell \
-pgmLmarkdown-unlit \
content/posts/2024-08-11_executable-blog-post-pandoc-filters.lhs \
content/posts/2024-08-11_executable-blog-post-pandoc-filters.md
It worked on my computer!
Wrap-Up
I used Lua for years to configure my . Since I bumped into an Emacs bug ( after 20 years of Emacs, and I am using Lua to configure my Neovim. Last but not least, OpenResty gives my Nginx superpowers with Lua.
Lua is silently taking over my life. Now, I am using Lua to configure my blog posts. I am not sure if I am getting better or worse.
As for my solution: Once stupid, always stupid. I promise it will get worse.
SOCIAL SHARE CARD GENERATOR