DEV Community

Riccardo Odone
Riccardo Odone

Posted on • Updated on • Originally published at odone.io

Production Drafts for Hakyll Posts

You can keep reading here or jump to my blog to get the full experience, including the wonderful pink, blue and white palette.


Last week I was asked to review a draft of a blog post:

That made me realize I don't have a way to do a similar trick. However, last week I worked on "Adding published to Hakyll Posts". Thus, I already did the hard work on understanding the internals, so I could just sit back and code it.

Up until now the blog worked as follows:

  • Published posts were compiled by Hakyll to posts/*.
  • Drafts were not compiled except when HAKYLL_ENV was set to development.

With the new changes:

  • All posts are compiled by Hakyll.
  • Published posts are compiled to posts/* and appear in the archive.
  • Drafts are compiled to drafts/posts/* but do not appear in the archive.

In particular, only published posts are compiled to posts/*:

- matchMetadata "posts/*" (isDevelopmentOrPublished env) $ do
+ matchMetadata "posts/*" isPublished $ do
    route $ setExtension "html"
Enter fullscreen mode Exit fullscreen mode

Drafts are compiled to drafts/posts/*. Also, when compiling the URL to the draft is printed:

matchMetadata "posts/*" (not . isPublished) $ do
  let draftPath = ("drafts/" <>) . (`replaceExtension` "html") . toFilePath
  route . customRoute $ draftPath
  let putDraftUrl path =
        traverse_
          (unsafeCompiler . putStrLn)
          [ "----DRAFT----",
            (previewUrl <>) . draftPath . itemIdentifier $ path,
            "-------------"
          ]
  compile $
    pandocCompiler
      >>= loadAndApplyTemplate "templates/post.html" postCtx
      >>= loadAndApplyTemplate "templates/default.html" postCtx
      >>= relativizeUrls
      >>= (\x -> putDraftUrl x >> pure x)
Enter fullscreen mode Exit fullscreen mode

In the archive only published posts are included except when in development:

  create ["archive.html"] $ do
    route idRoute
    compile $ do
-     posts <- recentFirst =<< loadAll "posts/*"
+     posts <- recentFirst =<< loadAllPublished env "posts/*"
Enter fullscreen mode Exit fullscreen mode
loadAllPublished :: (Binary a, Typeable a) => [(String, String)] -> Pattern -> Compiler [Item a]
loadAllPublished env pattern_ = if isDevelopmentEnv env then all else published
  where
    all = loadAll pattern_
    published = publishedIds pattern_ >>= traverse load
    isDevelopmentEnv env = lookup "HAKYLL_ENV" env == Just "development"
Enter fullscreen mode Exit fullscreen mode

A similar change appears in the Atom feed code:

- =<< loadAllSnapshots "posts/*" "content"
+ =<< loadAllSnapshotsPublished "posts/*" "content"
Enter fullscreen mode Exit fullscreen mode
loadAllSnapshotsPublished :: (Binary a, Typeable a) => Pattern -> Snapshot -> Compiler [Item a]
loadAllSnapshotsPublished pattern_ snapshot = publishedIds pattern_ >>= traverse (`loadSnapshot` snapshot)

publishedIds :: MonadMetadata m => Pattern -> m [Identifier]
publishedIds = fmap (fmap fst . filter (isPublished . snd)) . getAllMetadata
Enter fullscreen mode Exit fullscreen mode

As always, feel free to go ahead and grab the code.


The draft I ended up reviewing was "Functional Fika — Nix and Haskell". Thanks Maxfield for the inspiration and for teaching me what fika is! ☕️


Get the latest content via email from me personally. Reply with your thoughts. Let's learn from each other. Subscribe to my PinkLetter!

Top comments (0)