on ec4fafd help sign in
58 files changed+351−881
since splitsnapshots
unifiedsplit
.circleci/config.yml+11−5 CHANGELOG.md−52 hakyll.cabal+12−15 lib/Hakyll/Core/Compiler.hs+2−35 lib/Hakyll/Core/Compiler/Internal.hs+41−126 lib/Hakyll/Core/Compiler/Require.hs−2 lib/Hakyll/Core/Identifier.hs+1−1 lib/Hakyll/Core/Logger.hs+3−9 lib/Hakyll/Core/Provider/Metadata.hs+2−3 lib/Hakyll/Core/Runtime.hs+3−3 lib/Hakyll/Core/Store.hs+23−26 lib/Hakyll/Web/Feed.hs+32−51 lib/Hakyll/Web/Html.hs+1−1 lib/Hakyll/Web/Tags.hs+1−2 lib/Hakyll/Web/Template.hs+2−24 lib/Hakyll/Web/Template/Context.hs+32−122 lib/Hakyll/Web/Template/Internal.hs+44−103 lib/Hakyll/Web/Template/Internal/Element.hs+14−7 stack.yaml+7−9 tests/Hakyll/Core/UnixFilter/Tests.hs+14−6 tests/Hakyll/Web/Template/Context/Tests.hs+3−40 tests/Hakyll/Web/Template/Tests.hs+17−49 tests/TestSuite.hs−2 tests/TestSuite/Util.hs+2−11 tests/data/embed.html−1 tests/data/posts/2018-09-26.md−1 tests/data/posts/2019/05/10/tomorrow.md−1 web/css/syntax.css+18−61 web/examples.markdown+6−20 web/libraries.markdown−1 web/site.hs+32−14 web/templates/tutorials.html+18−24 web/tutorials/01-installation.markdown+2−4 web/tutorials/02-basics.markdown−1 web/tutorials/03-rules-routes-compilers.markdown−1 web/tutorials/04-compilers.markdown−1 web/tutorials/05-snapshots-feeds.markdown−1 web/tutorials/06-versions.markdown−1 web/tutorials/a-guide-to-the-hakyll-module-zoo.markdown−1 web/tutorials/external-add-tags-to-your-hakyll-blog.md+1−1 web/tutorials/external-blogger-port.md+1−1 web/tutorials/external-cassius-template.md+1−1 web/tutorials/external-clean-urls-with-hakyll.md+1−1 web/tutorials/external-deploying-with-widely.md+1−1 web/tutorials/external-external-code-inclusion.md−6 web/tutorials/external-functionfield.md+1−1 web/tutorials/external-github-pages-circle-ci.md+1−1 web/tutorials/external-inlining-and-compressing-css.md+1−1 web/tutorials/faq.markdown−1 web/tutorials/github-pages-tutorial.md−1 web/tutorials/hakyll-3-to-hakyll4-migration-guide.markdown−1 web/tutorials/robertwpearce-01-setup-and-initial-customization.html−5 web/tutorials/robertwpearce-02-generating-a-sitemap-xml-file.html−5 web/tutorials/robertwpearce-03-generating-rss-and-atom-xml-feeds.html−5 web/tutorials/robertwpearce-04-copying-static-files-for-your-build.html−5 web/tutorials/robertwpearce-05-generating-custom-post-filenames-from-a-title-slug.html−5 web/tutorials/using-clay-with-hakyll.markdown−1 web/tutorials/using-teasers-in-hakyll.markdown−1
.circleci/config.yml+11−5
@@ -6,14 +6,20 @@ jobs:
66 steps:
77 - checkout
88 - restore_cache:
9− key: 'v3-hakyll-{{ arch }}-{{ .Branch }}'
9+ key: 'v2-hakyll-{{ arch }}-{{ .Branch }}'
10+ - run:
11+ name: 'Update cabal indices'
12+ command: 'cabal update'
1013 - run:
1114 # We set jobs to 1 here because that prevents Out-Of-Memory exceptions
1215 # while compiling dependencies.
1316 name: 'Install dependencies'
14− command: '.circleci/tickle.sh stack build --test --copy-bins --jobs=1'
17+ command: '.circleci/tickle.sh cabal install --only-dependencies --enable-tests --jobs=1 --flags="+buildWebsite"'
18+ - run:
19+ name: 'Build and run tests'
20+ command: 'cabal test'
1521 - save_cache:
16− key: 'v3-hakyll-{{ arch }}-{{ .Branch }}-{{ .Revision }}'
22+ key: 'v2-hakyll-{{ arch }}-{{ .Branch }}-{{ .Revision }}'
1723 paths:
18− - '~/.stack-work'
19− - '~/.stack'
24+ - '~/.cabal'
25+ - '~/.ghc'
CHANGELOG.md−52
@@ -4,58 +4,6 @@ title: Releases
44
55# Releases
66
7−## Hakyll 4.13.0.0 (2019-08-30)
8−
9−- Improved documentation in many places (contribution by Bergi)
10−- Significantly improve error messages when reading and applying templates
11− (contribution by Bergi)
12− * `empty` and `fail` for `Compiler` now fail without or with a message,
13− allowing for much better debug output
14− * `renderFeed`, `renderRssWithTemplates` and `renderAtomWithTemplates` now
15− take `Template` rather than `String` arguments
16−- Add option to specify date in directory structure,e.g.
17− `posts/2019/05/10/tomorrow.md` (contribution by Taeer Bar-Ya)
18−- Bump QuickCheck to 2.13
19−
20−## Hakyll 4.12.5.2 (2019-05-09)
21−
22−- Bump pandoc to 2.7
23−- Add `srcset` to the list of URL attributes (contribution by c50a326)
24−- Expose `getCategory` in `Hakyll.Web.Tags` (contribution by Ng Wei En)
25−- Fix issue where `published` overwrites the user's context (contribution by
26− ncaq)
27−
28−## Hakyll 4.12.5.1 (2019-02-03)
29−
30−- Bump pandoc to 2.6
31−- Bump pandoc-citeproc to 0.16
32−
33−## Hakyll 4.12.5.0 (2019-01-12)
34−
35−- Update dependencies (contribution by Hexirp):
36− * Bump containers to 0.6
37− * Bump yaml to 0.11
38− * Bump pandoc to 2.5
39− * Bump pandoc-citeproc to 0.15
40− * Bump tasty to 1.2
41−- Correct assertion in unixFilterError test
42− (contribution by Jim McStanton)
43−- Add renderRssWithTemplates, renderAtomWithTemplates
44− (contribution by Abhinav Sarkar)
45−- Speed up hashing in cache (contribution by 0xd34df00d)
46−- Update type of fromFilePath to use FilePath instead of String
47− (contribution by Jim McStanton)
48−- Drop extension when parsing dates in filepaths (contribution by
49− Gabriel Aumala)
50−
51−## Hakyll 4.12.4.0 (2018-08-13)
52−
53−- Bump yaml to 0.10
54−- Bump file-embed to 0.0.11
55−- Bump QuickCheck to 2.12
56−- Use makeRelativeToProject with embedFile for stack ghci (contribution by
57− Michael Sloan)
58−
597## Hakyll 4.12.3.0 (2018-05-29)
608
619- Bump tasty to 1.1
hakyll.cabal+12−15
@@ -1,5 +1,5 @@
11Name: hakyll
2−Version: 4.13.0.0
2+Version: 4.12.3.0
33
44Synopsis: A static website compiler library
55Description:
@@ -68,7 +68,6 @@ Extra-source-files:
6868 tests/data/partial.html
6969 tests/data/partial.html.out
7070 tests/data/posts/2010-08-26-birthday.md
71− tests/data/posts/2018-09-26.md
7271 tests/data/russian.md
7372 tests/data/strip.html
7473 tests/data/strip.html.out
@@ -167,17 +166,15 @@ Library
167166 blaze-html >= 0.5 && < 0.10,
168167 blaze-markup >= 0.5.1 && < 0.9,
169168 bytestring >= 0.9 && < 0.11,
170− containers >= 0.3 && < 0.7,
169+ containers >= 0.3 && < 0.6,
171170 cryptohash >= 0.7 && < 0.12,
172171 data-default >= 0.4 && < 0.8,
173172 deepseq >= 1.3 && < 1.5,
174173 directory >= 1.0 && < 1.4,
175− file-embed >= 0.0.10.1 && < 0.0.12,
176174 filepath >= 1.0 && < 1.5,
177175 lrucache >= 1.1.1 && < 1.3,
178176 mtl >= 1 && < 2.3,
179177 network-uri >= 2.6 && < 2.7,
180− optparse-applicative >= 0.12 && < 0.15,
181178 parsec >= 3.0 && < 3.2,
182179 process >= 1.6 && < 1.7,
183180 random >= 1.0 && < 1.2,
@@ -185,13 +182,14 @@ Library
185182 resourcet >= 1.1 && < 1.3,
186183 scientific >= 0.3.4 && < 0.4,
187184 tagsoup >= 0.13.1 && < 0.15,
188− template-haskell >= 2.14 && < 2.15,
189185 text >= 0.11 && < 1.3,
190186 time >= 1.8 && < 1.10,
191187 time-locale-compat >= 0.1 && < 0.2,
192188 unordered-containers >= 0.2 && < 0.3,
193189 vector >= 0.11 && < 0.13,
194− yaml >= 0.8.11 && < 0.12
190+ yaml >= 0.8.11 && < 0.10,
191+ optparse-applicative >= 0.12 && < 0.15,
192+ file-embed >= 0.0.10.1 && < 0.0.11
195193
196194 If flag(previewServer)
197195 Build-depends:
@@ -229,8 +227,8 @@ Library
229227 Other-Modules:
230228 Hakyll.Web.Pandoc.Binary
231229 Build-Depends:
232− pandoc >= 2.0.5 && < 2.8,
233− pandoc-citeproc >= 0.14 && < 0.17
230+ pandoc >= 2.0.5 && < 2.3,
231+ pandoc-citeproc >= 0.14 && < 0.15
234232 Cpp-options:
235233 -DUSE_PANDOC
236234
@@ -255,25 +253,24 @@ Test-suite hakyll-tests
255253 Hakyll.Web.CompressCss.Tests
256254 Hakyll.Web.Html.RelativizeUrls.Tests
257255 Hakyll.Web.Html.Tests
258− Hakyll.Web.Tags.Tests
259256 Hakyll.Web.Template.Context.Tests
260257 Hakyll.Web.Template.Tests
261258 TestSuite.Util
262259
263260 Build-Depends:
264261 hakyll,
265− QuickCheck >= 2.8 && < 2.14,
266− tasty >= 0.11 && < 1.3,
262+ QuickCheck >= 2.8 && < 2.12,
263+ tasty >= 0.11 && < 1.2,
267264 tasty-hunit >= 0.9 && < 0.11,
268265 tasty-quickcheck >= 0.8 && < 0.11,
269266 -- Copy pasted from hakyll dependencies:
270267 base >= 4.8 && < 5,
271268 bytestring >= 0.9 && < 0.11,
272− containers >= 0.3 && < 0.7,
269+ containers >= 0.3 && < 0.6,
273270 filepath >= 1.0 && < 1.5,
274271 text >= 0.11 && < 1.3,
275272 unordered-containers >= 0.2 && < 0.3,
276− yaml >= 0.8.11 && < 0.12
273+ yaml >= 0.8.11 && < 0.10
277274
278275 If flag(previewServer)
279276 Cpp-options:
@@ -324,4 +321,4 @@ Executable hakyll-website
324321 base >= 4 && < 5,
325322 directory >= 1.0 && < 1.4,
326323 filepath >= 1.0 && < 1.5,
327− pandoc >= 2.0.5 && < 2.8
324+ pandoc >= 2.0.5 && < 2.3
lib/Hakyll/Core/Compiler.hs+2−35
@@ -24,16 +24,13 @@ module Hakyll.Core.Compiler
2424 , cached
2525 , unsafeCompiler
2626 , debugCompiler
27− , noResult
28− , withErrorMessage
2927 ) where
3028
3129
3230--------------------------------------------------------------------------------
33−import Control.Monad (unless, when, (>=>))
31+import Control.Monad (when, unless)
3432import Data.Binary (Binary)
3533import Data.ByteString.Lazy (ByteString)
36−import qualified Data.List.NonEmpty as NonEmpty
3734import Data.Typeable (Typeable)
3835import System.Environment (getProgName)
3936import System.FilePath (takeExtension)
@@ -65,7 +62,6 @@ getUnderlyingExtension = takeExtension . toFilePath <$> getUnderlying
6562
6663
6764--------------------------------------------------------------------------------
68−-- | Create an item from the underlying identifier and a given value.
6965makeItem :: a -> Compiler (Item a)
7066makeItem x = do
7167 identifier <- getUnderlying
@@ -145,10 +141,6 @@ saveSnapshot snapshot item = do
145141
146142
147143--------------------------------------------------------------------------------
148−-- | Turn on caching for a compilation value to avoid recomputing it
149−-- on subsequent Hakyll runs.
150−-- The storage key consists of the underlying identifier of the compiled
151−-- ressource and the given name.
152144cached :: (Binary a, Typeable a)
153145 => String
154146 -> Compiler a
@@ -185,37 +177,12 @@ cached name compiler = do
185177
186178
187179--------------------------------------------------------------------------------
188−-- | Run an IO computation without dependencies in a Compiler
189180unsafeCompiler :: IO a -> Compiler a
190181unsafeCompiler = compilerUnsafeIO
191182
192183
193184--------------------------------------------------------------------------------
194−-- | Fail so that it is treated as non-defined in an @\$if()\$@ branching
195−-- "Hakyll.Web.Template" macro, and alternative
196−-- 'Hakyll.Web.Template.Context.Context's are tried
197−--
198−-- @since 4.13.0
199−noResult :: String -> Compiler a
200−noResult = compilerNoResult . return
201−
202−
203−--------------------------------------------------------------------------------
204−-- | Prepend an error line to the error, if there is one. This allows you to
205−-- add helpful context to error messages.
206−--
207−-- @since 4.13.0
208−withErrorMessage :: String -> Compiler a -> Compiler a
209−withErrorMessage x = do
210− compilerTry >=> either (compilerResult . CompilerError . prepend) return
211− where
212− prepend (CompilationFailure es) = CompilationFailure (x `NonEmpty.cons` es)
213− prepend (CompilationNoResult es) = CompilationNoResult (x : es)
214−
215−
216−--------------------------------------------------------------------------------
217−-- | Compiler for debugging purposes.
218−-- Passes a message to the debug logger that is printed in verbose mode.
185+-- | Compiler for debugging purposes
219186debugCompiler :: String -> Compiler ()
220187debugCompiler msg = do
221188 logger <- compilerLogger <$> compilerAsk
lib/Hakyll/Core/Compiler/Internal.hs+41−126
@@ -1,7 +1,6 @@
11--------------------------------------------------------------------------------
22-- | Internally used compiler module
33{-# LANGUAGE CPP #-}
4−{-# LANGUAGE DeriveFunctor #-}
54{-# LANGUAGE FlexibleInstances #-}
65{-# LANGUAGE GADTs #-}
76{-# LANGUAGE GeneralizedNewtypeDeriving #-}
@@ -11,26 +10,19 @@ module Hakyll.Core.Compiler.Internal
1110 Snapshot
1211 , CompilerRead (..)
1312 , CompilerWrite (..)
14− , CompilerErrors (..)
1513 , CompilerResult (..)
1614 , Compiler (..)
1715 , runCompiler
1816
1917 -- * Core operations
20− , compilerResult
2118 , compilerTell
2219 , compilerAsk
23− , compilerUnsafeIO
24−
25− -- * Error operations
2620 , compilerThrow
27− , compilerNoResult
2821 , compilerCatch
29− , compilerTry
30− , compilerErrorMessages
22+ , compilerResult
23+ , compilerUnsafeIO
3124
3225 -- * Utilities
33− , compilerDebugEntries
3426 , compilerTellDependencies
3527 , compilerTellCacheHits
3628 ) where
@@ -40,9 +32,7 @@ module Hakyll.Core.Compiler.Internal
4032import Control.Applicative (Alternative (..))
4133import Control.Exception (SomeException, handle)
4234import Control.Monad (forM_)
43−import Control.Monad.Except (MonadError (..))
44−import Data.List.NonEmpty (NonEmpty (..))
45−import qualified Data.List.NonEmpty as NonEmpty
35+import Control.Monad.Except (MonadError (..))
4636#if MIN_VERSION_base(4,9,0)
4737import Data.Semigroup (Semigroup (..))
4838#endif
@@ -55,6 +45,7 @@ import Hakyll.Core.Configuration
5545import Hakyll.Core.Dependencies
5646import Hakyll.Core.Identifier
5747import Hakyll.Core.Identifier.Pattern
48+import Hakyll.Core.Logger (Logger)
5849import qualified Hakyll.Core.Logger as Logger
5950import Hakyll.Core.Metadata
6051import Hakyll.Core.Provider
@@ -84,7 +75,7 @@ data CompilerRead = CompilerRead
8475 , -- | Compiler store
8576 compilerStore :: Store
8677 , -- | Logger
87− compilerLogger :: Logger.Logger
78+ compilerLogger :: Logger
8879 }
8980
9081
@@ -113,29 +104,11 @@ instance Monoid CompilerWrite where
113104
114105
115106--------------------------------------------------------------------------------
116−-- | Distinguishes reasons in a 'CompilerError'
117−data CompilerErrors a
118− -- | One or more exceptions occured during compilation
119− = CompilationFailure (NonEmpty a)
120− -- | Absence of any result, most notably in template contexts. May still
121− -- have error messages.
122− | CompilationNoResult [a]
123− deriving Functor
124−
125−
126−-- | Unwrap a `CompilerErrors`
127−compilerErrorMessages :: CompilerErrors a -> [a]
128−compilerErrorMessages (CompilationFailure x) = NonEmpty.toList x
129−compilerErrorMessages (CompilationNoResult x) = x
130−
131−
132−--------------------------------------------------------------------------------
133−-- | An intermediate result of a compilation step
134−data CompilerResult a
135− = CompilerDone a CompilerWrite
136− | CompilerSnapshot Snapshot (Compiler a)
137− | CompilerRequire (Identifier, Snapshot) (Compiler a)
138− | CompilerError (CompilerErrors String)
107+data CompilerResult a where
108+ CompilerDone :: a -> CompilerWrite -> CompilerResult a
109+ CompilerSnapshot :: Snapshot -> Compiler a -> CompilerResult a
110+ CompilerError :: [String] -> CompilerResult a
111+ CompilerRequire :: (Identifier, Snapshot) -> Compiler a -> CompilerResult a
139112
140113
141114--------------------------------------------------------------------------------
@@ -153,14 +126,14 @@ instance Functor Compiler where
153126 return $ case res of
154127 CompilerDone x w -> CompilerDone (f x) w
155128 CompilerSnapshot s c' -> CompilerSnapshot s (fmap f c')
156− CompilerRequire i c' -> CompilerRequire i (fmap f c')
157129 CompilerError e -> CompilerError e
130+ CompilerRequire i c' -> CompilerRequire i (fmap f c')
158131 {-# INLINE fmap #-}
159132
160133
161134--------------------------------------------------------------------------------
162135instance Monad Compiler where
163− return x = compilerResult $ CompilerDone x mempty
136+ return x = Compiler $ \_ -> return $ CompilerDone x mempty
164137 {-# INLINE return #-}
165138
166139 Compiler c >>= f = Compiler $ \r -> do
@@ -173,14 +146,14 @@ instance Monad Compiler where
173146 CompilerSnapshot s c' -> CompilerSnapshot s $ do
174147 compilerTell w -- Save dependencies!
175148 c'
149+ CompilerError e -> CompilerError e
176150 CompilerRequire i c' -> CompilerRequire i $ do
177151 compilerTell w -- Save dependencies!
178152 c'
179− CompilerError e -> CompilerError e
180153
181154 CompilerSnapshot s c' -> return $ CompilerSnapshot s (c' >>= f)
182− CompilerRequire i c' -> return $ CompilerRequire i (c' >>= f)
183155 CompilerError e -> return $ CompilerError e
156+ CompilerRequire i c' -> return $ CompilerRequire i (c' >>= f)
184157 {-# INLINE (>>=) #-}
185158
186159 fail = compilerThrow . return
@@ -197,145 +170,87 @@ instance Applicative Compiler where
197170
198171
199172--------------------------------------------------------------------------------
200−-- | Access provided metadata from anywhere
201173instance MonadMetadata Compiler where
202174 getMetadata = compilerGetMetadata
203175 getMatches = compilerGetMatches
204176
205177
206178--------------------------------------------------------------------------------
207−-- | Compilation may fail with multiple error messages.
208−-- 'catchError' handles errors from 'throwError', 'fail' and 'Hakyll.Core.Compiler.noResult'
209179instance MonadError [String] Compiler where
210− throwError = compilerThrow
211− catchError c = compilerCatch c . (. compilerErrorMessages)
180+ throwError = compilerThrow
181+ catchError = compilerCatch
212182
213183
214184--------------------------------------------------------------------------------
215−-- | Like 'unCompiler' but treating IO exceptions as 'CompilerError's
216185runCompiler :: Compiler a -> CompilerRead -> IO (CompilerResult a)
217186runCompiler compiler read' = handle handler $ unCompiler compiler read'
218187 where
219188 handler :: SomeException -> IO (CompilerResult a)
220− handler e = return $ CompilerError $ CompilationFailure $ show e :| []
189+ handler e = return $ CompilerError [show e]
221190
222191
223192--------------------------------------------------------------------------------
224−-- | Trying alternative compilers if the first fails, regardless whether through
225−-- 'fail', 'throwError' or 'Hakyll.Core.Compiler.noResult'.
226−-- Aggregates error messages if all fail.
227193instance Alternative Compiler where
228− empty = compilerNoResult []
229− x <|> y = x `compilerCatch` (\rx -> y `compilerCatch` (\ry ->
230− case (rx, ry) of
231− (CompilationFailure xs, CompilationFailure ys) ->
232− compilerThrow $ NonEmpty.toList xs ++ NonEmpty.toList ys
233− (CompilationFailure xs, CompilationNoResult ys) ->
234− debug ys >> compilerThrow (NonEmpty.toList xs)
235− (CompilationNoResult xs, CompilationFailure ys) ->
236− debug xs >> compilerThrow (NonEmpty.toList ys)
237− (CompilationNoResult xs, CompilationNoResult ys) -> compilerNoResult $ xs ++ ys
238− ))
239− where
240− debug = compilerDebugEntries "Hakyll.Core.Compiler.Internal: Alternative fail suppressed"
194+ empty = compilerThrow []
195+ x <|> y = compilerCatch x $ \es -> do
196+ logger <- compilerLogger <$> compilerAsk
197+ forM_ es $ \e -> compilerUnsafeIO $ Logger.debug logger $
198+ "Hakyll.Core.Compiler.Internal: Alternative failed: " ++ e
199+ y
241200 {-# INLINE (<|>) #-}
242201
243202
244203--------------------------------------------------------------------------------
245−-- | Put the result back in a compiler
246−compilerResult :: CompilerResult a -> Compiler a
247−compilerResult x = Compiler $ \_ -> return x
248−{-# INLINE compilerResult #-}
249−
250−
251−--------------------------------------------------------------------------------
252−-- | Get the current environment
253204compilerAsk :: Compiler CompilerRead
254205compilerAsk = Compiler $ \r -> return $ CompilerDone r mempty
255206{-# INLINE compilerAsk #-}
256207
257208
258209--------------------------------------------------------------------------------
259−-- | Put a 'CompilerWrite'
260210compilerTell :: CompilerWrite -> Compiler ()
261−compilerTell = compilerResult . CompilerDone ()
211+compilerTell deps = Compiler $ \_ -> return $ CompilerDone () deps
262212{-# INLINE compilerTell #-}
263213
264214
265215--------------------------------------------------------------------------------
266−-- | Run an IO computation without dependencies in a Compiler
267−compilerUnsafeIO :: IO a -> Compiler a
268−compilerUnsafeIO io = Compiler $ \_ -> do
269− x <- io
270− return $ CompilerDone x mempty
271−{-# INLINE compilerUnsafeIO #-}
272−
273−
274−--------------------------------------------------------------------------------
275−-- | Throw errors in the 'Compiler'.
276−--
277−-- If no messages are given, this is considered a 'CompilationNoResult' error.
278−-- Otherwise, it is treated as a proper compilation failure.
279216compilerThrow :: [String] -> Compiler a
280−compilerThrow = compilerResult . CompilerError .
281− maybe (CompilationNoResult []) CompilationFailure .
282− NonEmpty.nonEmpty
283−
284−-- | Put a 'CompilerError' with multiple messages as 'CompilationNoResult'
285−compilerNoResult :: [String] -> Compiler a
286−compilerNoResult = compilerResult . CompilerError . CompilationNoResult
217+compilerThrow es = Compiler $ \_ -> return $ CompilerError es
218+{-# INLINE compilerThrow #-}
287219
288220
289221--------------------------------------------------------------------------------
290−-- | Allows to distinguish 'CompilerError's and branch on them with 'Either'
291−--
292−-- prop> compilerTry = (`compilerCatch` return . Left) . fmap Right
293−compilerTry :: Compiler a -> Compiler (Either (CompilerErrors String) a)
294−compilerTry (Compiler x) = Compiler $ \r -> do
295− res <- x r
296− case res of
297− CompilerDone res' w -> return (CompilerDone (Right res') w)
298− CompilerSnapshot s c -> return (CompilerSnapshot s (compilerTry c))
299− CompilerRequire i c -> return (CompilerRequire i (compilerTry c))
300− CompilerError e -> return (CompilerDone (Left e) mempty)
301−{-# INLINE compilerTry #-}
302−
303−
304−--------------------------------------------------------------------------------
305−-- | Allows you to recover from 'CompilerError's.
306−-- Uses the same parameter order as 'catchError' so that it can be used infix.
307−--
308−-- prop> c `compilerCatch` f = compilerTry c >>= either f return
309−compilerCatch :: Compiler a -> (CompilerErrors String -> Compiler a) -> Compiler a
222+compilerCatch :: Compiler a -> ([String] -> Compiler a) -> Compiler a
310223compilerCatch (Compiler x) f = Compiler $ \r -> do
311224 res <- x r
312225 case res of
313226 CompilerDone res' w -> return (CompilerDone res' w)
314227 CompilerSnapshot s c -> return (CompilerSnapshot s (compilerCatch c f))
315− CompilerRequire i c -> return (CompilerRequire i (compilerCatch c f))
316228 CompilerError e -> unCompiler (f e) r
229+ CompilerRequire i c -> return (CompilerRequire i (compilerCatch c f))
317230{-# INLINE compilerCatch #-}
318231
319232
320233--------------------------------------------------------------------------------
321−compilerDebugLog :: [String] -> Compiler ()
322−compilerDebugLog ms = do
323− logger <- compilerLogger <$> compilerAsk
324− compilerUnsafeIO $ forM_ ms $ Logger.debug logger
234+-- | Put the result back in a compiler
235+compilerResult :: CompilerResult a -> Compiler a
236+compilerResult x = Compiler $ \_ -> return x
237+{-# INLINE compilerResult #-}
238+
325239
326240--------------------------------------------------------------------------------
327−-- | Pass a list of messages with a heading to the debug logger
328−compilerDebugEntries :: String -> [String] -> Compiler ()
329−compilerDebugEntries msg = compilerDebugLog . (msg:) . map indent
330− where
331− indent = unlines . map (" "++) . lines
241+compilerUnsafeIO :: IO a -> Compiler a
242+compilerUnsafeIO io = Compiler $ \_ -> do
243+ x <- io
244+ return $ CompilerDone x mempty
245+{-# INLINE compilerUnsafeIO #-}
332246
333247
334248--------------------------------------------------------------------------------
335249compilerTellDependencies :: [Dependency] -> Compiler ()
336250compilerTellDependencies ds = do
337− compilerDebugLog $ map (\d ->
338− "Hakyll.Core.Compiler.Internal: Adding dependency: " ++ show d) ds
251+ logger <- compilerLogger <$> compilerAsk
252+ forM_ ds $ \d -> compilerUnsafeIO $ Logger.debug logger $
253+ "Hakyll.Core.Compiler.Internal: Adding dependency: " ++ show d
339254 compilerTell mempty {compilerDependencies = ds}
340255{-# INLINE compilerTellDependencies #-}
341256
lib/Hakyll/Core/Compiler/Require.hs−2
@@ -91,7 +91,6 @@ loadBody id' = loadSnapshotBody id' final
9191
9292
9393--------------------------------------------------------------------------------
94−-- | A shortcut for only requiring the body for a specific snapshot of an item
9594loadSnapshotBody :: (Binary a, Typeable a)
9695 => Identifier -> Snapshot -> Compiler a
9796loadSnapshotBody id' snapshot = fmap itemBody $ loadSnapshot id' snapshot
@@ -104,7 +103,6 @@ loadAll pattern = loadAllSnapshots pattern final
104103
105104
106105--------------------------------------------------------------------------------
107−-- | Load a specific snapshot for each of dynamic list of items
108106loadAllSnapshots :: (Binary a, Typeable a)
109107 => Pattern -> Snapshot -> Compiler [Item a]
110108loadAllSnapshots pattern snapshot = do
lib/Hakyll/Core/Identifier.hs+1−1
@@ -62,7 +62,7 @@ instance Show Identifier where
6262
6363--------------------------------------------------------------------------------
6464-- | Parse an identifier from a string
65−fromFilePath :: FilePath -> Identifier
65+fromFilePath :: String -> Identifier
6666fromFilePath = Identifier Nothing .
6767 intercalate "/" . filter (not . null) . split'
6868 where
lib/Hakyll/Core/Logger.hs+3−9
@@ -18,7 +18,6 @@ import Control.Concurrent.Chan (Chan, newChan, readChan, writeChan)
1818import Control.Concurrent.MVar (MVar, newEmptyMVar, putMVar, takeMVar)
1919import Control.Monad (forever)
2020import Control.Monad.Trans (MonadIO, liftIO)
21−import Data.List (intercalate)
2221import Prelude hiding (error)
2322
2423
@@ -80,7 +79,7 @@ string l v m
8079
8180--------------------------------------------------------------------------------
8281error :: MonadIO m => Logger -> String -> m ()
83−error l m = string l Error $ " [ERROR] " ++ indent m
82+error l m = string l Error $ " [ERROR] " ++ m
8483
8584
8685--------------------------------------------------------------------------------
@@ -90,14 +89,9 @@ header l = string l Message
9089
9190--------------------------------------------------------------------------------
9291message :: MonadIO m => Logger -> String -> m ()
93−message l m = string l Message $ " " ++ indent m
92+message l m = string l Message $ " " ++ m
9493
9594
9695--------------------------------------------------------------------------------
9796debug :: MonadIO m => Logger -> String -> m ()
98−debug l m = string l Debug $ " [DEBUG] " ++ indent m
99−
100−
101−--------------------------------------------------------------------------------
102−indent :: String -> String
103−indent = intercalate "\n " . lines
97+debug l m = string l Debug $ " [DEBUG] " ++ m
lib/Hakyll/Core/Provider/Metadata.hs+2−3
@@ -27,7 +27,6 @@ import Hakyll.Core.Identifier
2727import Hakyll.Core.Metadata
2828import Hakyll.Core.Provider.Internal
2929import System.IO as IO
30−import System.IO.Error (modifyIOError, ioeSetLocation)
3130
3231
3332--------------------------------------------------------------------------------
@@ -52,7 +51,7 @@ loadMetadata p identifier = do
5251--------------------------------------------------------------------------------
5352loadMetadataHeader :: FilePath -> IO (Metadata, String)
5453loadMetadataHeader fp = do
55− fileContent <- modifyIOError (`ioeSetLocation` "loadMetadataHeader") $ readFile fp
54+ fileContent <- readFile fp
5655 case parsePage fileContent of
5756 Right x -> return x
5857 Left err -> throwIO $ MetadataException fp err
@@ -61,7 +60,7 @@ loadMetadataHeader fp = do
6160--------------------------------------------------------------------------------
6261loadMetadataFile :: FilePath -> IO Metadata
6362loadMetadataFile fp = do
64− fileContent <- modifyIOError (`ioeSetLocation` "loadMetadataFile") $ B.readFile fp
63+ fileContent <- B.readFile fp
6564 let errOrMeta = Yaml.decodeEither' fileContent
6665 either (fail . show) return errOrMeta
6766
lib/Hakyll/Core/Runtime.hs+3−3
@@ -199,9 +199,9 @@ chase trail id'
199199 result <- liftIO $ runCompiler compiler read'
200200 case result of
201201 -- Rethrow error
202− CompilerError e -> throwError $ case compilerErrorMessages e of
203− [] -> "Compiler failed but no info given, try running with -v?"
204− es -> intercalate "; " es
202+ CompilerError [] -> throwError
203+ "Compiler failed but no info given, try running with -v?"
204+ CompilerError es -> throwError $ intercalate "; " es
205205
206206 -- Signal that a snapshot was saved ->
207207 CompilerSnapshot snapshot c -> do
lib/Hakyll/Core/Store.hs+23−26
@@ -16,6 +16,7 @@ module Hakyll.Core.Store
1616
1717
1818--------------------------------------------------------------------------------
19+import Control.Exception (IOException, handle)
1920import qualified Crypto.Hash.MD5 as MD5
2021import Data.Binary (Binary, decode, encodeFile)
2122import qualified Data.ByteString as B
@@ -26,13 +27,11 @@ import Data.Maybe (isJust)
2627import qualified Data.Text as T
2728import qualified Data.Text.Encoding as T
2829import Data.Typeable (TypeRep, Typeable, cast, typeOf)
29−import Numeric (showHex)
3030import System.Directory (createDirectoryIfMissing)
3131import System.Directory (doesFileExist, removeFile)
3232import System.FilePath ((</>))
3333import System.IO (IOMode (..), hClose, openFile)
34−import System.IO.Error (catchIOError, ioeSetFileName,
35− ioeSetLocation, modifyIOError)
34+import Text.Printf (printf)
3635
3736
3837--------------------------------------------------------------------------------
@@ -85,14 +84,6 @@ new inMemory directory = do
8584 where
8685 csize = Just 500
8786
88−--------------------------------------------------------------------------------
89−withStore :: Store -> String -> (String -> FilePath -> IO a) -> [String] -> IO a
90−withStore store loc run identifier = modifyIOError handle $ run key path
91− where
92− key = hash identifier
93− path = storeDirectory store </> key
94− handle e = e `ioeSetFileName` (path ++ " for " ++ intercalate "/" identifier)
95− `ioeSetLocation` ("Store." ++ loc)
9687
9788--------------------------------------------------------------------------------
9889-- | Auxiliary: add an item to the in-memory cache
@@ -133,16 +124,17 @@ cacheDelete (Store _ (Just lru)) key = do
133124--------------------------------------------------------------------------------
134125-- | Store an item
135126set :: (Binary a, Typeable a) => Store -> [String] -> a -> IO ()
136−set store identifier value = withStore store "set" (\key path -> do
137− encodeFile path value
127+set store identifier value = do
128+ encodeFile (storeDirectory store </> key) value
138129 cacheInsert store key value
139− ) identifier
130+ where
131+ key = hash identifier
140132
141133
142134--------------------------------------------------------------------------------
143135-- | Load an item
144136get :: (Binary a, Typeable a) => Store -> [String] -> IO (Result a)
145−get store = withStore store "get" $ \key path -> do
137+get store identifier = do
146138 -- First check the in-memory map
147139 ref <- cacheLookup store key
148140 case ref of
@@ -154,14 +146,17 @@ get store = withStore store "get" $ \key path -> do
154146 then return NotFound
155147 -- Found in the filesystem
156148 else do
157− v <- decodeClose path
149+ v <- decodeClose
158150 cacheInsert store key v
159151 return $ Found v
160152 -- Found in the in-memory map (or wrong type), just return
161153 s -> return s
162154 where
155+ key = hash identifier
156+ path = storeDirectory store </> key
157+
163158 -- 'decodeFile' from Data.Binary which closes the file ASAP
164− decodeClose path = do
159+ decodeClose = do
165160 h <- openFile path ReadMode
166161 lbs <- BL.hGetContents h
167162 BL.length lbs `seq` hClose h
@@ -171,30 +166,32 @@ get store = withStore store "get" $ \key path -> do
171166--------------------------------------------------------------------------------
172167-- | Strict function
173168isMember :: Store -> [String] -> IO Bool
174−isMember store = withStore store "isMember" $ \key path -> do
169+isMember store identifier = do
175170 inCache <- cacheIsMember store key
176171 if inCache then return True else doesFileExist path
172+ where
173+ key = hash identifier
174+ path = storeDirectory store </> key
177175
178176
179177--------------------------------------------------------------------------------
180178-- | Delete an item
181179delete :: Store -> [String] -> IO ()
182−delete store = withStore store "delete" $ \key path -> do
180+delete store identifier = do
183181 cacheDelete store key
184− deleteFile path
182+ deleteFile $ storeDirectory store </> key
183+ where
184+ key = hash identifier
185185
186186
187187--------------------------------------------------------------------------------
188188-- | Delete a file unless it doesn't exist...
189189deleteFile :: FilePath -> IO ()
190−deleteFile = (`catchIOError` \_ -> return ()) . removeFile
190+deleteFile = handle (\(_ :: IOException) -> return ()) . removeFile
191191
192192
193193--------------------------------------------------------------------------------
194194-- | Mostly meant for internal usage
195195hash :: [String] -> String
196−hash = toHex . B.unpack . MD5.hash . T.encodeUtf8 . T.pack . intercalate "/"
197− where
198− toHex [] = ""
199− toHex (x : xs) | x < 16 = '0' : showHex x (toHex xs)
200− | otherwise = showHex x (toHex xs)
196+hash = concatMap (printf "%02x") . B.unpack .
197+ MD5.hash . T.encodeUtf8 . T.pack . intercalate "/"
lib/Hakyll/Web/Feed.hs+32−51
@@ -1,5 +1,4 @@
1−{-# LANGUAGE OverloadedStrings #-}
2−{-# LANGUAGE TemplateHaskell #-}
1+{-# LANGUAGE TemplateHaskell #-}
32
43--------------------------------------------------------------------------------
54-- | A Module that allows easy rendering of RSS feeds.
@@ -23,8 +22,6 @@ module Hakyll.Web.Feed
2322 ( FeedConfiguration (..)
2423 , renderRss
2524 , renderAtom
26− , renderRssWithTemplates
27− , renderAtomWithTemplates
2825 ) where
2926
3027
@@ -38,26 +35,27 @@ import Hakyll.Web.Template.List
3835
3936
4037--------------------------------------------------------------------------------
41−import Data.FileEmbed (makeRelativeToProject)
38+import Data.FileEmbed (makeRelativeToProject, embedFile)
39+import qualified Data.Text as T
40+import qualified Data.Text.Encoding as T
4241
4342
4443--------------------------------------------------------------------------------
45−rssTemplate :: Template
46−rssTemplate =
47− $(makeRelativeToProject "data/templates/rss.xml" >>= embedTemplate)
44+rssTemplate :: String
45+rssTemplate = T.unpack $
46+ T.decodeUtf8 $(makeRelativeToProject "data/templates/rss.xml" >>= embedFile)
4847
49−rssItemTemplate :: Template
50−rssItemTemplate =
51− $(makeRelativeToProject "data/templates/rss-item.xml" >>= embedTemplate)
48+rssItemTemplate :: String
49+rssItemTemplate = T.unpack $
50+ T.decodeUtf8 $(makeRelativeToProject "data/templates/rss-item.xml" >>= embedFile)
5251
53−atomTemplate :: Template
54−atomTemplate =
55− $(makeRelativeToProject "data/templates/atom.xml" >>= embedTemplate)
56−
57−atomItemTemplate :: Template
58−atomItemTemplate =
59− $(makeRelativeToProject "data/templates/atom-item.xml" >>= embedTemplate)
52+atomTemplate :: String
53+atomTemplate = T.unpack $
54+ T.decodeUtf8 $(makeRelativeToProject "data/templates/atom.xml" >>= embedFile)
6055
56+atomItemTemplate :: String
57+atomItemTemplate = T.unpack $
58+ T.decodeUtf8 $(makeRelativeToProject "data/templates/atom-item.xml" >>= embedFile)
6159
6260--------------------------------------------------------------------------------
6361-- | This is a data structure to keep the configuration of a feed.
@@ -77,13 +75,16 @@ data FeedConfiguration = FeedConfiguration
7775
7876--------------------------------------------------------------------------------
7977-- | Abstract function to render any feed.
80−renderFeed :: Template -- ^ Default feed template
81− -> Template -- ^ Default item template
78+renderFeed :: String -- ^ Default feed template
79+ -> String -- ^ Default item template
8280 -> FeedConfiguration -- ^ Feed configuration
8381 -> Context String -- ^ Context for the items
8482 -> [Item String] -- ^ Input items
8583 -> Compiler (Item String) -- ^ Resulting item
86−renderFeed feedTpl itemTpl config itemContext items = do
84+renderFeed defFeed defItem config itemContext items = do
85+ feedTpl <- readTemplateFile defFeed
86+ itemTpl <- readTemplateFile defItem
87+
8788 protectedItems <- mapM (applyFilter protectCDATA) items
8889 body <- makeItem =<< applyTemplateList itemTpl itemContext' protectedItems
8990 applyTemplate feedTpl feedContext body
@@ -117,35 +118,11 @@ renderFeed feedTpl itemTpl config itemContext items = do
117118 updatedField = field "updated" $ \_ -> case items of
118119 [] -> return "Unknown"
119120 (x : _) -> unContext itemContext' "updated" [] x >>= \cf -> case cf of
121+ ListField _ _ -> fail "Hakyll.Web.Feed.renderFeed: Internal error"
120122 StringField s -> return s
121− _ -> fail "Hakyll.Web.Feed.renderFeed: Internal error"
122−
123−--------------------------------------------------------------------------------
124−-- | Render an RSS feed using given templates with a number of items.
125−renderRssWithTemplates ::
126− Template -- ^ Feed template
127− -> Template -- ^ Item template
128− -> FeedConfiguration -- ^ Feed configuration
129− -> Context String -- ^ Item context
130− -> [Item String] -- ^ Feed items
131− -> Compiler (Item String) -- ^ Resulting feed
132−renderRssWithTemplates feedTemplate itemTemplate config context = renderFeed
133− feedTemplate itemTemplate config
134− (makeItemContext "%a, %d %b %Y %H:%M:%S UT" context)
135123
136−
137−--------------------------------------------------------------------------------
138−-- | Render an Atom feed using given templates with a number of items.
139−renderAtomWithTemplates ::
140− Template -- ^ Feed template
141− -> Template -- ^ Item template
142− -> FeedConfiguration -- ^ Feed configuration
143− -> Context String -- ^ Item context
144− -> [Item String] -- ^ Feed items
145− -> Compiler (Item String) -- ^ Resulting feed
146−renderAtomWithTemplates feedTemplate itemTemplate config context = renderFeed
147− feedTemplate itemTemplate config
148− (makeItemContext "%Y-%m-%dT%H:%M:%SZ" context)
124+ readTemplateFile :: String -> Compiler Template
125+ readTemplateFile value = pure $ template $ readTemplateElems value
149126
150127
151128--------------------------------------------------------------------------------
@@ -154,7 +131,9 @@ renderRss :: FeedConfiguration -- ^ Feed configuration
154131 -> Context String -- ^ Item context
155132 -> [Item String] -- ^ Feed items
156133 -> Compiler (Item String) -- ^ Resulting feed
157−renderRss = renderRssWithTemplates rssTemplate rssItemTemplate
134+renderRss config context = renderFeed
135+ rssTemplate rssItemTemplate config
136+ (makeItemContext "%a, %d %b %Y %H:%M:%S UT" context)
158137
159138
160139--------------------------------------------------------------------------------
@@ -163,11 +142,13 @@ renderAtom :: FeedConfiguration -- ^ Feed configuration
163142 -> Context String -- ^ Item context
164143 -> [Item String] -- ^ Feed items
165144 -> Compiler (Item String) -- ^ Resulting feed
166−renderAtom = renderAtomWithTemplates atomTemplate atomItemTemplate
145+renderAtom config context = renderFeed
146+ atomTemplate atomItemTemplate config
147+ (makeItemContext "%Y-%m-%dT%H:%M:%SZ" context)
167148
168149
169150--------------------------------------------------------------------------------
170151-- | Copies @$updated$@ from @$published$@ if it is not already set.
171152makeItemContext :: String -> Context a -> Context a
172153makeItemContext fmt context = mconcat
173− [context, dateField "published" fmt, dateField "updated" fmt]
154+ [dateField "published" fmt, context, dateField "updated" fmt]
lib/Hakyll/Web/Html.hs+1−1
@@ -59,7 +59,7 @@ demoteHeaders = withTags $ \tag -> case tag of
5959
6060--------------------------------------------------------------------------------
6161isUrlAttribute :: String -> Bool
62−isUrlAttribute = (`elem` ["src", "href", "data", "poster", "srcset"])
62+isUrlAttribute = (`elem` ["src", "href", "data", "poster"])
6363
6464
6565--------------------------------------------------------------------------------
lib/Hakyll/Web/Tags.hs+1−2
@@ -43,7 +43,6 @@
4343module Hakyll.Web.Tags
4444 ( Tags (..)
4545 , getTags
46− , getCategory
4746 , buildTagsWith
4847 , buildTags
4948 , buildCategories
@@ -113,7 +112,7 @@ getTags identifier = do
113112
114113
115114--------------------------------------------------------------------------------
116−-- | Obtain category from a page.
115+-- | Obtain categories from a page.
117116getCategory :: MonadMetadata m => Identifier -> m [String]
118117getCategory = return . return . takeBaseName . takeDirectory . toFilePath
119118
lib/Hakyll/Web/Template.hs+2−24
@@ -138,41 +138,19 @@
138138-- > 3...2...1
139139-- > </p>
140140--
141−{-# LANGUAGE TemplateHaskell #-}
142141module Hakyll.Web.Template
143142 ( Template
143+ , template
144+ , readTemplateElems
144145 , templateBodyCompiler
145146 , templateCompiler
146147 , applyTemplate
147148 , loadAndApplyTemplate
148149 , applyAsTemplate
149150 , readTemplate
150− , compileTemplateItem
151151 , unsafeReadTemplateFile
152− , embedTemplate
153152 ) where
154153
155154
156155--------------------------------------------------------------------------------
157156import Hakyll.Web.Template.Internal
158−
159−
160−--------------------------------------------------------------------------------
161−import Data.FileEmbed (embedFile)
162−import qualified Data.Text as T
163−import qualified Data.Text.Encoding as T
164−import Language.Haskell.TH (Exp, Q)
165−
166−
167−--------------------------------------------------------------------------------
168−-- | Embed template allows you embed a template within the Haskell binary.
169−-- Example:
170−--
171−-- > myTemplate :: Template
172−-- > myTemplate = $(embedTemplate "test.html")
173−embedTemplate :: FilePath -> Q Exp
174−embedTemplate filePath = [|
175− let source = T.unpack $ T.decodeUtf8 $(embedFile filePath) in
176− case parseTemplateElemsFile filePath source of
177− Left err -> error err
178− Right tpl -> template filePath tpl |]
lib/Hakyll/Web/Template/Context.hs+32−122
@@ -1,27 +1,6 @@
1−-- | This module provides 'Context's which are used to expand expressions in
2−-- templates and allow for arbitrary customisation.
3−--
4−-- 'Template's define a small expression DSL which consists of strings,
5−-- identifiers and function application. There is no type system, every value is
6−-- a string and on the top level they get substituted verbatim into the page.
7−--
8−-- For example, you can build a context that contains
9−--
10−-- > … <> functionField "concat" (const . concat) <> …
11−--
12−-- which will allow you to use the @concat@ identifier as a function that takes
13−-- arbitrarily many stings and concatenates them to a new string:
14−--
15−-- > $partial(concat("templates/categories/", category))$
16−--
17−-- This will evaluate the @category@ field in the context, then prepend he path,
18−-- and include the referenced file as a template.
19−
20−
211--------------------------------------------------------------------------------
222{-# LANGUAGE CPP #-}
233{-# LANGUAGE ExistentialQuantification #-}
24−{-# LANGUAGE RankNTypes #-}
254module Hakyll.Web.Template.Context
265 ( ContextField (..)
276 , Context (..)
@@ -49,14 +28,13 @@ module Hakyll.Web.Template.Context
4928 , teaserField
5029 , teaserFieldWithSeparator
5130 , missingField
52− , bindItem
5331 ) where
5432
5533
5634--------------------------------------------------------------------------------
5735import Control.Applicative (Alternative (..))
5836import Control.Monad (msum)
59−import Data.List (intercalate, tails)
37+import Data.List (intercalate)
6038#if MIN_VERSION_base(4,9,0)
6139import Data.Semigroup (Semigroup (..))
6240#endif
@@ -72,16 +50,13 @@ import Hakyll.Core.Metadata
7250import Hakyll.Core.Provider
7351import Hakyll.Core.Util.String (needlePrefix, splitAll)
7452import Hakyll.Web.Html
75−import Prelude hiding (id)
76−import System.FilePath (dropExtension, splitDirectories,
77− takeBaseName)
53+import System.FilePath (splitDirectories, takeBaseName)
7854
7955
8056--------------------------------------------------------------------------------
8157-- | Mostly for internal usage
8258data ContextField
83− = EmptyField
84− | StringField String
59+ = StringField String
8560 | forall a. ListField (Context a) [Item a]
8661
8762
@@ -106,8 +81,6 @@ newtype Context a = Context
10681
10782
10883--------------------------------------------------------------------------------
109−-- | Tries to find a key in the left context,
110−-- or when that fails in the right context.
11184#if MIN_VERSION_base(4,9,0)
11285instance Semigroup (Context a) where
11386 (<>) (Context f) (Context g) = Context $ \k a i -> f k a i <|> g k a i
@@ -124,105 +97,64 @@ instance Monoid (Context a) where
12497
12598--------------------------------------------------------------------------------
12699field' :: String -> (Item a -> Compiler ContextField) -> Context a
127−field' key value = Context $ \k _ i ->
128− if k == key
129− then value i
130− else noResult $ "Tried field " ++ key
100+field' key value = Context $ \k _ i -> if k == key then value i else empty
131101
132102
133103--------------------------------------------------------------------------------
134−-- | Constructs a new field for a 'Context'.
135−-- If the key matches, the compiler is run and its result is substituted in the
136−-- template.
137−--
138−-- If the compiler fails, the field will be considered non-existent
139−-- in an @$if()$@ macro or ultimately break the template application
140−-- (unless the key is found in another context when using '<>').
141−-- Use 'empty' or 'noResult' for intentional failures of fields used in
142−-- @$if()$@, to distinguish them from exceptions thrown with 'fail'.
104+-- | Constructs a new field in the 'Context.'
143105field
144106 :: String -- ^ Key
145107 -> (Item a -> Compiler String) -- ^ Function that constructs a value based
146− -- on the item (e.g. accessing metadata)
108+ -- on the item
147109 -> Context a
148110field key value = field' key (fmap StringField . value)
149111
150112
151113--------------------------------------------------------------------------------
152114-- | Creates a 'field' to use with the @$if()$@ template macro.
153−-- Attempting to substitute the field into the template will cause an error.
154115boolField
155116 :: String
156117 -> (Item a -> Bool)
157118 -> Context a
158−boolField name f = field' name (\i -> if f i
159− then return EmptyField
160− else noResult $ "Field " ++ name ++ " is false")
119+boolField name f = field name (\i -> if f i
120+ then pure (error $ unwords ["no string value for bool field:",name])
121+ else empty)
161122
162123
163124--------------------------------------------------------------------------------
164−-- | Creates a 'field' that does not depend on the 'Item' but always yields
165−-- the same string
166−constField :: String -- ^ Key
167− -> String -- ^ Value
168− -> Context a
125+-- | Creates a 'field' that does not depend on the 'Item'
126+constField :: String -> String -> Context a
169127constField key = field key . const . return
170128
171129
172130--------------------------------------------------------------------------------
173−-- | Creates a list field to be consumed by a @$for(…)$@ expression.
174−-- The compiler returns multiple items which are rendered in the loop body
175−-- with the supplied context.
176131listField :: String -> Context a -> Compiler [Item a] -> Context b
177132listField key c xs = listFieldWith key c (const xs)
178133
179134
180135--------------------------------------------------------------------------------
181−-- | Produces a new 'Context' which has list field 'key'. All fields from
182−-- 'c' are also accessible from the produced context.
183−-- Be careful when doing @listFieldWith k ca f <> cb@ as any fields in @ca@
184−-- will override fields in @cb@ with the same name.
185−-- Creates a list field like 'listField', but supplies the current page
186−-- to the compiler.
187136listFieldWith
188137 :: String -> Context a -> (Item b -> Compiler [Item a]) -> Context b
189138listFieldWith key c f = field' key $ fmap (ListField c) . f
190139
191140
192141--------------------------------------------------------------------------------
193−-- | Creates a variadic function field.
194−--
195−-- The function will be called with the dynamically evaluated string arguments
196−-- from the template as well as the page that is currently rendered.
197−functionField :: String -- ^ Key
198− -> ([String] -> Item a -> Compiler String) -- ^ Function
199− -> Context a
142+functionField :: String -> ([String] -> Item a -> Compiler String) -> Context a
200143functionField name value = Context $ \k args i ->
201144 if k == name
202145 then StringField <$> value args i
203− else noResult $ "Tried function field " ++ name
146+ else empty
204147
205148
206149--------------------------------------------------------------------------------
207−-- | Transform the respective string results of all fields in a context.
208−-- For example,
209−--
210−-- > mapContext (++"c") (constField "x" "a" <> constField "y" "b")
211−--
212−-- is equivalent to
213−--
214−-- > constField "x" "ac" <> constField "y" "bc"
215−--
216150mapContext :: (String -> String) -> Context a -> Context a
217151mapContext f (Context c) = Context $ \k a i -> do
218152 fld <- c k a i
219153 case fld of
220− EmptyField -> wrongType "boolField"
221154 StringField str -> return $ StringField (f str)
222− _ -> wrongType "ListField"
223− where
224− wrongType typ = fail $ "Hakyll.Web.Template.Context.mapContext: " ++
225− "can't map over a " ++ typ ++ "!"
155+ ListField _ _ -> fail $
156+ "Hakyll.Web.Template.Context.mapContext: " ++
157+ "can't map over a ListField!"
226158
227159--------------------------------------------------------------------------------
228160-- | A context that allows snippet inclusion. In processed file, use as:
@@ -231,15 +163,15 @@ mapContext f (Context c) = Context $ \k a i -> do
231163-- > $snippet("path/to/snippet/")$
232164-- > ...
233165--
234−-- The contents of the included file will not be interpolated like @partial@
235−-- does it.
166+-- The contents of the included file will not be interpolated.
236167--
237168snippetField :: Context String
238169snippetField = functionField "snippet" f
239170 where
240171 f [contentsPath] _ = loadBody (fromFilePath contentsPath)
241− f [] _ = fail "No argument to function 'snippet()'"
242− f _ _ = fail "Too many arguments to function 'snippet()'"
172+ f _ i = error $
173+ "Too many arguments to function 'snippet()' in item " ++
174+ show (itemIdentifier i)
243175
244176--------------------------------------------------------------------------------
245177-- | A context that contains (in that order)
@@ -259,7 +191,8 @@ defaultContext =
259191 metadataField `mappend`
260192 urlField "url" `mappend`
261193 pathField "path" `mappend`
262− titleField "title"
194+ titleField "title" `mappend`
195+ missingField
263196
264197
265198--------------------------------------------------------------------------------
@@ -277,20 +210,15 @@ bodyField key = field key $ return . itemBody
277210-- | Map any field to its metadata value, if present
278211metadataField :: Context a
279212metadataField = Context $ \k _ i -> do
280− let id = itemIdentifier i
281− empty' = noResult $ "No '" ++ k ++ "' field in metadata " ++
282− "of item " ++ show id
283− value <- getMetadataField id k
284− maybe empty' (return . StringField) value
213+ value <- getMetadataField (itemIdentifier i) k
214+ maybe empty (return . StringField) value
285215
286216
287217--------------------------------------------------------------------------------
288218-- | Absolute url to the resulting item
289219urlField :: String -> Context a
290−urlField key = field key $ \i -> do
291− let id = itemIdentifier i
292− empty' = fail $ "No route url found for item " ++ show id
293− fmap (maybe empty' toUrl) $ getRoute id
220+urlField key = field key $
221+ fmap (maybe empty toUrl) . getRoute . itemIdentifier
294222
295223
296224--------------------------------------------------------------------------------
@@ -341,12 +269,6 @@ titleField = mapContext takeBaseName . pathField
341269-- that begins with @yyyy-mm-dd@ . For example:
342270-- @folder//yyyy-mm-dd-title//dist//main.extension@ .
343271-- In case of multiple matches, the rightmost one is used.
344−--
345−-- As another alternative, if none of the above matches, and the file has a
346−-- path which contains nested directories specifying a date, then that date
347−-- will be used. In other words, if the path is of the form
348−-- @**//yyyy//mm//dd//**//main.extension@ .
349−-- As above, in case of multiple matches, the rightmost one is used.
350272
351273dateField :: String -- ^ Key in which the rendered date should be placed
352274 -> String -- ^ Format to use on the date
@@ -357,7 +279,7 @@ dateField = dateFieldWith defaultTimeLocale
357279--------------------------------------------------------------------------------
358280-- | This is an extended version of 'dateField' that allows you to
359281-- specify a time locale that is used for outputting the date. For more
360−-- details, see 'dateField' and 'formatTime'.
282+-- details, see 'dateField'.
361283dateFieldWith :: TimeLocale -- ^ Output time locale
362284 -> String -- ^ Destination key
363285 -> String -- ^ Format to use on the date
@@ -378,13 +300,12 @@ getItemUTC :: MonadMetadata m
378300getItemUTC locale id' = do
379301 metadata <- getMetadata id'
380302 let tryField k fmt = lookupString k metadata >>= parseTime' fmt
381− paths = splitDirectories $ (dropExtension . toFilePath) id'
303+ paths = splitDirectories $ toFilePath id'
382304
383305 maybe empty' return $ msum $
384306 [tryField "published" fmt | fmt <- formats] ++
385307 [tryField "date" fmt | fmt <- formats] ++
386− [parseTime' "%Y-%m-%d" $ intercalate "-" $ take 3 $ splitAll "-" fnCand | fnCand <- reverse paths] ++
387− [parseTime' "%Y-%m-%d" $ intercalate "-" $ fnCand | fnCand <- map (take 3) $ reverse . tails $ paths]
308+ [parseTime' "%Y-%m-%d" $ intercalate "-" $ take 3 $ splitAll "-" fnCand | fnCand <- reverse paths]
388309 where
389310 empty' = fail $ "Hakyll.Web.Template.Context.getItemUTC: " ++
390311 "could not parse time for " ++ show id'
@@ -412,7 +333,6 @@ getItemModificationTime identifier = do
412333
413334
414335--------------------------------------------------------------------------------
415−-- | Creates a field with the last modification date of the underlying item.
416336modificationTimeField :: String -- ^ Key
417337 -> String -- ^ Format
418338 -> Context a -- ^ Resulting context
@@ -420,8 +340,6 @@ modificationTimeField = modificationTimeFieldWith defaultTimeLocale
420340
421341
422342--------------------------------------------------------------------------------
423−-- | Creates a field with the last modification date of the underlying item
424−-- in a custom localisation format (see 'formatTime').
425343modificationTimeFieldWith :: TimeLocale -- ^ Time output locale
426344 -> String -- ^ Key
427345 -> String -- ^ Format
@@ -460,11 +378,10 @@ teaserFieldWithSeparator separator key snapshot = field key $ \item -> do
460378
461379
462380--------------------------------------------------------------------------------
463−-- | Constantly reports any field as missing. Mostly for internal usage,
464−-- it is the last choice in every context used in a template application.
465381missingField :: Context a
466−missingField = Context $ \k _ _ -> noResult $
467− "Missing field '" ++ k ++ "' in context"
382+missingField = Context $ \k _ i -> fail $
383+ "Missing field $" ++ k ++ "$ in context for item " ++
384+ show (itemIdentifier i)
468385
469386parseTimeM :: Bool -> TimeLocale -> String -> String -> Maybe UTCTime
470387#if MIN_VERSION_time(1,5,0)
@@ -472,10 +389,3 @@ parseTimeM = TF.parseTimeM
472389#else
473390parseTimeM _ = TF.parseTime
474391#endif
475−
476−--------------------------------------------------------------------------------
477−
478−-- | Binds an 'Item' to a given 'Context', allowing it to be combined with any
479−-- other 'Context' of any type.
480−bindItem :: Context a -> Item a -> forall b. Context b
481−bindItem (Context ctx) ia = Context $ \k args _ -> ctx k args ia
lib/Hakyll/Web/Template/Internal.hs+44−103
@@ -1,18 +1,15 @@
1−{-# LANGUAGE DeriveAnyClass #-}
2−{-# LANGUAGE DeriveGeneric #-}
31{-# LANGUAGE GeneralizedNewtypeDeriving #-}
42{-# LANGUAGE ScopedTypeVariables #-}
5−
63module Hakyll.Web.Template.Internal
74 ( Template (..)
85 , template
96 , templateBodyCompiler
107 , templateCompiler
118 , applyTemplate
9+ , applyTemplate'
1210 , loadAndApplyTemplate
1311 , applyAsTemplate
1412 , readTemplate
15− , compileTemplateItem
1613 , unsafeReadTemplateFile
1714
1815 , module Hakyll.Web.Template.Internal.Element
@@ -21,19 +18,16 @@ module Hakyll.Web.Template.Internal
2118
2219
2320--------------------------------------------------------------------------------
24−import Control.Monad (forM)
25−import Control.Monad.Except (MonadError (..), catchError)
21+import Control.Monad.Except (MonadError (..))
2622import Data.Binary (Binary)
2723import Data.List (intercalate)
28−import qualified Data.List.NonEmpty as NonEmpty
2924import Data.Typeable (Typeable)
3025import GHC.Exts (IsString (..))
31−import GHC.Generics (Generic)
26+import Prelude hiding (id)
3227
3328
3429--------------------------------------------------------------------------------
3530import Hakyll.Core.Compiler
36−import Hakyll.Core.Compiler.Internal
3731import Hakyll.Core.Identifier
3832import Hakyll.Core.Item
3933import Hakyll.Core.Writable
@@ -44,10 +38,9 @@ import Hakyll.Web.Template.Internal.Trim
4438
4539--------------------------------------------------------------------------------
4640-- | Datatype used for template substitutions.
47−data Template = Template
48− { tplElements :: [TemplateElement]
49− , tplOrigin :: FilePath -- Only for error messages.
50− } deriving (Show, Eq, Generic, Binary, Typeable)
41+newtype Template = Template
42+ { unTemplate :: [TemplateElement]
43+ } deriving (Show, Eq, Binary, Typeable)
5144
5245
5346--------------------------------------------------------------------------------
@@ -63,68 +56,39 @@ instance IsString Template where
6356
6457--------------------------------------------------------------------------------
6558-- | Wrap the constructor to ensure trim is called.
66−template :: FilePath -> [TemplateElement] -> Template
67−template p = flip Template p . trim
59+template :: [TemplateElement] -> Template
60+template = Template . trim
6861
6962
7063--------------------------------------------------------------------------------
71−-- | Parse a string into a template.
72−-- You should prefer 'compileTemplateItem' over this.
7364readTemplate :: String -> Template
74−readTemplate = either error (template origin) . parseTemplateElemsFile origin
75− where
76− origin = "{literal}"
77−{-# DEPRECATED readTemplate "Use templateCompiler instead" #-}
78−
79−--------------------------------------------------------------------------------
80−-- | Parse an item body into a template.
81−-- Provides useful error messages in the 'Compiler' monad.
82−compileTemplateItem :: Item String -> Compiler Template
83−compileTemplateItem item = let file = itemIdentifier item
84− in compileTemplateFile file (itemBody item)
85−
86−--------------------------------------------------------------------------------
87−compileTemplateFile :: Identifier -> String -> Compiler Template
88−compileTemplateFile file = either fail (return . template origin)
89− . parseTemplateElemsFile origin
90− where
91− origin = show file
65+readTemplate = Template . trim . readTemplateElems
9266
9367--------------------------------------------------------------------------------
9468-- | Read a template, without metadata header
9569templateBodyCompiler :: Compiler (Item Template)
9670templateBodyCompiler = cached "Hakyll.Web.Template.templateBodyCompiler" $ do
9771 item <- getResourceBody
98− file <- getUnderlying
99− withItemBody (compileTemplateFile file) item
72+ file <- getResourceFilePath
73+ return $ fmap (template . readTemplateElemsFile file) item
10074
10175--------------------------------------------------------------------------------
10276-- | Read complete file contents as a template
10377templateCompiler :: Compiler (Item Template)
10478templateCompiler = cached "Hakyll.Web.Template.templateCompiler" $ do
10579 item <- getResourceString
106− file <- getUnderlying
107− withItemBody (compileTemplateFile file) item
80+ file <- getResourceFilePath
81+ return $ fmap (template . readTemplateElemsFile file) item
10882
10983
11084--------------------------------------------------------------------------------
111−-- | Interpolate template expressions from context values in a page
11285applyTemplate :: Template -- ^ Template
11386 -> Context a -- ^ Context
11487 -> Item a -- ^ Page
11588 -> Compiler (Item String) -- ^ Resulting item
11689applyTemplate tpl context item = do
117− body <- applyTemplate' (tplElements tpl) context item `catchError` handler
90+ body <- applyTemplate' (unTemplate tpl) context item
11891 return $ itemSetBody body item
119− where
120− tplName = tplOrigin tpl
121− itemName = show $ itemIdentifier item
122− handler es = fail $ "Hakyll.Web.Template.applyTemplate: Failed to " ++
123− (if tplName == itemName
124− then "interpolate template in item " ++ itemName
125− else "apply template " ++ tplName ++ " to item " ++ itemName) ++
126− ":\n" ++ intercalate ",\n" es
127−
12892
12993
13094--------------------------------------------------------------------------------
@@ -141,6 +105,9 @@ applyTemplate' tes context x = go tes
141105
142106 go = fmap concat . mapM applyElem
143107
108+ trimError = error $ "Hakyll.Web.Template.applyTemplate: template not " ++
109+ "fully trimmed."
110+
144111 ---------------------------------------------------------------------------
145112
146113 applyElem :: TemplateElement -> Compiler String
@@ -151,43 +118,29 @@ applyTemplate' tes context x = go tes
151118
152119 applyElem (Chunk c) = return c
153120
154− applyElem (Expr e) = withErrorMessage evalMsg (applyStringExpr typeMsg e)
155− where
156− evalMsg = "In expr '$" ++ show e ++ "$'"
157− typeMsg = "expr '$" ++ show e ++ "$'"
121+ applyElem (Expr e) = applyExpr e >>= getString e
158122
159123 applyElem Escaped = return "$"
160124
161− applyElem (If e t mf) = compilerTry (applyExpr e) >>= handle
125+ applyElem (If e t mf) = (applyExpr e >> go t) `catchError` handler
162126 where
163− f = maybe (return "") go mf
164− handle (Right _) = go t
165− handle (Left (CompilationNoResult _)) = f
166− handle (Left (CompilationFailure es)) = debug (NonEmpty.toList es) >> f
167− debug = compilerDebugEntries ("Hakyll.Web.Template.applyTemplate: " ++
168− "[ERROR] in 'if' condition on expr '" ++ show e ++ "':")
169−
170− applyElem (For e b s) = withErrorMessage headMsg (applyExpr e) >>= \cf -> case cf of
171− EmptyField -> expected "list" "boolean" typeMsg
172− StringField _ -> expected "list" "string" typeMsg
173− ListField c xs -> withErrorMessage bodyMsg $ do
127+ handler _ = case mf of
128+ Nothing -> return ""
129+ Just f -> go f
130+
131+ applyElem (For e b s) = applyExpr e >>= \cf -> case cf of
132+ StringField _ -> fail $
133+ "Hakyll.Web.Template.applyTemplateWith: expected ListField but " ++
134+ "got StringField for expr " ++ show e
135+ ListField c xs -> do
174136 sep <- maybe (return "") go s
175− bs <- forM xs $ applyTemplate' b $ c <> bindItem context x
137+ bs <- mapM (applyTemplate' b c) xs
176138 return $ intercalate sep bs
177− where
178− headMsg = "In expr '$for(" ++ show e ++ ")$'"
179− typeMsg = "loop expr '" ++ show e ++ "'"
180− bodyMsg = "In loop context of '$for(" ++ show e ++ ")$'"
181−
182− applyElem (Partial e) = withErrorMessage headMsg $
183− applyStringExpr typeMsg e >>= \p ->
184− withErrorMessage inclMsg $ do
185− tpl' <- loadBody (fromFilePath p)
186− itemBody <$> applyTemplate tpl' context x
187− where
188− headMsg = "In expr '$partial(" ++ show e ++ ")$'"
189− typeMsg = "partial expr '" ++ show e ++ "'"
190− inclMsg = "In inclusion of '$partial(" ++ show e ++ ")$'"
139+
140+ applyElem (Partial e) = do
141+ p <- applyExpr e >>= getString e
142+ Template tpl' <- loadBody (fromFilePath p)
143+ applyTemplate' tpl' context x
191144
192145 ---------------------------------------------------------------------------
193146
@@ -196,29 +149,17 @@ applyTemplate' tes context x = go tes
196149 applyExpr (Ident (TemplateKey k)) = context' k [] x
197150
198151 applyExpr (Call (TemplateKey k) args) = do
199− args' <- mapM (\e -> applyStringExpr (typeMsg e) e) args
152+ args' <- mapM (\e -> applyExpr e >>= getString e) args
200153 context' k args' x
201− where
202− typeMsg e = "argument '" ++ show e ++ "'"
203154
204155 applyExpr (StringLiteral s) = return (StringField s)
205156
206157 ----------------------------------------------------------------------------
207158
208− applyStringExpr :: String -> TemplateExpr -> Compiler String
209− applyStringExpr msg expr =
210− applyExpr expr >>= getString
211− where
212− getString EmptyField = expected "string" "boolean" msg
213− getString (StringField s) = return s
214− getString (ListField _ _) = expected "string" "list" msg
215−
216− expected typ act expr = fail $ unwords ["Hakyll.Web.Template.applyTemplate:",
217− "expected", typ, "but got", act, "for", expr]
218−
219− -- expected to never happen with all templates constructed by 'template'
220− trimError = fail $
221− "Hakyll.Web.Template.applyTemplate: template not fully trimmed."
159+ getString _ (StringField s) = return s
160+ getString e (ListField _ _) = fail $
161+ "Hakyll.Web.Template.applyTemplateWith: expected StringField but " ++
162+ "got ListField for expr " ++ show e
222163
223164
224165--------------------------------------------------------------------------------
@@ -248,14 +189,14 @@ loadAndApplyTemplate identifier context item = do
248189applyAsTemplate :: Context String -- ^ Context
249190 -> Item String -- ^ Item and template
250191 -> Compiler (Item String) -- ^ Resulting item
251−applyAsTemplate context item = do
252− tpl <- compileTemplateItem item
253− applyTemplate tpl context item
192+applyAsTemplate context item =
193+ let tpl = template $ readTemplateElemsFile file (itemBody item)
194+ file = toFilePath $ itemIdentifier item
195+ in applyTemplate tpl context item
254196
255197
256198--------------------------------------------------------------------------------
257199unsafeReadTemplateFile :: FilePath -> Compiler Template
258200unsafeReadTemplateFile file = do
259201 tpl <- unsafeCompiler $ readFile file
260− compileTemplateFile (fromFilePath file) tpl
261−{-# DEPRECATED unsafeReadTemplateFile "Use templateCompiler" #-}
202+ pure $ template $ readTemplateElemsFile file tpl
lib/Hakyll/Web/Template/Internal/Element.hs+14−7
@@ -7,14 +7,14 @@ module Hakyll.Web.Template.Internal.Element
77 , TemplateExpr (..)
88 , TemplateElement (..)
99 , templateElems
10− , parseTemplateElemsFile
10+ , readTemplateElems
11+ , readTemplateElemsFile
1112 ) where
1213
1314
1415--------------------------------------------------------------------------------
15−import Control.Applicative ((<|>), (<*))
16+import Control.Applicative ((<|>))
1617import Control.Monad (void)
17−import Control.Arrow (left)
1818import Data.Binary (Binary, get, getWord8, put, putWord8)
1919import Data.List (intercalate)
2020import Data.Maybe (isJust)
@@ -107,10 +107,17 @@ instance Binary TemplateExpr where
107107 2 -> StringLiteral <$> get
108108 _ -> error "Hakyll.Web.Template.Internal: Error reading cached template"
109109
110+
111+--------------------------------------------------------------------------------
112+readTemplateElems :: String -> [TemplateElement]
113+readTemplateElems = readTemplateElemsFile "{literal}"
114+
115+
110116--------------------------------------------------------------------------------
111−parseTemplateElemsFile :: FilePath -> String -> Either String [TemplateElement]
112−parseTemplateElemsFile file = left (\e -> "Cannot parse template " ++ show e)
113− . P.parse (templateElems <* P.eof) file
117+readTemplateElemsFile :: FilePath -> String -> [TemplateElement]
118+readTemplateElemsFile file input = case P.parse templateElems file input of
119+ Left err -> error $ "Cannot parse template: " ++ show err
120+ Right t -> t
114121
115122
116123--------------------------------------------------------------------------------
@@ -160,7 +167,7 @@ trimOpen = do
160167--------------------------------------------------------------------------------
161168trimClose :: P.Parser Bool
162169trimClose = do
163− trimIfR <- P.optionMaybe $ (P.char '-')
170+ trimIfR <- P.optionMaybe $ P.try (P.char '-')
164171 void $ P.char '$'
165172 pure $ isJust trimIfR
166173
stack.yaml+7−9
@@ -1,4 +1,5 @@
1−resolver: lts-13.29
1+resolver: lts-11.7
2+extra-package-dbs: []
23save-hackage-creds: false
34
45flags:
@@ -23,11 +24,8 @@ build:
2324 haddock-deps: false
2425
2526extra-deps:
26−- 'cmark-gfm-0.2.0'
27−- 'hslua-module-system-0.2.1'
28−- 'ipynb-0.1'
29−- 'lrucache-1.2.0.1'
30−- 'pandoc-2.7.3'
31−- 'pandoc-citeproc-0.16.2'
32−- 'skylighting-0.8.2'
33−- 'skylighting-core-0.8.2'
27+- 'pandoc-2.2'
28+- 'pandoc-types-1.17.4.2'
29+- 'pandoc-citeproc-0.14.3.1'
30+- 'tasty-1.1.0.1'
31+- 'fsnotify-0.3.0.0'
tests/Hakyll/Core/UnixFilter/Tests.hs+14−6
@@ -6,16 +6,18 @@ module Hakyll.Core.UnixFilter.Tests
66
77
88--------------------------------------------------------------------------------
9−import Test.Tasty (TestTree, testGroup)
10−import Test.Tasty.HUnit (testCase)
11−import qualified Test.Tasty.HUnit as H
9+import Data.List (isInfixOf)
10+import Test.Tasty (TestTree, testGroup)
11+import Test.Tasty.HUnit (testCase)
12+import qualified Test.Tasty.HUnit as H
1213
1314
1415--------------------------------------------------------------------------------
1516import Hakyll.Core.Compiler
16−import Hakyll.Core.Identifier
17+import Hakyll.Core.Compiler.Internal
1718import Hakyll.Core.Item
1819import Hakyll.Core.UnixFilter
20+import Hakyll.Core.Identifier
1921import TestSuite.Util
2022
2123
@@ -49,7 +51,10 @@ unixFilterFalse :: H.Assertion
4951unixFilterFalse = do
5052 store <- newTestStore
5153 provider <- newTestProvider store
52− testCompilerError store provider testMarkdown compiler "exit code"
54+ result <- testCompiler store provider testMarkdown compiler
55+ case result of
56+ CompilerError es -> True H.@=? any ("exit code" `isInfixOf`) es
57+ _ -> H.assertFailure "Expecting CompilerError"
5358 cleanTestEnv
5459 where
5560 compiler = getResourceString >>= withItemBody (unixFilter "false" [])
@@ -60,7 +65,10 @@ unixFilterError :: H.Assertion
6065unixFilterError = do
6166 store <- newTestStore
6267 provider <- newTestProvider store
63− testCompilerError store provider testMarkdown compiler "option"
68+ result <- testCompiler store provider testMarkdown compiler
69+ case result of
70+ CompilerError es -> True H.@=? any ("invalid option" `isInfixOf`) es
71+ _ -> H.assertFailure "Expecting CompilerError"
6472 cleanTestEnv
6573 where
6674 compiler = getResourceString >>= withItemBody (unixFilter "head" ["-#"])
tests/Hakyll/Web/Template/Context/Tests.hs+3−40
@@ -13,19 +13,16 @@ import Test.Tasty.HUnit (Assertion, testCase, (@=?))
1313--------------------------------------------------------------------------------
1414import Hakyll.Core.Compiler
1515import Hakyll.Core.Identifier
16−import Hakyll.Core.Item
1716import Hakyll.Core.Provider
1817import Hakyll.Core.Store (Store)
1918import Hakyll.Web.Template.Context
20−import Hakyll.Web.Template.Internal
2119import TestSuite.Util
2220
2321
2422--------------------------------------------------------------------------------
2523tests :: TestTree
26−tests = testGroup "Hakyll.Web.Template.Context.Tests"
24+tests = testGroup "Hakyll.Core.Template.Context.Tests"
2725 [ testCase "testDateField" testDateField
28− , testCase "testOuerLoopContextAccess" testOuerLoopContextAccess
2926 ]
3027
3128
@@ -44,15 +41,6 @@ testDateField = do
4441 dateField "date" "%B %e, %Y"
4542 date2 @=? "August 26, 2010"
4643
47− date3 <- testContextDone store provider
48− "posts/2018-09-26.md" "date" $
49− dateField "date" "%B %e, %Y"
50− date3 @=? "September 26, 2018"
51−
52− date4 <- testContextDone store provider
53− "posts/2019/05/10/tomorrow.md" "date" $
54− dateField "date" "%B %e, %Y"
55− date4 @=? "May 10, 2019"
5644 cleanTestEnv
5745
5846
@@ -65,31 +53,6 @@ testContextDone store provider identifier key context =
6553 cf <- unContext context key [] item
6654 case cf of
6755 StringField str -> return str
68− _ -> error $
56+ ListField _ _ -> error $
6957 "Hakyll.Web.Template.Context.Tests.testContextDone: " ++
70− "expected StringField"
71−
72−--------------------------------------------------------------------------------
73−
74−testOuerLoopContextAccess :: Assertion
75−testOuerLoopContextAccess = do
76− store <- newTestStore
77− provider <- newTestProvider store
78− test store provider ctx "baz"
79− test store provider (ctx' <> ctx) "not baz"
80− test store provider (ctx <> ctx') "baz"
81−
82− cleanTestEnv
83− where
84− tpl = readTemplate "$for(foo)$$for(bar)$$qux$$endfor$$endfor$"
85− ctx = mconcat [
86− field "qux" $ const $ return "baz"
87− , listField "foo" (listField "bar" mempty $ return [mockItem])
88− $ return [mockItem]
89− ]
90− ctx' = field "qux" $ const $ return "not baz"
91− mockItem = Item "" ()
92− test store provider context str = do
93− str' <- testCompilerDone store provider ""
94− $ applyTemplate tpl context mockItem
95− str @=? itemBody str'
58+ "Didn't expect ListField"
tests/Hakyll/Web/Template/Tests.hs+17−49
@@ -1,6 +1,5 @@
11--------------------------------------------------------------------------------
22{-# LANGUAGE OverloadedStrings #-}
3−{-# LANGUAGE TemplateHaskell #-}
43module Hakyll.Web.Template.Tests
54 ( tests
65 ) where
@@ -8,10 +7,9 @@ module Hakyll.Web.Template.Tests
87
98--------------------------------------------------------------------------------
109import Test.Tasty (TestTree, testGroup)
11−import Test.Tasty.HUnit (Assertion, assertBool, testCase,
12− (@=?), (@?=))
10+import Test.Tasty.HUnit (Assertion, testCase, (@=?),
11+ (@?=))
1312
14−import Data.Either (isLeft)
1513
1614--------------------------------------------------------------------------------
1715import Hakyll.Core.Compiler
@@ -27,20 +25,20 @@ import TestSuite.Util
2725
2826--------------------------------------------------------------------------------
2927tests :: TestTree
30−tests = testGroup "Hakyll.Web.Template.Tests" $ concat
28+tests = testGroup "Hakyll.Core.Template.Tests" $ concat
3129 [ [ testCase "case01" $ test ("template.html.out", "template.html", "example.md")
3230 , testCase "case02" $ test ("strip.html.out", "strip.html", "example.md")
3331 , testCase "case03" $ test ("just-meta.html.out", "just-meta.html", "example.md")
3432 , testCase "applyJoinTemplateList" testApplyJoinTemplateList
3533 ]
3634
37− , fromAssertions "parseTemplate"
38− [ Right [Chunk "Hello ", Expr (Call "guest" [])]
39− @=? parse "Hello $guest()$"
40− , Right [If (Call "a" [StringLiteral "bar"]) [Chunk "foo"] Nothing]
41− @=? parse "$if(a(\"bar\"))$foo$endif$"
35+ , fromAssertions "readTemplate"
36+ [ [Chunk "Hello ", Expr (Call "guest" [])]
37+ @=? readTemplateElems "Hello $guest()$"
38+ , [If (Call "a" [StringLiteral "bar"]) [Chunk "foo"] Nothing]
39+ @=? readTemplateElems "$if(a(\"bar\"))$foo$endif$"
4240 -- 'If' trim check.
43− , Right [ TrimL
41+ , [ TrimL
4442 , If (Ident (TemplateKey "body"))
4543 [ TrimR
4644 , Chunk "\n"
@@ -56,39 +54,29 @@ tests = testGroup "Hakyll.Web.Template.Tests" $ concat
5654 ])
5755 , TrimR
5856 ]
59− @=? parse "$-if(body)-$\n$body$\n$-else-$\n$body$\n$-endif-$"
57+ @=? readTemplateElems "$-if(body)-$\n$body$\n$-else-$\n$body$\n$-endif-$"
6058 -- 'For' trim check.
61− , Right [ TrimL
59+ , [ TrimL
6260 , For (Ident (TemplateKey "authors"))
6361 [TrimR, Chunk "\n body \n", TrimL]
6462 Nothing
6563 , TrimR
6664 ]
67− @=? parse "$-for(authors)-$\n body \n$-endfor-$"
65+ @=? readTemplateElems "$-for(authors)-$\n body \n$-endfor-$"
6866 -- 'Partial' trim check.
69− , Right [ TrimL
67+ , [ TrimL
7068 , Partial (StringLiteral "path")
7169 , TrimR
7270 ]
73− @=? parse "$-partial(\"path\")-$"
71+ @=? readTemplateElems "$-partial(\"path\")-$"
7472 -- 'Expr' trim check.
75− , Right [ TrimL
73+ , [ TrimL
7674 , Expr (Ident (TemplateKey "foo"))
7775 , TrimR
7876 ]
79− @=? parse "$-foo-$"
80− -- fail on incomplete template.
81− , assertBool "did not yield error" $ isLeft $
82− parse "a$b"
83− -- fail on mismatched template syntax.
84− , assertBool "did not fail to parse" $ isLeft $
85− parse "$for(xs)$\n <p>foo</p>\n$endif$"
77+ @=? readTemplateElems "$-foo-$"
8678 ]
87−
88− , [testCase "embeddedTemplate" testEmbeddedTemplate]
8979 ]
90− where
91− parse = parseTemplateElemsFile ""
9280
9381
9482--------------------------------------------------------------------------------
@@ -125,8 +113,6 @@ testApplyJoinTemplateList :: Assertion
125113testApplyJoinTemplateList = do
126114 store <- newTestStore
127115 provider <- newTestProvider store
128− tpl <- testCompilerDone store provider "tpl" $
129− compileTemplateItem (Item "tpl" "<b>$body$</b>")
130116 str <- testCompilerDone store provider "item3" $
131117 applyJoinTemplateList ", " tpl defaultContext [i1, i2]
132118
@@ -135,22 +121,4 @@ testApplyJoinTemplateList = do
135121 where
136122 i1 = Item "item1" "Hello"
137123 i2 = Item "item2" "World"
138−
139−
140−--------------------------------------------------------------------------------
141−embeddedTemplate :: Template
142−embeddedTemplate = $(embedTemplate "tests/data/embed.html")
143−
144−--------------------------------------------------------------------------------
145−testEmbeddedTemplate :: Assertion
146−testEmbeddedTemplate = do
147− store <- newTestStore
148− provider <- newTestProvider store
149− str <- testCompilerDone store provider "item3" $
150− applyTemplate embeddedTemplate defaultContext item
151−
152− itemBody str @?= "<p>Hello, world</p>\n"
153− cleanTestEnv
154− where
155− item = Item "item1" "Hello, world"
156−
124+ tpl = readTemplate "<b>$body$</b>"
tests/TestSuite.hs−2
@@ -28,7 +28,6 @@ import qualified Hakyll.Web.Pandoc.FileType.Tests
2828#endif
2929import qualified Hakyll.Web.Template.Context.Tests
3030import qualified Hakyll.Web.Template.Tests
31−import qualified Hakyll.Web.Tags.Tests
3231
3332
3433--------------------------------------------------------------------------------
@@ -50,7 +49,6 @@ main = defaultMain $ testGroup "Hakyll"
5049#ifdef USE_PANDOC
5150 , Hakyll.Web.Pandoc.FileType.Tests.tests
5251#endif
53− , Hakyll.Web.Tags.Tests.tests
5452 , Hakyll.Web.Template.Context.Tests.tests
5553 , Hakyll.Web.Template.Tests.tests
5654 ]
tests/TestSuite/Util.hs+2−11
@@ -6,7 +6,6 @@ module TestSuite.Util
66 , newTestProvider
77 , testCompiler
88 , testCompilerDone
9− , testCompilerError
109 , testConfiguration
1110 , cleanTestEnv
1211 , renderParagraphs
@@ -14,7 +13,7 @@ module TestSuite.Util
1413
1514
1615--------------------------------------------------------------------------------
17−import Data.List (intercalate, isInfixOf)
16+import Data.List (intercalate)
1817import Data.Monoid (mempty)
1918import qualified Data.Set as S
2019import Test.Tasty
@@ -81,21 +80,13 @@ testCompilerDone store provider underlying compiler = do
8180 CompilerDone x _ -> return x
8281 CompilerError e -> fail $
8382 "TestSuite.Util.testCompilerDone: compiler " ++ show underlying ++
84− " threw: " ++ intercalate "; " (compilerErrorMessages e)
83+ " threw: " ++ intercalate "; " e
8584 CompilerRequire i _ -> fail $
8685 "TestSuite.Util.testCompilerDone: compiler " ++ show underlying ++
8786 " requires: " ++ show i
8887 CompilerSnapshot _ _ -> fail
8988 "TestSuite.Util.testCompilerDone: unexpected CompilerSnapshot"
9089
91−testCompilerError :: Store -> Provider -> Identifier -> Compiler a -> String -> IO ()
92−testCompilerError store provider underlying compiler expectedMessage = do
93− result <- testCompiler store provider underlying compiler
94− case result of
95− CompilerError e ->
96− any (expectedMessage `isInfixOf`) (compilerErrorMessages e) @?
97− "Expecting '" ++ expectedMessage ++ "' error"
98− _ -> assertFailure "Expecting CompilerError"
9990
10091--------------------------------------------------------------------------------
10192testConfiguration :: Configuration
tests/data/embed.htmldeleted−1
@@ -1 +0,0 @@
1−<p>$body$</p>
tests/data/posts/2018-09-26.mddeleted−1
@@ -1 +0,0 @@
1−Something happened today.
tests/data/posts/2019/05/10/tomorrow.mddeleted−1
@@ -1 +0,0 @@
1−This day hasn't happened yet (as of writing this).
web/css/syntax.css+18−61
@@ -1,61 +1,18 @@
1−a.sourceLine { display: inline-block; line-height: 1.25; }
2−a.sourceLine { pointer-events: none; color: inherit; text-decoration: inherit; }
3−a.sourceLine:empty { height: 1.2em; }
4−.sourceCode { overflow: visible; }
5−code.sourceCode { white-space: pre; position: relative; }
6−div.sourceCode { margin: 1em 0; }
7−pre.sourceCode { margin: 0; }
8−@media screen {
9−div.sourceCode { overflow: auto; }
10−}
11−@media print {
12−code.sourceCode { white-space: pre-wrap; }
13−a.sourceLine { text-indent: -1em; padding-left: 1em; }
14−}
15−pre.numberSource a.sourceLine
16− { position: relative; left: -4em; }
17−pre.numberSource a.sourceLine::before
18− { content: attr(title);
19− position: relative; left: -1em; text-align: right; vertical-align: baseline;
20− border: none; pointer-events: all; display: inline-block;
21− -webkit-touch-callout: none; -webkit-user-select: none;
22− -khtml-user-select: none; -moz-user-select: none;
23− -ms-user-select: none; user-select: none;
24− padding: 0 4px; width: 4em;
25− color: #aaaaaa;
26− }
27−pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
28−div.sourceCode
29− { }
30−@media screen {
31−a.sourceLine::before { text-decoration: underline; }
32−}
33−code span.al { color: #ff0000; font-weight: bold; } /* Alert */
34−code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
35−code span.at { color: #7d9029; } /* Attribute */
36−code span.bn { color: #40a070; } /* BaseN */
37−code span.bu { } /* BuiltIn */
38−code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
39−code span.ch { color: #4070a0; } /* Char */
40−code span.cn { color: #880000; } /* Constant */
41−code span.co { color: #60a0b0; font-style: italic; } /* Comment */
42−code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
43−code span.do { color: #ba2121; font-style: italic; } /* Documentation */
44−code span.dt { color: #902000; } /* DataType */
45−code span.dv { color: #40a070; } /* DecVal */
46−code span.er { color: #ff0000; font-weight: bold; } /* Error */
47−code span.ex { } /* Extension */
48−code span.fl { color: #40a070; } /* Float */
49−code span.fu { color: #06287e; } /* Function */
50−code span.im { } /* Import */
51−code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
52−code span.kw { color: #007020; font-weight: bold; } /* Keyword */
53−code span.op { color: #666666; } /* Operator */
54−code span.ot { color: #007020; } /* Other */
55−code span.pp { color: #bc7a00; } /* Preprocessor */
56−code span.sc { color: #4070a0; } /* SpecialChar */
57−code span.ss { color: #bb6688; } /* SpecialString */
58−code span.st { color: #4070a0; } /* String */
59−code span.va { color: #19177c; } /* Variable */
60−code span.vs { color: #4070a0; } /* VerbatimString */
61−code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
1+/* Generated by pandoc. */
2+table.sourceCode, tr.sourceCode, td.lineNumbers, td.sourceCode, table.sourceCode pre
3+ { margin: 0; padding: 0; border: 0; vertical-align: baseline; border: none; }
4+td.lineNumbers { border-right: 1px solid #AAAAAA; text-align: right; color: #AAAAAA; padding-right: 5px; padding-left: 5px; }
5+td.sourceCode { padding-left: 5px; }
6+.sourceCode span.kw { color: #007020; font-weight: bold; }
7+.sourceCode span.dt { color: #902000; }
8+.sourceCode span.dv { color: #40a070; }
9+.sourceCode span.bn { color: #40a070; }
10+.sourceCode span.fl { color: #40a070; }
11+.sourceCode span.ch { color: #4070a0; }
12+.sourceCode span.st { color: #4070a0; }
13+.sourceCode span.co { color: #60a0b0; font-style: italic; }
14+.sourceCode span.ot { color: #007020; }
15+.sourceCode span.al { color: red; font-weight: bold; }
16+.sourceCode span.fu { color: #06287e; }
17+.sourceCode span.re { }
18+.sourceCode span.er { color: red; font-weight: bold; }
web/examples.markdown+6−20
@@ -45,7 +45,7 @@ directly with the default Hakyll site.
4545- <http://www.regisfoucault.com/>,
4646 [source](https://github.com/regisfoucault/blog)
4747- <http://www.chaoxuprime.com/>,
48− [source](https://github.com/chaoxu/chaoxu.github.io/tree/develop)
48+ [source](https://github.com/Mgccl/blog)
4949- <http://www.skybluetrades.net/>,
5050 [source](https://github.com/ian-ross/blog)
5151- <http://chromaticleaves.com/>,
@@ -98,8 +98,8 @@ directly with the default Hakyll site.
9898 [source](https://github.com/Libbum/AxiomaticSemantics)
9999- [https://tesser.org/](http://web.archive.org/web/20161022221713/https://tesser.org/),
100100 [source](https://github.com/fractalcat/tesser.org)
101−- <https://weien.io/>,
102−  [source](https://github.com/wei2912/blog-src)
101+- <https://wei2912.github.io/>,
102+ [source](https://github.com/wei2912/blog_src)
103103- <http://www.metasepi.org/>,
104104 [source](https://github.com/metasepi/metasepi-website)
105105- <http://gisli.hamstur.is/>,
@@ -166,24 +166,10 @@ directly with the default Hakyll site.
166166 [source](https://github.com/bendo/bendo.github.io)
167167- <http://adarsh.cc/>,
168168 [source](https://github.com/adarshp/adarsh.cc)
169−- <http://0a.io>,
170− [source](https://github.com/arrchyy/0a.io)
169+- <http://zer0.degree/>,
170+ [source](https://github.com/arrchhy/zer0.degree)
171171- <https://nasy.moe/>,
172− [source](https://github.com/nasyxx/NasyMoe)
173−- <http://uniprocess.org/>,
174− [source](https://gitlab.com/uniprocess/uniprocess.org)
175−- <https://regularflolloping.com/>,
176− [source](https://github.com/techieAgnostic/rf)
177−- <http://enter-haken.github.io/>,
178− [source](https://github.com/enter-haken/blog)
179−- <https://modular.blind.guru/>,
180− [source](https://github.com/mlang/eurorack-metadata)
181−- <http://www.jonashietala.se/>,
182− [source](https://github.com/treeman/jonashietala)
183−- <https://abhinavsarkar.net/>,
184− [source](https://github.com/abhin4v/abhin4v.github.io)
185−- <https://dailyreadingnotes.com/>,
186− [source](https://github.com/hackeryarn/daily-reading-notes)
172+ [source](https://github.com/nasyxx/nasyxx.github.io)
187173
188174## Hakyll 3.X
189175
web/libraries.markdown−1
@@ -18,4 +18,3 @@ Here is a list of the ones I know of (I would be happy to hear about others).
1818 a conversion tool from other blog engines to Hakyll.
1919- [hakyll-elm](https://hackage.haskell.org/package/hakyll-elm),
2020 Hakyll wrapper for the Elm compiler.
21−- [hakyll-sass](http://hackage.haskell.org/package/hakyll-sass), a compiler for SASS templates.
web/site.hs+32−14
@@ -1,7 +1,8 @@
11--------------------------------------------------------------------------------
22{-# LANGUAGE OverloadedStrings #-}
33import Control.Arrow (second)
4−import Control.Monad (filterM, forM_)
4+import Control.Monad (forM_)
5+import Data.Char (isDigit)
56import Data.List (isPrefixOf, sortBy)
67import Data.Monoid ((<>))
78import Data.Ord (comparing)
@@ -56,11 +57,17 @@ main = hakyllWith config $ do
5657 create ["tutorials.html"] $ do
5758 route idRoute
5859 compile $ do
59− ctx <- tutorialsCtx <$>
60+ tuts <-
6061 sortBy (comparing itemIdentifier) <$> loadAll "tutorials/*"
62+
63+ let tutorialsCtx =
64+ constField "title" "Tutorials" `mappend`
65+ listField "tutorials" tutorialCtx (return tuts) `mappend`
66+ defaultContext
67+
6168 makeItem ""
62− >>= loadAndApplyTemplate "templates/tutorials.html" ctx
63− >>= loadAndApplyTemplate "templates/default.html" ctx
69+ >>= loadAndApplyTemplate "templates/tutorials.html" tutorialsCtx
70+ >>= loadAndApplyTemplate "templates/default.html" tutorialsCtx
6471 >>= relativizeUrls
6572
6673 -- Templates
@@ -101,17 +108,28 @@ hackage url
101108 reverse $ splitPath url
102109
103110
111+--------------------------------------------------------------------------------
112+data TutorialType = SeriesTutorial | ArticlesTutorial | ExternalTutorial
113+ deriving (Eq)
114+
115+
104116--------------------------------------------------------------------------------
105117-- | Partition tutorials into tutorial series, other articles, external articles
106−tutorialsCtx :: [Item String] -> Context String
107−tutorialsCtx tuts =
108− constField "title" "Tutorials" <>
109− listField "main" defaultContext (ofType "main") <>
110− listField "articles" defaultContext (ofType "article") <>
111− listField "externals" defaultContext (ofType "external") <>
112− listField "robertwpearce" defaultContext (ofType "robertwpearce") <>
118+tutorialCtx :: Context String
119+tutorialCtx =
120+ field "isSeries" (isTutorialType SeriesTutorial) <>
121+ field "isArticle" (isTutorialType ArticlesTutorial) <>
122+ field "isExternal" (isTutorialType ExternalTutorial) <>
113123 defaultContext
114124 where
115− ofType ty = filterM (\item -> do
116− mbType <- getMetadataField (itemIdentifier item) "type"
117− return $ Just ty == mbType) tuts
125+ getTutorialType item = do
126+ mbExternal <- getMetadataField (itemIdentifier item) "external"
127+ return $ case mbExternal of
128+ Just _ -> ExternalTutorial
129+ _ -> case splitPath (toFilePath $ itemIdentifier item) of
130+ [_, (x : _)] -> if isDigit x then SeriesTutorial else ArticlesTutorial
131+ _ -> ArticlesTutorial
132+
133+ isTutorialType ty0 item = do
134+ ty1 <- getTutorialType item
135+ if ty0 == ty1 then return "yes" else fail "no"
web/templates/tutorials.html+18−24
@@ -1,19 +1,23 @@
11<h1>Tutorials about Hakyll</h1>
22<h2 id="series">Tutorial series</h2>
33<ul>
4− $for(main)$
5− <li>
6− <a href="$url$">$title$</a>
7− </li>
4+ $for(tutorials)$
5+ $if(isSeries)$
6+ <li>
7+ <a href="$url$">$title$</a>
8+ </li>
9+ $endif$
810 $endfor$
911</ul>
1012<h2 id="articles">Other articles</h2>
1113<p>In no particular order:</p>
1214<ul>
13− $for(articles)$
14− <li>
15− <a href="$url$">$title$</a> by <em>$author$</em>
16− </li>
15+ $for(tutorials)$
16+ $if(isArticle)$
17+ <li>
18+ <a href="$url$">$title$</a> by <em>$author$</em>
19+ </li>
20+ $endif$
1721 $endfor$
1822</ul>
1923<h2 id="external-articles">External articles</h2>
@@ -24,22 +28,12 @@
2428</p>
2529<p>In no particular order:</p>
2630<ul>
27− $for(externals)$
28− <li>
29− <a href="$url$">$title$</a> by <em>$author$</em>
30− </li>
31− $endfor$
32−</ul>
33−<h2 id="robertwpearce">Robert Pearce's series</h2>
34−<p>
35− <a href="https://robertwpearce.com/">Robert Pearce</a> has been writing an
36− excellent tutorial-style series on Hakyll as well. You can find those here:
37−</p>
38−<ul>
39− $for(robertwpearce)$
40− <li>
41− <a href="$url$">$title$</a>
42− </li>
31+ $for(tutorials)$
32+ $if(isExternal)$
33+ <li>
34+ <a href="$url$">$title$</a> by <em>$author$</em>
35+ </li>
36+ $endif$
4337 $endfor$
4438</ul>
4539<p>
web/tutorials/01-installation.markdown+2−4
@@ -1,7 +1,6 @@
11---
22title: Installation
33author: Jasper Van der Jeugt
4−type: main
54---
65
76Installation
@@ -18,8 +17,7 @@ different distributions. For installation from source (i.e. via Hackage),
1817Linux distro packages:
1918
2019- [Debian unstable](http://packages.debian.org/source/sid/haskell-hakyll)
21−- [Fedora](https://apps.fedoraproject.org/packages/ghc-hakyll)
22−- [Nix](https://nixos.org/nixos/packages.html#hakyll): `$ nix-env -i hakyll`
20+- [Fedora](https://apps.fedoraproject.org/packages/ghc-hakyll-devel)
2321
2422Building the example site
2523-------------------------
@@ -28,7 +26,7 @@ Apart from the main Hakyll library, the cabal package also provides you with an
2826executable `hakyll-init` to create an example site. This is an easy way to get
2927started:
3028
31− $ stack exec hakyll-init my-site
29+ $ hakyll-init my-site
3230
3331This creates a folder `my-site` in the current directory, with some example
3432content and a generic configuration.
web/tutorials/02-basics.markdown−1
@@ -1,7 +1,6 @@
11---
22title: The basics
33author: Jasper Van der Jeugt
4−type: main
54---
65
76Building and cleaning
web/tutorials/03-rules-routes-compilers.markdown−1
@@ -1,7 +1,6 @@
11---
22title: Rules, routes and compilers
33author: Jasper Van der Jeugt
4−type: main
54---
65
76Basic rules
web/tutorials/04-compilers.markdown−1
@@ -1,7 +1,6 @@
11---
22title: 'More on compilers: load, and templates'
33author: Jasper Van der Jeugt
4−type: main
54---
65
76Loading items
web/tutorials/05-snapshots-feeds.markdown−1
@@ -1,7 +1,6 @@
11---
22title: Snapshots, and how to produce an RSS/Atom feed
33author: Jasper Van der Jeugt
4−type: main
54---
65
76Basic feed configuration
web/tutorials/06-versions.markdown−1
@@ -1,7 +1,6 @@
11---
22title: Producing multiple versions of a single file
33author: Jasper Van der Jeugt
4−type: main
54---
65
76Basics
web/tutorials/a-guide-to-the-hakyll-module-zoo.markdown−1
@@ -1,7 +1,6 @@
11---
22title: A Guide to the Hakyll Module Zoo
33author: Brent Yorgey
4−type: article
54---
65
76The hakyll package [contains a bewildering array](/reference/) of modules, and
web/tutorials/external-add-tags-to-your-hakyll-blog.md+1−1
@@ -2,5 +2,5 @@
22title: Add tags to your Hakyll blog
33author: Javran Cheng
44url: https://javran.github.io/posts/2014-03-01-add-tags-to-your-hakyll-blog.html
5−type: external
5+external: true
66---
web/tutorials/external-blogger-port.md+1−1
@@ -2,5 +2,5 @@
22title: Port an existing Blogger blog to Hakyll
33author: Daniel Campoverde
44url: 'http://www.sillybytes.net/posts/from_blogger_to_hakyll.html'
5−type: external
5+external: true
66---
web/tutorials/external-cassius-template.md+1−1
@@ -2,5 +2,5 @@
22title: Using Cassius templating with Hakyll
33author: Daniel Campoverde
44url: 'http://www.sillybytes.net/posts/using_hakyll_with_cassius.html'
5−type: external
5+external: true
66---
web/tutorials/external-clean-urls-with-hakyll.md+1−1
@@ -2,5 +2,5 @@
22title: Clean URLs with Hakyll
33author: Rohan Jain
44url: http://www.rohanjain.in/hakyll-clean-urls/
5−type: external
5+external: true
66---
web/tutorials/external-deploying-with-widely.md+1−1
@@ -2,5 +2,5 @@
22title: Deploying with Widely
33author: Kyle Marek-Spartz
44url: http://kyle.marek-spartz.org/posts/2013-12-09-widely-and-hakyll.html
5−type: external
5+external: true
66---
web/tutorials/external-external-code-inclusion.mddeleted−6
@@ -1,6 +0,0 @@
1−---
2−title: 'External code inclusion with Hakyll'
3−author: 'Tobias Pleyer'
4−url: 'http://blog.tpleyer.de/posts/2019-04-21-external-code-inclusion-with-hakyll.html'
5−type: external
6−---
web/tutorials/external-functionfield.md+1−1
@@ -2,5 +2,5 @@
22title: Hakyll's functionField
33author: Beerend Lauwers
44url: http://beerendlauwers.be/posts/2015-09-21-hakylls-functionfield.html
5−type: external
5+external: true
66---
web/tutorials/external-github-pages-circle-ci.md+1−1
@@ -2,5 +2,5 @@
22title: Create a GitHub page with Hakyll and CircleCI
33author: Juan Pedro Villa
44url: https://www.stackbuilders.com/news/dr-hakyll-create-a-github-page-with-hakyll-and-circleci
5−type: external
5+external: true
66---
web/tutorials/external-inlining-and-compressing-css.md+1−1
@@ -2,5 +2,5 @@
22title: Inlining and compressing CSS
33author: Kyle Marek-Spartz
44url: http://kyle.marek-spartz.org/posts/2014-12-09-hakyll-css-template-compiler.html
5−type: external
5+external: true
66---
web/tutorials/faq.markdown−1
@@ -1,7 +1,6 @@
11---
22title: FAQ
33author: Jasper Van der Jeugt
4−type: article
54---
65
76## "hGetContents: invalid argument" or "commitBuffer: invalid argument"
web/tutorials/github-pages-tutorial.md−1
@@ -1,7 +1,6 @@
11---
22title: Using Hakyll with GitHub Pages
33author: Erik Stevenson
4−type: article
54---
65
76## Introduction
web/tutorials/hakyll-3-to-hakyll4-migration-guide.markdown−1
@@ -1,7 +1,6 @@
11---
22title: Hakyll 3 to Hakyll 4 migration guide
33author: Jasper Van der Jeugt
4−type: article
54---
65
76Introduction
web/tutorials/robertwpearce-01-setup-and-initial-customization.htmldeleted−5
@@ -1,5 +0,0 @@
1−---
2−title: 'Pt. 1: Setup & Initial Customization'
3−url: 'https://robertwpearce.com/hakyll-pt-1-setup-and-initial-customization.html'
4−type: robertwpearce
5−---
web/tutorials/robertwpearce-02-generating-a-sitemap-xml-file.htmldeleted−5
@@ -1,5 +0,0 @@
1−---
2−title: 'Pt. 2: Generating a Sitemap XML File'
3−url: 'https://robertwpearce.com/hakyll-pt-2-generating-a-sitemap-xml-file.html'
4−type: robertwpearce
5−---
web/tutorials/robertwpearce-03-generating-rss-and-atom-xml-feeds.htmldeleted−5
@@ -1,5 +0,0 @@
1−---
2−title: 'Pt. 3: Generating RSS and Atom XML Feeds'
3−url: 'https://robertwpearce.com/hakyll-pt-3-generating-rss-and-atom-xml-feeds.html'
4−type: robertwpearce
5−---
web/tutorials/robertwpearce-04-copying-static-files-for-your-build.htmldeleted−5
@@ -1,5 +0,0 @@
1−---
2−title: 'Pt. 4: Copying Static Files For Your Build'
3−url: 'https://robertwpearce.com/hakyll-pt-4-copying-static-files-for-your-build.html'
4−type: robertwpearce
5−---
web/tutorials/robertwpearce-05-generating-custom-post-filenames-from-a-title-slug.htmldeleted−5
@@ -1,5 +0,0 @@
1−---
2−title: 'Pt. 5 – Generating Custom Post Filenames From a Title Slug'
3−url: 'https://robertwpearce.com/hakyll-pt-5-generating-custom-post-filenames-from-a-title-slug.html'
4−type: robertwpearce
5−---
web/tutorials/using-clay-with-hakyll.markdown−1
@@ -1,7 +1,6 @@
11---
22title: Using Clay with Hakyll
33author: Jasper Van der Jeugt
4−type: article
54---
65
76[Clay](http://sebastiaanvisser.github.com/clay/) is a nice CSS preprocesser
web/tutorials/using-teasers-in-hakyll.markdown−1
@@ -1,7 +1,6 @@
11---
22title: Using teasers in Hakyll
33author: Ivan Veselov
4−type: article
54---
65
76## Introduction