-
Notifications
You must be signed in to change notification settings - Fork 11
/
site.hs
228 lines (191 loc) · 8.63 KB
/
site.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
--------------------------------------------------------------------------------
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverloadedStrings #-}
import qualified Data.Char as C
import Data.List
import Data.Monoid (mappend)
import qualified Data.Set as S
import Hakyll
import System.Environment
import System.FilePath.Posix
import Text.Blaze.Html (toHtml, toValue, (!))
import Text.Blaze.Html.Renderer.String (renderHtml)
import qualified Text.Blaze.Html5 as H
import qualified Text.Blaze.Html5.Attributes as A
import Text.Pandoc.Options
--------------------------------------------------------------------------------
main :: IO ()
main = do
(action:_) <- getArgs
let postsPattern = if action == "watch"
then "posts/**.md" .||. "drafts/**.md"
else "posts/**.md"
hakyll $ do
pages <- buildPages postsPattern
categories <- buildCategories postsPattern (fromCapture "*/index.html")
match "images/*" $ do
route idRoute
compile copyFileCompiler
-- Tell hakyll to watch the less files
match "css/*.less" $ compile getResourceBody
-- Compile the main less file
-- We tell hakyll it depends on all the less files,
-- so it will recompile it when needed
d <- makePatternDependency "css/*.less"
rulesExtraDependencies [d] $ create ["css/main.css"] $ do
route idRoute
compile $ loadBody "css/main.less"
>>= makeItem
>>= withItemBody
(unixFilter "lessc" ["--clean-css","--include-path=css","-"])
match "pages/*.md" $ do
route niceRoutePages
compile $ pandocMathCompiler
>>= loadAndApplyTemplate "templates/default.html" defaultContext
>>= removeIndexHtml
>>= relativizeUrls
match postsPattern $ do
route niceRoute
compile $ pandocMathCompiler
>>= saveSnapshot "content"
>>= loadAndApplyTemplate "templates/post.html" (postCtxWithCat categories)
>>= loadAndApplyTemplate "templates/default.html" (postCtxWithCat categories)
>>= removeIndexHtml
>>= relativizeUrls
create ["archive.html"] $ do
route idRoute
compile $ do
posts <- recentFirst =<< loadAll postsPattern
let archiveCtx =
listField "posts" (postCtxWithCat categories) (return posts) `mappend`
constField "title" "Archives" `mappend`
defaultContext
makeItem ""
>>= loadAndApplyTemplate "templates/archive.html" archiveCtx
>>= loadAndApplyTemplate "templates/default.html" archiveCtx
>>= removeIndexHtml
>>= relativizeUrls
match "404.md" $ do
route niceRoute
compile $ do
let notFoundCtx =
constField "title" "404 Page Not Found" `mappend`
defaultContext
pandocMathCompiler
>>= loadAndApplyTemplate "templates/default.html" notFoundCtx
>>= removeIndexHtml
>>= relativizeUrls
paginateRules pages $ \index pattern -> do
route idRoute
compile $ do
let posts = recentFirst =<< loadAllSnapshots postsPattern "content"
let indexCtx =
listField "posts" (previewContextWithCat categories) (takeFromTo start end <$> posts) `mappend`
constField "title" "Home" `mappend`
paginateContext pages index `mappend`
defaultContext
where
start = 5 * (index - 1)
end = 5 * index
makeItem ""
>>= loadAndApplyTemplate "templates/posts.html" indexCtx
>>= loadAndApplyTemplate "templates/default.html" indexCtx
>>= removeIndexHtml
>>= relativizeUrls
match "templates/*" $ compile templateCompiler
let buildPaginatedTag (tag, idlist) = do
pages <- buildTagPages tag idlist
paginateRules pages $ \index pattern -> do
route idRoute
compile $ do
let posts = recentFirst =<< loadAllSnapshots (fromList idlist) "content"
let indexCtx =
listField "posts" (previewContextWithCat categories) (takeFromTo start end <$> posts) `mappend`
constField "title" "Home" `mappend`
paginateContext pages index `mappend`
defaultContext
where
start = 5 * (index - 1)
end = 5 * index
makeItem ""
>>= loadAndApplyTemplate "templates/posts.html" indexCtx
>>= loadAndApplyTemplate "templates/default.html" indexCtx
>>= removeIndexHtml
>>= relativizeUrls
mapM_ buildPaginatedTag $ tagsMap categories
--------------------------------------------------------------------------------
postCtx :: Context String
postCtx =
dateField "date" "%B %e, %Y" `mappend`
defaultContext
postCtxWithCat :: Tags -> Context String
postCtxWithCat categories = categoryField' "category" categories `mappend` postCtx
previewContext :: Context String
previewContext = teaserField "preview" "content" `mappend` postCtx
previewContextWithCat :: Tags -> Context String
previewContextWithCat categories = teaserField "preview" "content" `mappend` postCtxWithCat categories
categoryField' =
tagsFieldWith getCategory simpleRenderLink' (mconcat . intersperse ", ")
-- | Obtain categories from a page.
getCategory :: MonadMetadata m => Identifier -> m [String]
getCategory = return . return . takeBaseName . takeDirectory . toFilePath
simpleRenderLink' :: String -> Maybe FilePath -> Maybe H.Html
simpleRenderLink' _ Nothing = Nothing
simpleRenderLink' tag (Just filePath) =
Just $ H.a ! A.href (toValue $ toUrl filePath) $ toHtml (firstUpper tag)
firstUpper :: String -> String
firstUpper (hd:tl) = C.toUpper hd : tl
firstUpper [] = []
pandocMathCompiler =
let mathExtensions = [Ext_tex_math_dollars, Ext_tex_math_double_backslash,
Ext_latex_macros]
defaultExtensions = writerExtensions defaultHakyllWriterOptions
newExtensions = foldr S.insert defaultExtensions mathExtensions
writerOptions = defaultHakyllWriterOptions {
writerExtensions = newExtensions,
writerHTMLMathMethod = MathJax ""
}
in pandocCompilerWith defaultHakyllReaderOptions writerOptions
-- replace a foo/bar.md by foo/bar/index.html
-- this way the url looks like: foo/bar in most browsers
niceRoute :: Routes
niceRoute = customRoute createIndexRoute
where
createIndexRoute ident =
takeDirectory p </> takeBaseName p </> "index.html"
where p = toFilePath ident
niceRoutePages :: Routes
niceRoutePages = customRoute createIndexRoute
where
createIndexRoute ident =
takeBaseName p </> "index.html"
where p = toFilePath ident
-- replace url of the form foo/bar/index.html by foo/bar
removeIndexHtml :: Item String -> Compiler (Item String)
removeIndexHtml item = return $ fmap (withUrls removeIndexStr) item
where
removeIndexStr :: String -> String
removeIndexStr url = case splitFileName url of
(dir, "index.html") | isLocal dir -> dir
_ -> url
where isLocal uri = not ("://" `isInfixOf` uri)
buildPages :: (MonadMetadata m) => Pattern -> m Paginate
buildPages pattern =
buildPaginateWith
(return . paginateEvery 5)
pattern
(\index ->
if index == 1
then fromFilePath "index.html"
else fromFilePath $ "page-" ++ show index ++ "/index.html")
buildTagPages :: (MonadMetadata m) => String -> [Identifier] -> m Paginate
buildTagPages tag identifiers =
buildPaginateWith
(return . paginateEvery 5)
(fromList identifiers)
(\index ->
if index == 1
then fromFilePath $ tag ++ "/index.html"
else fromFilePath $ tag ++ "/page-" ++ show index ++ "/index.html")
takeFromTo :: Int -> Int -> [a] -> [a]
takeFromTo start end = drop start . take end