module Main (main) where import Zoxuli import Pages (pageWrapper, mainPages, tagPages, postPages, projectPages) import Lucid hiding (h2_, h3_, h4_, h5_, h6_) import Optics import System.Directory (listDirectory, createDirectoryIfMissing) import System.FilePath (()) import System.Process (spawnCommand, readProcess) import Control.Monad (void) import Control.Monad.State.Lazy (evalState, execState) import Data.Maybe (fromMaybe) import Data.Text (Text, unpack, pack) import Data.Foldable (foldrM) import Data.Map (Map) import qualified Data.Map as Map import qualified Data.ByteString.Lazy as BS -- | page rendering and writing main :: IO () main = do mapM_ (createDirectoryIfMissing True) [ buildDir , buildDir "projects" , buildDir "posts" , buildDir "tags" ] void $ spawnCommand $ "cp -r static/* " <> buildDir srcFileNames <- listDirectory "app" let getFileContents name = do content <- readFile $ "app" name highlightCode $ Code "haskell" $ pack content srcFileContents <- mapM getFileContents srcFileNames let srcFiles = reverse $ zipWith SrcFile (pack <$> srcFileNames) srcFileContents void $ spawnCommand $ "cp -r app " <> buildDir <> "/src" passTwoData <- foldrM runPassOne Map.empty allPages let passTwoData' = passTwoData <&> (#_Two % #srcFiles .~ srcFiles) mapM_ (\p -> writePage p.path $ runPassTwo passTwoData' p) allPages buildDir :: FilePath buildDir = "site" allPages :: [Page] allPages = pageWrapper <$> ( mainPages <> (fst <$> projectPages) <> (fst <$> postPages) <> tagPages ) runPassOne :: Page -> Map Text PassData -> IO (Map Text PassData) runPassOne page acc = let passOneData = execState (renderBST page.html) (One (PassOne [] [] [] [])) in case passOneData of One (PassOne links headers footnotes codes) -> do codes' <- mapM highlightCode codes let acc' = Map.alter fromPassOne page.title acc footnotes' = zipWith Footnote [1..] footnotes fromPassOne (Just (Two d)) = Just . Two $ d & #headers .~ headers & #footnotes .~ footnotes' & #codes .~ codes' fromPassOne _ = Just . Two $ PassTwo [] headers footnotes' 1 codes' [] pure $ foldr (makeBacklinks page) acc' links _ -> pure acc -- the theme css is generated with the following command: -- `chroma --html-styles --style "witchhazel"` highlightCode :: Code -> IO Text highlightCode c = pack <$> readProcess "chroma" [ "--html" , "--html-only" -- only return code fragment , "--html-lines" -- include line numbers , "--lexer", unpack c.lang ] (unpack c.body) makeBacklinks :: Page -> Link -> Map Text PassData -> Map Text PassData makeBacklinks page ln = Map.alter addBacklink ln.destinationTitle where backlink = Link ln.body page.title page.path addBacklink (Just (Two d)) = Just . Two $ d & #backlinks %~ cons backlink addBacklink _ = Just . Two $ PassTwo [backlink] [] [] 1 [] [] runPassTwo :: Map Text PassData -> Page -> BS.ByteString runPassTwo passOneData page = evalState (renderBST page.html) pageData where pageData = fromMaybe (Two (PassTwo [] [] [] 1 [] [])) $ Map.lookup page.title passOneData writePage :: Text -> BS.ByteString -> IO () writePage path = BS.writeFile (buildDir unpack path <> ".html")