-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsite.hs
273 lines (228 loc) · 9.03 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
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
import Data.Monoid
import Hakyll
import Text.Pandoc.Options
import Control.Monad
import Data.List
import Data.Maybe
import Control.Applicative ((<$>))
import System.FilePath
import Data.Binary (Binary (..))
import Data.Typeable (Typeable)
import System.Process (rawSystem)
import System.Exit
import System.IO (hPutStrLn, stderr)
import Text.XML.HXT.Core as HXT
import GHC.IO.Encoding as E
--------------------------------------------------------------------------------
main :: IO ()
main = do
setLocaleEncoding E.utf8
setFileSystemEncoding E.utf8
setForeignEncoding E.utf8
hakyll $ do
match "images/*" $ do
route idRoute
compile copyFileCompiler
match "css/main.scss" $ do
route $ setExtension "css"
compile sassCompiler
match (fromList ["about.rst", "contact.markdown"]) $ do
route $ setExtension "html"
compile $ pandocCompiler
>>= loadAndApplyTemplate "templates/default.html" defaultContext
>>= relativizeUrls
>>= slashUrlsCompiler
match "posts/*" $ do
route blogRoute
compile $ do
ident <- getUnderlying
toc <- getMetadataField ident "toc"
let writerSettings = case toc of
(Just "yes") -> myWriterOptionsToc
(Just "no") -> myWriterOptions
Nothing -> myWriterOptions
pandocCompilerWith myReaderOptions writerSettings
>>= saveSnapshot "content"
>>= loadAndApplyTemplate "templates/post.html" postCtx
>>= loadAndApplyTemplate "templates/default.html" postCtx
>>= slashUrlsCompiler
>>= relativizeUrls
create ["archive.html"] $ do
route idRoute
compile $ do
posts <- recentFirst =<< loadAll "posts/*"
let archiveCtx =
listField "posts" postCtx (return posts) `mappend`
constField "title" "Archives" `mappend`
defaultContext
makeItem ""
>>= loadAndApplyTemplate "templates/archive.html" archiveCtx
>>= loadAndApplyTemplate "templates/default.html" archiveCtx
>>= relativizeUrls
>>= slashUrlsCompiler
match "index.html" $ do
route idRoute
compile $ do
posts <- recentFirst =<< loadAll "posts/*"
let indexCtx =
listField "posts" (myTeaserCtx <> postCtx) (return $ take 7 posts) `mappend`
defaultContext
getResourceBody
>>= applyAsTemplate indexCtx
>>= loadAndApplyTemplate "templates/default.html" indexCtx
>>= slashUrlsCompiler
>>= relativizeUrls
match "templates/*" $ compile templateCompiler
create ["rss.xml"] $ do
route idRoute
compile $ do
posts <- fmap (take 10) . recentFirst =<<
loadAllSnapshots "posts/*" "content"
renderRss feedConfiguration feedContext posts
>>= slashUrlsCompiler
--------------------------------------------------------------------------------
myTeaserCtx :: Context String
myTeaserCtx = field "teaser" teaserBody
-- myTeaserCtx = teaserField "teaser" "content" <> field "content" (\item -> itemBody <$> loadSnapshot (itemIdentifier item) "content")
postCtx :: Context String
postCtx =
dateField "date" "%B %e, %Y" `mappend`
defaultContext
myWriterOptions :: WriterOptions
myWriterOptions = defaultHakyllWriterOptions {
writerReferenceLinks = True
, writerHtml5 = True
, writerHighlight = True
}
myWriterOptionsToc :: WriterOptions
myWriterOptionsToc = myWriterOptions {
writerTableOfContents = True
, writerTOCDepth = 2
, writerTemplate = "$if(toc)$<div id=\"toc\">$toc$</div>$endif$\n$body$"
, writerStandalone = True
}
myReaderOptions :: ReaderOptions
myReaderOptions = defaultHakyllReaderOptions
feedContext :: Context String
feedContext = mconcat
[ rssBodyField "description"
, rssTitleField "title"
, wpUrlField "url"
, dateField "date" "%B %e, %Y"
]
feedConfiguration :: FeedConfiguration
feedConfiguration = FeedConfiguration
{ feedTitle = "Hyper Lambda"
, feedDescription = "All things functional"
, feedAuthorName = "Sarunas Valaskevicius"
, feedAuthorEmail = "[email protected]"
, feedRoot = "http://www.hyperlambda.com"
}
rssBodyField :: String -> Context String
rssBodyField key = field key (\item -> do
teaser <- teaserBody item
return $ withUrls wordpress . withUrls absolute $ teaser)
where
wordpress = replaceAll "/index.html" (const "/")
absolute x
| head x == '/' = feedRoot feedConfiguration ++ x
| take 8 x == "/files/" = feedRoot feedConfiguration ++ drop 1 x
| otherwise = x
empty :: Compiler String
empty = return ""
rssTitleField :: String -> Context a
rssTitleField key = field key $ \i -> do
value <- getMetadataField (itemIdentifier i) "title"
let value' = liftM (replaceAll "&" (const "&")) value
maybe empty return value'
toWordPressUrl :: FilePath -> String
toWordPressUrl url =
replaceAll "/index.html" (const "/") (toUrl url)
wpUrlField :: String -> Context a
wpUrlField key = field key $
fmap (maybe "" toWordPressUrl) . getRoute . itemIdentifier
teaserBody :: Item String -> Compiler String
teaserBody item = do
body <- itemBody <$> loadSnapshot (itemIdentifier item) "content"
return $ extractTeaser . maxLengthTeaser . compactTeaser $ body
where
extractTeaser :: String -> String
extractTeaser [] = []
extractTeaser xs@(x : xr)
| "<!-- more -->" `isPrefixOf` xs = []
| otherwise = x : extractTeaser xr
maxLengthTeaser :: String -> String
maxLengthTeaser s = if isNothing $ findIndex (isPrefixOf "<!-- more -->") (tails s)
then unwords (take 60 (words s))
else s
compactTeaser :: String -> String
compactTeaser =
replaceAll "<iframe [^>]*>" (const "") .
replaceAll "<img [^>]*>" (const "") .
replaceAll "<p>" (const "") .
replaceAll "</p>" (const "") .
replaceAll "<blockquote>" (const "") .
replaceAll "</blockquote>" (const "") .
replaceAll "<strong>" (const "") .
replaceAll "</strong>" (const "") .
replaceAll "<ol>" (const "") .
replaceAll "</ol>" (const "") .
replaceAll "<ul>" (const "") .
replaceAll "</ul>" (const "") .
replaceAll "<li>" (const "") .
replaceAll "</li>" (const "") .
replaceAll "<h[0-9][^>]*>" (const "") .
replaceAll "</h[0-9]>" (const "") .
replaceAll "<pre.*" (const "") .
replaceAll "<a [^>]*>" (const "") .
replaceAll "</a>" (const "") .
replaceAll "<div [^>]*>" (const "") .
replaceAll "</div>" (const "") .
removeToc
removeToc :: String -> String
removeToc s = concat $ (runLA . xshow) (hread >>> manipulate) $ "<html><body>"++s++"</body></html>"
where
manipulate = processTopDown (
none
`HXT.when`
(isElem >>> hasName "div" >>> getAttrValue "id" >>> isA (=="toc"))
)
slashUrlsCompiler :: Item String -> Compiler (Item String)
slashUrlsCompiler item = do
myRoute <- getRoute $ itemIdentifier item
return $ case myRoute of
Nothing -> item
Just _ -> fmap slashUrls item
slashUrls :: String -> String
slashUrls = fileLinks . withUrls convert
where
convert = replaceAll "/index.html" (const "/")
fileLinks = replaceAll "/files/" (const "/files/")
blogRoute :: Routes
blogRoute =
cleanDate
`composeRoutes` gsubRoute ".html" (const "/index.html")
`composeRoutes` gsubRoute ".md" (const "/index.html")
`composeRoutes` gsubRoute ".lhs" (const "/index.html")
cleanDate :: Routes
cleanDate = customRoute removeDatePrefix
removeDatePrefix :: Identifier -> FilePath
removeDatePrefix ident = replaceFileName file (drop 11 $ takeFileName file)
where file = toFilePath ident
--------------------------------------------------------------------------------
newtype SassRunner = SassRunner FilePath
deriving (Binary, Eq, Ord, Show, Typeable)
instance Writable SassRunner where
write dst (Item _ (SassRunner src)) = do
code <- rawSystem "bundle" ["exec", "sass", "--trace", "-t", "compressed", src, dst]
case code of
ExitSuccess -> return ()
ExitFailure e -> hPutStrLn stderr $ "Could not run sass for "++src++" to "++dst++": the command has returned error code "++show e
sassCompiler :: Compiler (Item SassRunner)
sassCompiler = do
path <- getResourceFilePath
debugCompiler $ "Compiling with sass: " ++ path
makeItem $ SassRunner path