I have setup Hakyll to generate basic tag pages from blog posts as follows:
main = do
hakyll $ do
match "preambles/*.md" $ compile $ pandocCompiler >>= relativizeUrls
tags <- buildTags "posts/*.md" (fromCapture "tags/*.html")
tagsRules tags $ \tag pattern -> do
let title = "Posts tagged \"" ++ tag ++ "\""
route idRoute
compile $ do
posts <- recentFirst =<< loadAll pattern
preamble <- loadBody $ fromFilePath ("preambles/" ++ tag ++ ".html")
let ctx = constField "title" title
`mappend` listField "posts" postCtx (return posts)
`mappend` bodyField preamble
`mappend` defaultContext
makeItem ""
>>= loadAndApplyTemplate "templates/tag.html" ctx
>>= loadAndApplyTemplate "templates/default.html" ctx
>>= relativizeUrls
...
I would like to be able to supply an optional preamble for each page.
To do this I would expect to have a markdown file per tag in a preambles directory, and attempt to use these when building the tags pages. However, I can't figure out how to make this optional, because loadSnapshot will fail if not found.
Is this an appropriate approach, and if so, how would I handle missing data?
Also, I'm not sure that bodyField is the appropriate way to pass the data to the template.
This seems like an entirely reasonable approach to me.
To answer your main question, you can take advantage of the
Alternativeinstance onCompilerto provide a fallback.I'd like to suggest a few minor changes too.
First off, replace your preamble rule with:
>>= pure . voidconverts theCompiler (Item String)into aCompiler (Item ()). This prevents Hakyll from producing any output files for this rule. ThesaveSnapshot "preamble"means we can still get at the content later.In addition to this, I've extracted the pattern into a variable, for reasons that will become clear later.
Then, replace:
with:
This uses
fromCapture, to generate the identifier from the Pattern. This reduces duplication, and also sidesteps the bug in your code where you used the extension".html"when theIdentifieris generated from the original filepath, and has extension".md".This uses
loadSnapshotBodyrather thanloadBodybecause of the change suggested above, where we remove the content from the rule output.Most importantly, it uses
<|>fromAlternativeto provide a fallback. You'll probably want to replace"fallback"with whichever default value you want.Finally,
makeItem ""needs to be replaced withmakeItem preamble.