on master help sign in
Initial commit.
36e4bf8Jasper Van der Jeugt
README.markdownnew file+3
@@ -0,0 +1,3 @@
1+# Hakyll
2+
3+Hakyll is a simple static site generator in Haskell.
hakyll.cabalnew file+19
@@ -0,0 +1,19 @@
1+Name: hackyll
2+Version: 0.1
3+
4+Synopsis: A simple static site generator.
5+Description:
6+ A simple static site generator, mainly aimed at creating
7+ blogs.
8+Author: Jasper Van der Jeugt
9+Maintainer: jaspervdj@gmail.com
10+Cabal-Version: >= 1.2
11+
12+build-type: Simple
13+
14+library
15+ hs-source-dirs: src/
16+ build-depends: base > 4, template, filepath, directory, containers, bytestring,
17+ pandoc >= 1
18+ exposed-modules: Text.Hakyll.Render
19+ Text.Hakyll.Page
src/Text/Hakyll/Page.hsnew file+47
@@ -0,0 +1,47 @@
1+module Text.Hakyll.Page where
2+
3+import qualified Data.Map as M
4+import qualified Data.List as L
5+import System.FilePath
6+import Data.Maybe
7+import Text.Pandoc
8+
9+type Page = M.Map String String
10+
11+addContext :: String -> String -> Page -> Page
12+addContext key value = M.insert key value
13+
14+getURL :: Page -> String
15+getURL context = fromMaybe "404.html" $ M.lookup "url" context
16+
17+getBody :: Page -> String
18+getBody context = fromMaybe "" $ M.lookup "body" context
19+
20+readConfig :: [String] -> Page
21+readConfig lines = M.fromList $ map (trim . break (== ':')) lines
22+ where trim (key, value) = (key, dropWhile (`elem` ": ") value)
23+
24+extractContext :: String -> Page
25+extractContext str = M.insert "body" (unlines body) (readConfig header)
26+ where allLines = lines str
27+ isDelimiter = L.isPrefixOf "---"
28+ (header, body) | isDelimiter (head allLines) = let (h, b) = L.break (isDelimiter) (tail allLines)
29+ in (h, tail b)
30+ | otherwise = ([], allLines)
31+
32+writerOptions :: WriterOptions
33+writerOptions = defaultWriterOptions
34+
35+markdownToHTML :: String -> String
36+markdownToHTML = writeHtmlString writerOptions .
37+ readMarkdown defaultParserState
38+
39+readPage :: FilePath -> IO Page
40+readPage path = do
41+ content <- readFile path
42+ let context = extractContext content
43+ body = (if takeExtension path == ".markdown" then markdownToHTML else id)
44+ (getBody context)
45+ url = addExtension (dropExtension path) ".html"
46+ return $ addContext "url" url $ addContext "body" body $ context
47+
src/Text/Hakyll/Render.hsnew file+40
@@ -0,0 +1,40 @@
1+module Text.Hakyll.Render where
2+
3+import Text.Template
4+import qualified Data.ByteString.Lazy.Char8 as B
5+import qualified Data.Map as M
6+import Control.Monad
7+
8+import System.FilePath
9+import System.Directory
10+
11+import Text.Hakyll.Page
12+import Text.Hakyll.Util
13+
14+toDestination :: FilePath -> FilePath
15+toDestination path = "_site" </> path
16+
17+createContext :: Page -> Context
18+createContext = M.fromList . map packPair . M.toList
19+ where packPair (a, b) = (B.pack a, B.pack b)
20+
21+renderPage :: FilePath -> Page -> IO Page
22+renderPage templatePath page = do
23+ template <- B.readFile templatePath
24+ let body = substitute template (createContext page)
25+ return $ addContext "body" (B.unpack body) page
26+
27+renderAndWrite :: FilePath -> Page -> IO ()
28+renderAndWrite templatePath page = do
29+ rendered <- renderPage templatePath page
30+ writeFile (toDestination $ getURL rendered) (getBody rendered)
31+
32+static :: FilePath -> IO ()
33+static source = do
34+ touchDirectories destination
35+ copyFile source destination
36+ where destination = toDestination source
37+
38+staticDirectory :: FilePath -> IO ()
39+staticDirectory dir =
40+ getRecursiveContents dir >>= mapM_ static
src/Text/Hakyll/Util.hsnew file+21
@@ -0,0 +1,21 @@
1+module Text.Hakyll.Util where
2+
3+import System.Directory
4+import System.FilePath
5+import Control.Monad
6+
7+touchDirectories :: FilePath -> IO ()
8+touchDirectories path = createDirectoryIfMissing True dir
9+ where dir = takeDirectory path
10+
11+getRecursiveContents :: FilePath -> IO [FilePath]
12+getRecursiveContents topdir = do
13+ names <- getDirectoryContents topdir
14+ let properNames = filter (`notElem` [".", ".."]) names
15+ paths <- forM properNames $ \name -> do
16+ let path = topdir </> name
17+ isDirectory <- doesDirectoryExist path
18+ if isDirectory
19+ then getRecursiveContents path
20+ else return [path]
21+ return (concat paths)