Working with files

If your static site is generated depending on local files on disk, the general flow of things is as follows:

runEma render $ \model -> do
  -- Load everything on launch
  initialModel <- loadFilesAndBuildModel
  LVar.set model initialModel
  -- Continue to monitor and update the model
  observeFileSystem $ \action -> 
    LVar.modify model $ applyAction action

For monitoring local files on disk you would typically use something like fsnotify in place of observeFileSystem. What is the point of doing this? To support hot reload on data change. Imagine that your static site is generated based on Markdown files as well as HTML templates on disk. If either the Markdown file, or a HTML template file is modified, we want the web browser to hot reload the updated HTML instantly. This is enabled by storing both these kinds of files in the application model and using LVar to update it over time.

Much of this is provided by the System.UnionMount module from unionmount package. You should use it:

import qualified System.UnionMount as UnionMount

Ema.runEma render $ \model -> do
  let pats = [((), "**/*.md")]
      ignorePats = [".*"]
  void . UnionMount.mountOnLVar "." pats ignorePats model model0 $ \() fp action -> do
    case action of
      UnionMount.Refresh _ () -> do
        when (takeExtension fp == ".md") $ do
          log $ "Update: " <> fp 
          s <- readFileText fp
          pure $ Map.insert fp s
      UnionMount.Delete ->
        whenJust (takeExtension fp == ".md") $ do
          log $ "Delete: " <> fp
          pure $ Map.delete fp

mountOnLVar “mounts” the files you specify onto the model LVar such that any changes to them are automatically reflected in your model value.

Full example here.

Links to this page
  • Using Markdown

    Note that with Ema you can get hot reload support for your Markdown files using the `unionmount` package.

  • Rendering HTML

    Of course when using Ema nothing prevents you from choosing to use traditional HTML templates, and you can get hot reload on them too with a little bit of plumbing.

  • Howto
    Working with filesUse unionmount to support hot-reload on files
  • Hot Reload

    Finally, hot reload on code changes are supported via ghcid. The template repo's `bin/run` script uses ghcid underneath. Any HTML DSL (like blaze-html) or CSS DSL automatically gets supported for hot-reload. If you choose to use a file-based HTML template language, you can enable hot-reload on template change using the `unionmount` library.

    For anything outside of the Haskell code, your code becomes responsible for monitoring and updating the model LVar. The `unionmount` library already provides utilities to facilitate this for monitoring changes to files and directories.

  • Defining your model

    In this contrived example (full code here), we are using UTCTime as the model. We set the initial value using LVar.set, and then continually update the current time every second. Every time the model gets updated, the web browser will hot reload to display the up to date value. For the BlogPosts model, you would typically use fsnotify to monitor changes to the underlying Markdown files, or even better use the `unionmount` library.