sitepipe-0.1.0: src/SitePipe/Pipes.hs
{-# language RecordWildCards #-}
{-# language OverloadedStrings #-}
module SitePipe.Pipes
( site
, siteWithGlobals
) where
import Control.Monad.Catch as Catch
import System.Directory
import Control.Monad.Reader
import Data.Foldable
import Control.Monad.Writer
import Options.Applicative
import qualified Text.Mustache.Types as MT
import qualified Data.HashMap.Strict as HM
import SitePipe.Types
-- | Build a site generator from a set of rules embedded in a 'SiteM'.
-- Use this in your @main@ function.
--
-- > main :: IO ()
-- > main = site $ do
-- > posts <- resourceLoader markdownReader ["posts/*.md"]
-- > writeTemplate "templates/post.html" posts
site :: SiteM () -> IO ()
site = siteWithGlobals (MT.Object mempty)
-- | Like 'site', but allows you to pass an 'MT.Value' Object which consists
-- of an environment which is available inside your templates.
--
-- This is useful for globally providing utility functions for use in your templates.
--
-- > import qualified Text.Mustache as MT
-- > import qualified Text.Mustache.Types as MT
-- > utilityFuncs :: MT.Value
-- > utilityFuncs = MT.object
-- > ["truncate" MT.~> MT.overText (T.take 30)
-- > ]
-- >
-- > main :: IO ()
-- > main = siteWithGlobals utilityFuncs $ do
-- > -- your site ...
--
-- > <!-- in your template -->
-- > {{#truncate}}
-- > Anything inside this block will be truncated to 30 chars.
-- > {{vars}} are interpolated before applying the function.
-- > {{/truncate}}
siteWithGlobals :: MT.Value -> SiteM () -> IO ()
siteWithGlobals globals spec = do
settings <- execParser settingsInfo >>= adjSettings
clean (outputDir settings)
(result, warnings) <- runWriterT (runReaderT (Catch.try spec) settings{globalContext=globals})
case result of
Left err -> print (err :: SitePipeError)
Right _ -> unless (null warnings) (traverse_ putStrLn warnings)
-- | Argument info for option parsing.
settingsInfo :: ParserInfo Settings
settingsInfo = info (settingsP <**> helper)
( fullDesc <>
progDesc "Static site generator" <>
header "SitePipe - simple static site generator")
-- | Settings parser
settingsP :: Parser Settings
settingsP = Settings <$> strOption srcD <*> strOption outputD <*> pure (MT.Object HM.empty)
where
srcD = mconcat [ help "The directory where site source is stored"
, metavar "SOURCE_DIR"
, short 's'
, value "./site"
, showDefault
]
outputD = mconcat [ help "Directory where site will be rendered"
, metavar "OUTPUT_DIR"
, short 'o'
, value "./dist"
, showDefault
]
-- | Make given source and output dirs relative.
adjSettings :: Settings -> IO Settings
adjSettings Settings{..} = do
outD <- makeAbsolute outputDir
srcD <- makeAbsolute srcDir
return Settings{outputDir=outD, srcDir=srcD, ..}
-- | Remove output directory if it exists and set up for next write.
-- This is called by 'site' automatically.
clean :: FilePath -> IO ()
clean outD = do
putStrLn $ "Purging " ++ outD
exists <- doesDirectoryExist outD
when exists (removeDirectoryRecursive outD)
createDirectoryIfMissing False outD