summaryrefslogtreecommitdiff
path: root/src/Text/Hakyll/Render.hs
blob: 34e1780f2badc04b94fc3cc2760ab6ad8a1b9a2c (plain)
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
-- | Module containing rendering functions. All these functions are used to
--   render files to the @_site@ directory.
module Text.Hakyll.Render 
    ( depends
    , render
    , renderWith
    , renderAndConcat
    , renderAndConcatWith
    , renderChain
    , renderChainWith
    , static
    , css
    ) where

import Control.Monad (unless)
import Control.Monad.Reader (liftIO)
import System.Directory (copyFile)

import Text.Hakyll.Hakyll (Hakyll)
import Text.Hakyll.Context (ContextManipulation)
import Text.Hakyll.Page
import Text.Hakyll.Renderable
import Text.Hakyll.File
import Text.Hakyll.Internal.Template (readTemplate)
import Text.Hakyll.Internal.CompressCss
import Text.Hakyll.Internal.Render

-- | Execute an IO action only when the cache is invalid.
depends :: FilePath -- ^ File to be rendered or created.
        -> [FilePath] -- ^ Files the render depends on.
        -> Hakyll () -- ^ IO action to execute when the file is out of date.
        -> Hakyll ()
depends file dependencies action = do
    destination <- toDestination file
    valid <- isFileMoreRecent destination dependencies
    unless valid action

-- | Render to a Page.
render :: Renderable a
       => FilePath -- ^ Template to use for rendering.
       -> a -- ^ Renderable object to render with given template.
       -> Hakyll Page -- ^ The body of the result will contain the render.
render = renderWith id

-- | Render to a Page. This function allows you to manipulate the context
--   first.
renderWith :: Renderable a
           => ContextManipulation -- ^ Manipulation to apply on the context.
           -> FilePath -- ^ Template to use for rendering.
           -> a -- ^ Renderable object to render with given template.
           -> Hakyll Page -- ^ The body of the result will contain the render.
renderWith manipulation templatePath renderable = do
    template <- readTemplate templatePath
    context <- toContext renderable
    return $ fromContext $ pureRenderWith manipulation template context

-- | Render each renderable with the given templates, then concatenate the
--   result. So, basically this function:
--
--   * Takes every renderable.
--
--   * Renders every renderable with all given templates. This is comparable
--     with a renderChain action.
--
--   * Concatenates the result.
--
renderAndConcat :: Renderable a
                => [FilePath] -- ^ Templates to apply on every renderable.
                -> [a] -- ^ Renderables to render.
                -> Hakyll String
renderAndConcat = renderAndConcatWith id

-- | Render each renderable with the given templates, then concatenate the
--   result. This function allows you to specify a @ContextManipulation@ to
--   apply on every @Renderable@.
renderAndConcatWith :: Renderable a
                    => ContextManipulation
                    -> [FilePath]
                    -> [a]
                    -> Hakyll String
renderAndConcatWith manipulation templatePaths renderables = do
    templates <- mapM readTemplate templatePaths
    contexts <- mapM toContext renderables
    return $ pureRenderAndConcatWith manipulation templates contexts

-- | Chain a render action for a page with a number of templates. This will
--   also write the result to the site destination. This is the preferred way
--   to do general rendering.
--
--   > renderChain [ "templates/notice.html"
--   >             , "templates/default.html"
--   >             ] $ createPagePath "warning.html"
--
--   This code will first render @warning.html@ using @templates/notice.html@,
--   and will then render the result with @templates/default.html@.
renderChain :: Renderable a => [FilePath] -> a -> Hakyll ()
renderChain = renderChainWith id

-- | A more custom render chain that allows you to specify a
--   @ContextManipulation@ which to apply on the context when it is read first.
renderChainWith :: Renderable a
                => ContextManipulation -> [FilePath] -> a -> Hakyll ()
renderChainWith manipulation templatePaths renderable = do
    url <- getUrl renderable
    depends url dependencies render'
  where
    dependencies = getDependencies renderable ++ templatePaths
    render' = do
        templates <- mapM readTemplate templatePaths
        context <- toContext renderable
        let result = pureRenderChainWith manipulation templates context
        writePage $ fromContext result

-- | Mark a certain file as static, so it will just be copied when the site is
--   generated.
static :: FilePath -> Hakyll ()
static source = do destination <- toDestination source
                   depends destination [source] (action destination)
  where
    action destination = do makeDirectories destination
                            liftIO $ copyFile source destination

-- | Render a css file, compressing it.
css :: FilePath -> Hakyll ()
css source = do destination <- toDestination source
                depends destination [source] (css' destination)
  where
    css' destination = do contents <- liftIO $ readFile source
                          makeDirectories destination
                          liftIO $ writeFile destination (compressCss contents)