{-# 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 ... -- -- > -- > {{#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