packages feed

shakebook 0.3.1.0 → 0.4.0.0

raw patch · 8 files changed

+134/−152 lines, 8 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

- Shakebook.Data: SbConfig :: Path Rel Dir -> Path Rel Dir -> Text -> ReaderOptions -> WriterOptions -> Int -> (Value -> Value) -> SbConfig
- Shakebook.Data: ShakebookEnv :: LogFunc -> SbConfig -> ShakebookEnv
- Shakebook.Data: [logFunc] :: ShakebookEnv -> LogFunc
- Shakebook.Data: [sbBaseUrl] :: SbConfig -> Text
- Shakebook.Data: [sbConfig] :: ShakebookEnv -> SbConfig
- Shakebook.Data: [sbGlobalApply] :: SbConfig -> Value -> Value
- Shakebook.Data: [sbHTWrite] :: SbConfig -> WriterOptions
- Shakebook.Data: [sbMdRead] :: SbConfig -> ReaderOptions
- Shakebook.Data: [sbOutDir] :: SbConfig -> Path Rel Dir
- Shakebook.Data: [sbPPP] :: SbConfig -> Int
- Shakebook.Data: [sbSrcDir] :: SbConfig -> Path Rel Dir
- Shakebook.Data: class HasSbConfig a
- Shakebook.Data: data SbConfig
- Shakebook.Data: data ShakebookEnv
- Shakebook.Data: instance RIO.Prelude.Logger.HasLogFunc Shakebook.Data.ShakebookEnv
- Shakebook.Data: instance Shakebook.Data.HasSbConfig Shakebook.Data.ShakebookEnv
- Shakebook.Data: leadingSlash :: Path Abs Dir
- Shakebook.Data: sbConfigL :: HasSbConfig a => Lens' a SbConfig
- Shakebook.Defaults: buildPDF :: (MonadThrow m, MonadAction m, MonadReader r m, HasSbConfig r) => Cofree [] (Path Rel File) -> Path Rel File -> FilePath -> m ()
+ Shakebook.Data: toGroundedUrl :: Path Rel File -> Text
+ Shakebook.Defaults: defaultPagePaths :: MonadThrow m => [Int] -> m [Path Rel File]
- Shakebook.Conventions: genIndexPageData :: (MonadReader r m, HasSbConfig r, MonadThrow m) => [Value] -> Text -> (Text -> Text) -> Int -> m (Zipper [] Value)
+ Shakebook.Conventions: genIndexPageData :: MonadThrow m => [Value] -> Text -> (Text -> Text) -> Int -> m (Zipper [] Value)
- Shakebook.Data: generateSupposedUrl :: MonadThrow m => Path Rel File -> m (Path Abs File)
+ Shakebook.Data: generateSupposedUrl :: MonadThrow m => Path Rel File -> m Text
- Shakebook.Data: loadMarkdownAsJSON :: (MonadReader r m, HasSbConfig r, MonadAction m, MonadThrow m) => Within Rel (Path Rel File) -> m Value
+ Shakebook.Data: loadMarkdownAsJSON :: (MonadAction m, MonadThrow m) => ReaderOptions -> WriterOptions -> Within Rel (Path Rel File) -> m Value
- Shakebook.Pandoc: makePDFLaTeX :: (MonadAction m, MonadThrow m) => WriterOptions -> Pandoc -> m (Either ByteString ByteString)
+ Shakebook.Pandoc: makePDFLaTeX :: (MonadAction m, MonadThrow m) => WriterOptions -> Pandoc -> m ByteString

Files

ChangeLog.md view
@@ -1,5 +1,10 @@ # Changelog for Shakebook +## (v0.4.0.0)++* Remove `SBConfig` and constraints from this library. This was only here for refactoring+  convenience and creating a context should be up to the user.+ ## (v0.3.1.0)  * Add `withContent` lens.
app/Main.hs view
@@ -13,6 +13,7 @@ import           Shakebook.Conventions import           Shakebook.Mustache import           Shakebook.Defaults+import           Text.Pandoc.Options import           Within  sample :: Parser SimpleOpts@@ -32,6 +33,28 @@     , ppp     :: Int     } +data BuildOpts = BuildOpts+    { sbSrcDir :: Path Rel Dir+    , sbOutDir :: Path Rel Dir+    , sbBaseUrl :: Text+    , sbMdRead :: ReaderOptions+    , sbHtWrite :: WriterOptions+    , sbPPP :: Int+    }+data AppConfig = AppConfig+    { _opts :: BuildOpts+    , _lf   :: LogFunc+    }++class HasBuildOpts a where+  buildOptsL :: Lens' a BuildOpts++instance HasLogFunc AppConfig where+  logFuncL = lens _lf undefined++instance HasBuildOpts AppConfig where+  buildOptsL = lens _opts undefined+ opts :: ParserInfo SimpleOpts opts = info (sample <**> helper)     ( fullDesc@@ -43,21 +66,22 @@   (x :: SimpleOpts) <- execParser opts   s' <- parseRelDir (srcDir x)   o' <- parseRelDir (outDir x)-  app $ SbConfig s' o' (T.pack $ baseUrl x) defaultMarkdownReaderOptions defaultHtml5WriterOptions (ppp x) id+  app $ BuildOpts s' o' (T.pack $ baseUrl x) defaultMarkdownReaderOptions defaultHtml5WriterOptions (ppp x) -app :: SbConfig -> IO ()+app :: BuildOpts -> IO () app sbc =  do     logOptions' <- logOptionsHandle stdout True     lf <- newLogFunc logOptions'-    let f = ShakebookEnv (fst lf) sbc+    let f = AppConfig sbc (fst lf)      shake shakeOptions $ do        want ["all"] -      runShakePlus f $ view sbConfigL >>= \SbConfig {..} -> do+      runShakePlus f $ do+        BuildOpts{..} <- view buildOptsL -        readMDC <- newCache loadMarkdownAsJSON+        readMDC <- newCache $ loadMarkdownAsJSON sbMdRead sbHtWrite          postsC  <- newCache $ \w -> do           xs <- batchLoadWithin' w readMDC
shakebook.cabal view
@@ -4,10 +4,10 @@ -- -- see: https://github.com/sol/hpack ----- hash: 57aeb1237df591d9d22b7ea05fc33ead1a5892537f1cc077b6f2df5853ada8d7+-- hash: 468c86683bdf3cc44a2aaa53465f140ee14fe822330207c10c6e16febcbd3b35  name:           shakebook-version:        0.3.1.0+version:        0.4.0.0 synopsis:       Shake-based technical documentation generator; HTML & PDF description:    Shakebook is a documentation generator aimed at covering all the bases for mathematical, technical and scientific diagrams and typesetting. Shakebook provides combinators for taking markdown files and combining them into documents, but allowing the user to control how. Shakebook provides general combinators for templating single pages, cofree comonads for representing tables of contents, and zipper comonads for representing pagers. category:       Web
src/Shakebook/Conventions.hs view
@@ -233,13 +233,7 @@                       . withJSON (genLinkData x (f x))                       . withPosts (extract xs) $ Object mempty -genPageDataM :: (MonadReader r m, HasSbConfig r) => Text -> (Text -> Text) -> Zipper [] [Value] -> m Value-genPageDataM t f xs = view sbConfigL >>= \SbConfig{..} -> do-  let z = genPageData t f xs-  return $ sbGlobalApply z-  --genIndexPageData :: (MonadReader r m, HasSbConfig r, MonadThrow m)+genIndexPageData :: MonadThrow m                  => [Value]                  -> Text                  -> (Text -> Text)@@ -247,4 +241,4 @@                  -> m (Zipper [] Value) genIndexPageData xs g h n = do   zs <- paginate' n xs- sequence $ extend (genPageDataM g h) zs+ return $ extend (genPageData g h) zs
src/Shakebook/Data.hs view
@@ -30,30 +30,6 @@   display [] = ""   display (x : xs) = display x <> " : " <> display xs -data SbConfig = SbConfig-    { sbSrcDir      :: Path Rel Dir-    , sbOutDir      :: Path Rel Dir-    , sbBaseUrl     :: Text-    , sbMdRead      :: ReaderOptions-    , sbHTWrite     :: WriterOptions-    , sbPPP         :: Int-    , sbGlobalApply :: Value -> Value-    }--data ShakebookEnv = ShakebookEnv-    { logFunc  :: LogFunc-    , sbConfig :: SbConfig-    }--class HasSbConfig a where-  sbConfigL :: Lens' a SbConfig--instance HasSbConfig ShakebookEnv where-  sbConfigL = lens sbConfig undefined--instance HasLogFunc ShakebookEnv where-  logFuncL = lens logFunc undefined- -- | View the "content" field of a JSON value. viewContent :: Value -> Text viewContent = view (key "content" . _String)@@ -106,9 +82,6 @@ enrichUrl :: (Text -> Text) -> Value -> Value enrichUrl f v = withUrl (f (viewSrcPath v)) v -leadingSlash :: Path Abs Dir-leadingSlash = $(mkAbsDir "/")- withHtmlExtension :: MonadThrow m => Path Rel File -> m (Path Rel File) withHtmlExtension = replaceExtension ".html" @@ -118,32 +91,35 @@ withHaskellExtension :: MonadThrow m => Path Rel File -> m (Path Rel File) withHaskellExtension = replaceExtension ".hs" -generateSupposedUrl :: MonadThrow m => Path Rel File -> m (Path Abs File)-generateSupposedUrl srcPath = (leadingSlash </>) <$> withHtmlExtension srcPath+toGroundedUrl :: Path Rel File -> Text+toGroundedUrl = T.pack . toFilePath . ($(mkAbsDir "/") </>) +generateSupposedUrl :: MonadThrow m => Path Rel File -> m Text+generateSupposedUrl srcPath = toGroundedUrl <$> withHtmlExtension srcPath+ enrichSupposedUrl :: MonadThrow m => Value -> m Value enrichSupposedUrl v = do   x <- parseRelFile $ T.unpack $ viewSrcPath v   y <- generateSupposedUrl x-  return $ withUrl (T.pack . toFilePath $ y) v+  return $ withUrl y v  {-|   Get a JSON Value of Markdown Data with markdown body as "contents" field   and the srcPath as "srcPath" field. -}-loadMarkdownAsJSON :: (MonadReader r m, HasSbConfig r, MonadAction m, MonadThrow m)-                  => Within Rel (Path Rel File)-                  -> m Value-loadMarkdownAsJSON srcPath = view sbConfigL >>= \SbConfig{..} -> do-  pdoc@(Pandoc meta _) <- readMDFileWithin sbMdRead srcPath-  meta' <- flattenMeta (writeHtml5String sbHTWrite) meta-  needPandocImagesIn sbOutDir pdoc-  outText <- runPandocA $ writeHtml5String sbHTWrite pdoc+loadMarkdownAsJSON :: (MonadAction m, MonadThrow m)+                   => ReaderOptions+                   -> WriterOptions+                   -> Within Rel (Path Rel File)+                   -> m Value+loadMarkdownAsJSON ropts wopts srcPath = do+  pdoc@(Pandoc meta _) <- readMDFileWithin ropts srcPath+  meta' <- flattenMeta (writeHtml5String wopts) meta+  outText <- runPandocA $ writeHtml5String wopts pdoc   supposedUrl <- generateSupposedUrl (extract srcPath)-  return $ sbGlobalApply-         . withContent outText+  return $ withContent outText          . withSrcPath (T.pack . toFilePath $ extract srcPath)-         . withUrl (T.pack . toFilePath $ supposedUrl) $ meta'+         . withUrl supposedUrl $ meta'  immediateShoots :: Cofree [] a -> [a] immediateShoots(_ :< xs) = extract <$> xs
src/Shakebook/Defaults.hs view
@@ -1,19 +1,13 @@ {-# LANGUAGE TemplateHaskell #-} module Shakebook.Defaults where -import           Control.Comonad.Cofree-import           Control.Monad.Extra import           Data.Aeson                   as A-import           Development.Shake.Plus import           RIO-import qualified RIO.ByteString.Lazy          as LBS import qualified RIO.Map                      as M import qualified RIO.Text                     as T import           RIO.Time-import           Path                         as P+import           Path import           Shakebook.Conventions-import           Shakebook.Data-import           Shakebook.Pandoc import           Text.DocTemplates import           Text.Pandoc.Definition import           Text.Pandoc.Options@@ -53,13 +47,7 @@ defaultVideoReplacement :: Text -> Text -> Inline defaultVideoReplacement baseUrl = \x -> Str $ "[Video available at [" <> baseUrl <> "/" <> x <> "]" ---- | Build a PDF from a Cofree table of contents.-buildPDF :: (MonadThrow m, MonadAction m, MonadReader r m, HasSbConfig r) => Cofree [] (Path Rel File) -> Path Rel File -> FilePath -> m ()-buildPDF toc meta out = view sbConfigL >>= \SbConfig {..} -> do-  k <- mapM (readMDFileIn sbMdRead sbSrcDir) toc-  a <- readMDFileIn sbMdRead sbSrcDir meta-  z <- replaceUnusableImages [".mp4"] (defaultVideoReplacement sbBaseUrl) $ foldr (<>) a $ progressivelyDemoteHeaders k-  needPandocImagesIn sbOutDir z-  let z' = prefixAllImages sbOutDir z-  f <- makePDFLaTeX defaultLatexWriterOptions z'-  either (throwM . PandocActionException . show) (LBS.writeFile out) f+defaultPagePaths :: MonadThrow m => [Int] -> m [Path Rel File]+defaultPagePaths xs = do+  xs' <- mapM (parseRelDir . show) xs+  return $ fmap (\i -> $(mkRelDir "pages") </> i </> $(mkRelFile "index.html")) xs'
src/Shakebook/Pandoc.hs view
@@ -63,10 +63,12 @@     f _ = []  -- | Make a pdflatex in an `Action`.-makePDFLaTeX :: (MonadAction m, MonadThrow m) => WriterOptions -> Pandoc -> m (Either LBS.ByteString LBS.ByteString)-makePDFLaTeX wopts p = runPandocA $ do-  t <- compileDefaultTemplate "latex"-  makePDF "pdflatex" [] writeLaTeX wopts { writerTemplate = Just t } p+makePDFLaTeX :: (MonadAction m, MonadThrow m) => WriterOptions -> Pandoc -> m LBS.ByteString+makePDFLaTeX wopts p = do+  f <- runPandocA $ do+    t <- compileDefaultTemplate "latex"+    makePDF "pdflatex" [] writeLaTeX wopts { writerTemplate = Just t } p+  either (throwM . PandocActionException . show) return f  -- | Precarious function that demotes Header numbers within the Pandoc according to its depth -- in the Cofree. This is so that Headers that H1s that would correctly display for an HTML page
test/Spec.hs view
@@ -31,8 +31,8 @@ outputFolder :: Path Rel Dir outputFolder = $(mkRelDir "test/public") -baseUrl :: Text-baseUrl = "http://blanky.test"+--baseUrl :: Text+--baseUrl = "http://blanky.test"  siteTitle :: Text siteTitle = "Blanky Site"@@ -48,8 +48,8 @@ numRecentPosts :: Int numRecentPosts = 3 ---numPageNeighbours :: Int---numPageNeighbours = 1+numPageNeighbours :: Int+numPageNeighbours = 1  postsPerPage :: Int postsPerPage = 5@@ -58,24 +58,11 @@ mySocial = uncurry genLinkData <$> [("twitter", "http://twitter.com/blanky-site-nowhere")                                    ,("youtube", "http://youtube.com/blanky-site-nowhere")                                    ,("gitlab", "http://gitlab.com/blanky-site-nowhere")]-sbc :: SbConfig-sbc = SbConfig {-  sbSrcDir      = sourceFolder-, sbOutDir      = outputFolder-, sbBaseUrl     = baseUrl-, sbMdRead      = defaultMarkdownReaderOptions-, sbHTWrite     = defaultHtml5WriterOptions-, sbPPP         = postsPerPage-, sbGlobalApply = withSiteTitle siteTitle . withHighlighting pygments . withSocialLinks mySocial-} -pagePaths :: MonadThrow m => (Path Rel Dir -> Path Rel File) -> Zipper [] [a] -> m [Path Rel File]-pagePaths f xs = forM [1..size xs] $ parseRelDir . show >=> return . f--rules :: ShakePlus ShakebookEnv ()-rules = view sbConfigL >>= \SbConfig {..} -> do+rules :: HasLogFunc r => ShakePlus r ()+rules = do -  readMDC <- newCache loadMarkdownAsJSON +  readMDC <- newCache $ loadMarkdownAsJSON defaultMarkdownReaderOptions defaultHtml5WriterOptions    postsC  <- newCache $ \w -> do     xs <- batchLoadWithin' w readMDC@@ -95,114 +82,121 @@                   (T.pack . defaultPrettyMonthFormat)                   (defaultMonthUrlFragment) (HM.elems allPosts) -  let myPosts = ["posts/*.md"] `within` sbSrcDir+  let myPosts = ["posts/*.md"] `within` sourceFolder -  ("index.html" `within` sbOutDir) %^> \out -> do-    src <- blinkAndMapM sbSrcDir withMarkdownExtension $ out+      myBuildPage tmpl v out = do+        rs <- getRecentPosts myPosts+        let v' = withHighlighting pygments+               . withSocialLinks mySocial+               . withSiteTitle siteTitle+               . withRecentPosts rs $ v+        buildPageActionWithin (tmpl `within` sourceFolder) v' out+++  ("index.html" `within` outputFolder) %^> \out -> do+    src <- blinkAndMapM sourceFolder withMarkdownExtension $ out     v   <- readMDC src-    r   <- getRecentPosts myPosts-    let v' = withRecentPosts r v-    buildPageActionWithin ($(mkRelFile "templates/index.html") `within` sbSrcDir) v' out+    myBuildPage $(mkRelFile "templates/index.html") v out -  ("posts/*.html" `within` sbOutDir) %^> \out -> do-    src <- blinkAndMapM sbSrcDir withMarkdownExtension $ out+  ("posts/*.html" `within` outputFolder) %^> \out -> do+    src <- blinkAndMapM sourceFolder withMarkdownExtension $ out     xs <- sortedPosts myPosts     let k = elemIndex src (fst <$> xs)     let z = fromJust $ liftA2 seek k $ zipper (snd <$> xs)-    r   <- getRecentPosts myPosts     n   <- getBlogNavbar myPosts-    let v' = withRecentPosts r . withJSON n $ (extract z) -    buildPageActionWithin ($(mkRelFile "templates/post.html") `within` sbSrcDir) v' out+    let v' = withJSON n $ (extract z) +    myBuildPage $(mkRelFile "templates/post.html") v' out -  toc' <- mapM (mapM withHtmlExtension) $ fmap (`within` sbOutDir) tableOfContents+  toc' <- mapM (mapM withHtmlExtension) $ fmap (`within` outputFolder) tableOfContents   void . sequence . flip extend toc' $ \xs -> (fmap toFilePath $ extract xs) %^> \out -> do-    let getDoc = readMDC <=< blinkAndMapM sbSrcDir withMarkdownExtension +    let getDoc = readMDC <=< blinkAndMapM sourceFolder withMarkdownExtension      ys <- mapM getDoc toc'     zs <- mapM getDoc $ (immediateShoots xs)     v  <- getDoc $ out     let v' = withJSON (genTocNavbarData ys) . withSubsections zs $ v-    buildPageActionWithin ($(mkRelFile "templates/docs.html") `within` sbSrcDir) v' out+    myBuildPage $(mkRelFile "templates/docs.html") v' out -  ("posts/index.html" `within` sbOutDir) %^>-    copyFileChangedWithin ($(mkRelFile "posts/pages/1/index.html") `within` sbOutDir)+  ("posts/index.html" `within` outputFolder) %^>+    copyFileChangedWithin ($(mkRelFile "posts/pages/1/index.html") `within` outputFolder) -  ("posts/pages/*/index.html" `within` sbOutDir) %^> \out -> do+  ("posts/pages/*/index.html" `within` outputFolder) %^> \out -> do     xs <- sortedPosts myPosts     let n = (+ (-1)) . read . (!! 2) . splitOn "/" . toFilePath . extract $ out-    p <- seek n <$> genIndexPageData (snd <$> xs) "Posts" ("/posts/pages" <>) sbPPP+    p <- seek n <$> genIndexPageData (snd <$> xs) "Posts" ("/posts/pages/" <>) postsPerPage     k <- getBlogNavbar myPosts-    let v = withJSON k $ extract p-    buildPageActionWithin ($(mkRelFile "templates/post-list.html") `within` sbSrcDir) v out+    let v = withJSON k $ extract $ extendPageNeighbours numPageNeighbours p+    myBuildPage $(mkRelFile "templates/post-list.html") v out  -  ("posts/tags/*/index.html" `within` sbOutDir) %^> \out -> do+  ("posts/tags/*/index.html" `within` outputFolder) %^> \out -> do     let t = (!! 2) . splitOn "/" . toFilePath . extract $ out     i <- parseRelFile $ "posts/tags/" <> t <> "/pages/1/index.html"-    copyFileChangedWithin (i `within` sbOutDir) out+    copyFileChangedWithin (i `within` outputFolder) out -  ("posts/tags/*/pages/*/index.html" `within` sbOutDir) %^> \out -> do+  ("posts/tags/*/pages/*/index.html" `within` outputFolder) %^> \out -> do     let t = T.pack          . (!! 2) . splitOn "/" . toFilePath . extract $ out     xs <- filter (elem t . viewTags . snd) <$> sortedPosts myPosts     let n = (+ (-1)) . read . (!! 4) . splitOn "/" . toFilePath . extract $ out-    p  <- seek n <$> genIndexPageData (snd <$> xs) ("Posts tagged " <> t) (("/posts/tags/"  <> t <> "/posts") <>) sbPPP+    p  <- seek n <$> genIndexPageData (snd <$> xs) ("Posts tagged " <> t) (("/posts/tags/"  <> t <> "/pages/") <>) postsPerPage     k <- getBlogNavbar myPosts-    let v = withJSON k $ extract p-    buildPageActionWithin ($(mkRelFile "templates/post-list.html") `within` sbSrcDir) v out+    let v = withJSON k $ extract $ extendPageNeighbours numPageNeighbours p+    myBuildPage $(mkRelFile "templates/post-list.html") v out -  ("posts/months/*/index.html" `within` sbOutDir) %^> \out -> do+  ("posts/months/*/index.html" `within` outputFolder) %^> \out -> do     let t = (!! 2) . splitOn "/" . toFilePath . extract $ out     i <- parseRelFile $ "posts/months/" <> t <> "/pages/1/index.html"-    copyFileChangedWithin (i `within` sbOutDir) out+    copyFileChangedWithin (i `within` outputFolder) out -  ("posts/months/*/pages/*/index.html" `within` sbOutDir) %^> \out -> do+  ("posts/months/*/pages/*/index.html" `within` outputFolder) %^> \out -> do     let t = parseISODateTime . T.pack . (!! 2) . splitOn "/" . toFilePath . extract $ out     xs <- filter (sameMonth t . viewPostTime . snd) <$> sortedPosts myPosts     let n = (+ (-1)) . read  . (!! 4) . splitOn "/" . toFilePath . extract $ out     p  <- seek n <$> genIndexPageData (snd <$> xs)                        (("Posts from " <>) . T.pack . defaultPrettyMonthFormat $ t)-                       (("/posts/months/"  <> T.pack (defaultMonthUrlFormat t) <> "/posts") <>) sbPPP+                       (("/posts/months/"  <> T.pack (defaultMonthUrlFormat t) <> "/pages/") <>) postsPerPage     k <- getBlogNavbar myPosts-    let v = withJSON k $ extract p-    buildPageActionWithin ($(mkRelFile "templates/post-list.html") `within` sbSrcDir) v out+    let v = withJSON k $ extract $ extendPageNeighbours numPageNeighbours p+    myBuildPage $(mkRelFile "templates/post-list.html") v out    phony "index" $-    needIn sbOutDir [$(mkRelFile "index.html")]+    needIn outputFolder [$(mkRelFile "index.html")]    phony "post-index" $ do     xs <- sortedPosts myPosts-    needIn sbOutDir [$(mkRelFile "posts/index.html")]-    paginate' sbPPP xs-      >>= pagePaths (\p -> $(mkRelDir "posts/pages") </> p </> $(mkRelFile "index.html"))-        >>= needIn sbOutDir+    ps <- paginate' postsPerPage xs+    fs <- defaultPagePaths [1..size ps]+    needIn (outputFolder </> $(mkRelDir "posts")) ($(mkRelFile "index.html") : fs)    phony "by-tag-index" $ do     xs <- sortedPosts myPosts     forM_ (viewAllPostTags (snd <$> xs)) $ \t -> do       u <- parseRelDir $ T.unpack t-      needIn sbOutDir [$(mkRelDir "posts/tags") </> u </> $(mkRelFile "index.html")]-      paginate' sbPPP xs-        >>= pagePaths (\p -> $(mkRelDir "posts/tags") </> u </> $(mkRelDir "pages") </> p </> $(mkRelFile "index.html"))-          >>= needIn sbOutDir+      let xs' = filter (elem t . viewTags . snd) xs+      ps <- paginate' postsPerPage xs'+      fs <- defaultPagePaths [1..size ps]+      let tagFolder = outputFolder </> $(mkRelDir "posts/tags") </> u+      needIn tagFolder ($(mkRelFile "index.html") : fs)    phony "by-month-index" $ do     xs <- sortedPosts myPosts     forM_ (viewAllPostTimes (snd <$> xs)) $ \t -> do       u <- parseRelDir $ defaultMonthUrlFormat t-      needIn sbOutDir [$(mkRelDir "posts/months") </> u </> $(mkRelFile "index.html")]-      paginate' sbPPP xs-        >>= pagePaths (\p -> $(mkRelDir "posts/months") </> u </> $(mkRelDir "pages") </> p </> $(mkRelFile "index.html"))-          >>= needIn sbOutDir+      let monthFolder = outputFolder </> $(mkRelDir "posts/months") </> u+      let xs' = filter (sameMonth t . viewPostTime . snd) xs+      ps <- paginate' postsPerPage xs'+      fs <- defaultPagePaths [1..size ps]+      needIn monthFolder ($(mkRelFile "index.html") : fs)    phony "docs" $-    mapM withHtmlExtension tableOfContents >>= needIn sbOutDir+    mapM withHtmlExtension tableOfContents >>= needIn outputFolder        phony "posts" $-    getDirectoryFilesWithin' (["posts/*.md"] `within` sbSrcDir) >>= -      mapM (blinkAndMapM sbOutDir withHtmlExtension) >>=+    getDirectoryFilesWithin' (["posts/*.md"] `within` sourceFolder) >>= +      mapM (blinkAndMapM outputFolder withHtmlExtension) >>=         needWithin'    phony "clean" $ do-    logInfo $ "Cleaning files in " <> display (PathDisplay sbOutDir)-    removeFilesAfter sbOutDir ["//*"]+    logInfo $ "Cleaning files in " <> display (PathDisplay outputFolder)+    removeFilesAfter outputFolder ["//*"]  tests :: [FilePath] -> TestTree tests xs = testGroup "Rendering Tests" $@@ -215,9 +209,8 @@ main = do    xs <- findByExtension [".html"] "test/golden"    logOptions' <- logOptionsHandle stdout True-   lf <- newLogFunc (setLogMinLevel LevelInfo logOptions')-   let f = ShakebookEnv (fst lf) sbc-   shake shakeOptions $ want ["clean"] >> runShakePlus f rules-   shake shakeOptions $ want ["index", "docs", "posts", "post-index", "by-tag-index", "by-month-index"] >> runShakePlus f rules--, "docs", "month-index", "posts-index", "tag-index", "posts"]  >> runShakePlus f rules+   (lf, dlf) <- newLogFunc (setLogMinLevel LevelInfo logOptions')+   shake shakeOptions $ want ["clean"] >> runShakePlus lf rules+   shake shakeOptions $ want ["index", "docs", "posts", "post-index", "by-tag-index", "by-month-index"] >> runShakePlus lf rules    defaultMain $ tests xs-   snd lf+   dlf