rss feed and robots.txt
This commit is contained in:
parent
e3cbfdb2fb
commit
11f0f80daf
5 changed files with 73 additions and 14 deletions
|
@ -4,7 +4,7 @@ outputDir :: String
|
||||||
outputDir = "publish"
|
outputDir = "publish"
|
||||||
|
|
||||||
assetGlobs :: [String]
|
assetGlobs :: [String]
|
||||||
assetGlobs = ["static//*"]
|
assetGlobs = ["static//*", "robots.txt"]
|
||||||
|
|
||||||
-- CAN ONLY BE TYPST DOCS UNLESS YOU CHANGE THINGS AT THE `pages` RULE in `Main.hs
|
-- CAN ONLY BE TYPST DOCS UNLESS YOU CHANGE THINGS AT THE `pages` RULE in `Main.hs
|
||||||
pagePaths :: [String]
|
pagePaths :: [String]
|
||||||
|
|
63
app/Main.hs
63
app/Main.hs
|
@ -11,12 +11,16 @@ import Config
|
||||||
import Control.Monad (forM, when)
|
import Control.Monad (forM, when)
|
||||||
import qualified Data.HashMap.Strict as HM
|
import qualified Data.HashMap.Strict as HM
|
||||||
import Data.List (sortOn)
|
import Data.List (sortOn)
|
||||||
|
import Data.Maybe (fromJust)
|
||||||
import qualified Data.Ord as Ord
|
import qualified Data.Ord as Ord
|
||||||
import qualified Data.Text as T
|
import qualified Data.Text as T
|
||||||
|
import Deriving.Aeson
|
||||||
|
import Deriving.Aeson.Stock (Vanilla)
|
||||||
import Development.Shake (Action, Rules, (%>), (|%>), (~>))
|
import Development.Shake (Action, Rules, (%>), (|%>), (~>))
|
||||||
import qualified Development.Shake as Shake
|
import qualified Development.Shake as Shake
|
||||||
import Development.Shake.FilePath ((</>))
|
import Development.Shake.FilePath ((</>))
|
||||||
import qualified Development.Shake.FilePath as FP
|
import qualified Development.Shake.FilePath as FP
|
||||||
|
import qualified Development.Shake.FilePath as Shake
|
||||||
import Templates
|
import Templates
|
||||||
import Types
|
import Types
|
||||||
import Utilities
|
import Utilities
|
||||||
|
@ -79,7 +83,14 @@ pages =
|
||||||
let metaSrc = indexHtmlTypstMetaPath target
|
let metaSrc = indexHtmlTypstMetaPath target
|
||||||
html <- typstToHtml src
|
html <- typstToHtml src
|
||||||
meta <- yamlToPost metaSrc
|
meta <- yamlToPost metaSrc
|
||||||
let page = Page (postTitle meta) html
|
time <- Utilities.now
|
||||||
|
let page =
|
||||||
|
Page
|
||||||
|
{ pageTitle = postTitle meta,
|
||||||
|
pageContent = html,
|
||||||
|
pageNow = time,
|
||||||
|
pageSection = T.pack $ fromJust $ Shake.stripExtension "html" target
|
||||||
|
}
|
||||||
applyTemplateAndWrite "default.html" page target
|
applyTemplateAndWrite "default.html" page target
|
||||||
Shake.putInfo $ "Built " <> target <> " from " <> src
|
Shake.putInfo $ "Built " <> target <> " from " <> src
|
||||||
|
|
||||||
|
@ -111,8 +122,14 @@ typstPost src = do
|
||||||
post <- readTypstPost src
|
post <- readTypstPost src
|
||||||
let rPost = fromPost post
|
let rPost = fromPost post
|
||||||
postHtml <- applyTemplate "post.html" rPost
|
postHtml <- applyTemplate "post.html" rPost
|
||||||
|
time <- Utilities.now
|
||||||
let page = Page (postTitle post) postHtml
|
let page =
|
||||||
|
Page
|
||||||
|
{ pageTitle = rPostTitle rPost,
|
||||||
|
pageContent = postHtml,
|
||||||
|
pageNow = time,
|
||||||
|
pageSection = T.pack $ fromJust $ Shake.stripExtension "html" target
|
||||||
|
}
|
||||||
applyTemplateAndWrite "default.html" page target
|
applyTemplateAndWrite "default.html" page target
|
||||||
Shake.putInfo $ "Built " <> target <> " from " <> src
|
Shake.putInfo $ "Built " <> target <> " from " <> src
|
||||||
|
|
||||||
|
@ -126,7 +143,14 @@ markdownPost src = do
|
||||||
-- Shake.putInfo $ show . toJSON $ rPost
|
-- Shake.putInfo $ show . toJSON $ rPost
|
||||||
postHtml <- applyTemplate "post.html" rPost
|
postHtml <- applyTemplate "post.html" rPost
|
||||||
|
|
||||||
let page = Page (postTitle post) postHtml
|
time <- Utilities.now
|
||||||
|
let page =
|
||||||
|
Page
|
||||||
|
{ pageTitle = rPostTitle rPost,
|
||||||
|
pageContent = postHtml,
|
||||||
|
pageNow = time,
|
||||||
|
pageSection = T.pack $ fromJust $ Shake.stripExtension "html" target
|
||||||
|
}
|
||||||
applyTemplateAndWrite "default.html" page target
|
applyTemplateAndWrite "default.html" page target
|
||||||
Shake.putInfo $ "Built " <> target <> " from " <> src
|
Shake.putInfo $ "Built " <> target <> " from " <> src
|
||||||
|
|
||||||
|
@ -140,7 +164,14 @@ home =
|
||||||
<$> forM postPaths readPost
|
<$> forM postPaths readPost
|
||||||
let posts' = map fromPost posts
|
let posts' = map fromPost posts
|
||||||
html <- applyTemplate "home.html" $ HM.singleton "posts" posts'
|
html <- applyTemplate "home.html" $ HM.singleton "posts" posts'
|
||||||
let page = Page (T.pack "Home") html
|
time <- Utilities.now
|
||||||
|
let page =
|
||||||
|
Page
|
||||||
|
{ pageTitle = T.pack "Home",
|
||||||
|
pageContent = html,
|
||||||
|
pageNow = time,
|
||||||
|
pageSection = T.pack $ fromJust $ Shake.stripExtension "html" target
|
||||||
|
}
|
||||||
applyTemplateAndWrite "default.html" page target
|
applyTemplateAndWrite "default.html" page target
|
||||||
Shake.putInfo $ "Built " <> target
|
Shake.putInfo $ "Built " <> target
|
||||||
|
|
||||||
|
@ -151,17 +182,31 @@ postList =
|
||||||
posts <- sortOn (Ord.Down . postDate) <$> forM postPaths readPost
|
posts <- sortOn (Ord.Down . postDate) <$> forM postPaths readPost
|
||||||
let posts' = map fromPost posts
|
let posts' = map fromPost posts
|
||||||
html <- applyTemplate "posts.html" $ HM.singleton "posts" posts'
|
html <- applyTemplate "posts.html" $ HM.singleton "posts" posts'
|
||||||
let page = Page (T.pack "Blog Posts") html
|
time <- Utilities.now
|
||||||
|
let page =
|
||||||
|
Page
|
||||||
|
{ pageTitle = T.pack "Blog Posts",
|
||||||
|
pageContent = html,
|
||||||
|
pageNow = time,
|
||||||
|
pageSection = T.pack $ fromJust $ Shake.stripExtension "html" target
|
||||||
|
}
|
||||||
applyTemplateAndWrite "default.html" page target
|
applyTemplateAndWrite "default.html" page target
|
||||||
Shake.putInfo $ "Built " <> target
|
Shake.putInfo $ "Built " <> target
|
||||||
|
|
||||||
|
data Rss = Rss
|
||||||
|
{ now :: T.Text,
|
||||||
|
posts :: [RenderedPost]
|
||||||
|
}
|
||||||
|
deriving (Show, Generic)
|
||||||
|
deriving (ToJSON) via Vanilla Rss
|
||||||
|
|
||||||
rss :: Rules ()
|
rss :: Rules ()
|
||||||
rss =
|
rss =
|
||||||
outputDir </> "index.xml" %> \target -> do
|
outputDir </> "index.xml" %> \target -> do
|
||||||
postPaths <- getPublishedPosts
|
postPaths <- getPublishedPosts
|
||||||
posts <- sortOn (Ord.Down . postDate) <$> forM postPaths readPost
|
posts <- map fromPost . sortOn (Ord.Down . postDate) <$> forM postPaths readPost
|
||||||
-- TODO: change this to actually have it's own type for things like updated
|
time <- Utilities.now
|
||||||
applyTemplateAndWrite "feed.xml" (HM.singleton "posts" posts) target
|
applyTemplateAndWrite "feed.xml" (Rss time posts) target
|
||||||
|
|
||||||
Shake.putInfo $ "Built " <> target
|
Shake.putInfo $ "Built " <> target
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ import Development.Shake.FilePath ((</>))
|
||||||
import GHC.Stack (HasCallStack)
|
import GHC.Stack (HasCallStack)
|
||||||
import qualified Text.Mustache as Mus
|
import qualified Text.Mustache as Mus
|
||||||
import qualified Text.Mustache.Compile as Mus
|
import qualified Text.Mustache.Compile as Mus
|
||||||
import Types (Post (postAuthor, postContent, postDate, postLink, postTags, postTitle), RenderedPost (RenderedPost, rPostAuthor, rPostContent, rPostDate, rPostHasTags, rPostIsoDate, rPostLink, rPostTags, rPostTitle))
|
import Types (Post (postAuthor, postContent, postDate, postDescription, postLink, postTags, postTitle), RenderedPost (RenderedPost, rPostAuthor, rPostContent, rPostDate, rPostHasTags, rPostId, rPostIsoDate, rPostLink, rPostSummary, rPostTags, rPostTitle))
|
||||||
import Utilities
|
import Utilities
|
||||||
|
|
||||||
applyTemplate :: (HasCallStack, (ToJSON a)) => String -> a -> Action Text
|
applyTemplate :: (HasCallStack, (ToJSON a)) => String -> a -> Action Text
|
||||||
|
@ -55,5 +55,8 @@ fromPost post =
|
||||||
rPostDate = postDate post,
|
rPostDate = postDate post,
|
||||||
rPostIsoDate = postDate post >>= parseDate,
|
rPostIsoDate = postDate post >>= parseDate,
|
||||||
rPostContent = postContent post,
|
rPostContent = postContent post,
|
||||||
rPostLink = postLink post
|
rPostLink = postLink post,
|
||||||
|
-- maybe can replace non-acceptable chars with - but unclear is exactly one - is allowed or now https://www.iana.org/assignments/urn-informal/urn-1
|
||||||
|
rPostId = T.pack . ("urn:urn-1:" ++) . filter (\c -> elem c "abcdefghijklmnopqrstuvwxyz0123456789") . T.unpack . T.toLower . postTitle $ post,
|
||||||
|
rPostSummary = postDescription post
|
||||||
}
|
}
|
||||||
|
|
11
app/Types.hs
11
app/Types.hs
|
@ -5,7 +5,14 @@ import Deriving.Aeson
|
||||||
import Deriving.Aeson.Stock (PrefixedSnake)
|
import Deriving.Aeson.Stock (PrefixedSnake)
|
||||||
|
|
||||||
-- pageSection is what css class should be specified in a style html element, I would do an enum but I foresee that being a mistake
|
-- pageSection is what css class should be specified in a style html element, I would do an enum but I foresee that being a mistake
|
||||||
data Page = Page {pageTitle :: Text, pageContent :: Text, pageNow :: Text, pageSection :: Text}
|
data Page = Page
|
||||||
|
{ pageTitle :: Text,
|
||||||
|
pageContent :: Text,
|
||||||
|
-- build time
|
||||||
|
pageNow :: Text,
|
||||||
|
-- css class for page section
|
||||||
|
pageSection :: Text
|
||||||
|
}
|
||||||
deriving (Show, Generic)
|
deriving (Show, Generic)
|
||||||
deriving (ToJSON) via PrefixedSnake "page" Page
|
deriving (ToJSON) via PrefixedSnake "page" Page
|
||||||
|
|
||||||
|
@ -31,7 +38,7 @@ data Post = Post
|
||||||
postDate :: Maybe Text,
|
postDate :: Maybe Text,
|
||||||
postContent :: Maybe Text,
|
postContent :: Maybe Text,
|
||||||
postLink :: Maybe Text,
|
postLink :: Maybe Text,
|
||||||
postSummary :: Maybe Text,
|
postDescription :: Maybe Text,
|
||||||
postDraft :: Maybe Bool
|
postDraft :: Maybe Bool
|
||||||
}
|
}
|
||||||
deriving (Show, Generic)
|
deriving (Show, Generic)
|
||||||
|
|
|
@ -8,6 +8,7 @@ import Data.List (find)
|
||||||
import Data.Text (Text)
|
import Data.Text (Text)
|
||||||
import qualified Data.Text as T
|
import qualified Data.Text as T
|
||||||
import Data.Time
|
import Data.Time
|
||||||
|
import Data.Time.Format.ISO8601 (iso8601Show)
|
||||||
import Data.Yaml.Aeson
|
import Data.Yaml.Aeson
|
||||||
import Development.Shake (Action)
|
import Development.Shake (Action)
|
||||||
import qualified Development.Shake as Shake
|
import qualified Development.Shake as Shake
|
||||||
|
@ -99,6 +100,9 @@ runPandoc action =
|
||||||
Pandoc.runIO (Pandoc.setVerbosity Pandoc.ERROR >> action)
|
Pandoc.runIO (Pandoc.setVerbosity Pandoc.ERROR >> action)
|
||||||
>>= either (fail . show) return
|
>>= either (fail . show) return
|
||||||
|
|
||||||
|
now :: Action T.Text
|
||||||
|
now = Shake.liftIO $ fmap (T.pack . iso8601Show) getCurrentTime
|
||||||
|
|
||||||
markdownToPost :: FilePath -> Action Post
|
markdownToPost :: FilePath -> Action Post
|
||||||
markdownToPost path = do
|
markdownToPost path = do
|
||||||
content <- Shake.readFile' path
|
content <- Shake.readFile' path
|
||||||
|
|
Loading…
Reference in a new issue