summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore3
-rw-r--r--.travis.yml1
-rw-r--r--README.markdown6
-rw-r--r--data/example/posts/2015-08-12-spqr.markdown (renamed from data/example/posts/2012-08-12-spqr.markdown)0
-rw-r--r--data/example/posts/2015-10-07-rosa-rosa-rosam.markdown (renamed from data/example/posts/2012-10-07-rosa-rosa-rosam.markdown)0
-rw-r--r--data/example/posts/2015-11-28-carpe-diem.markdown (renamed from data/example/posts/2012-11-28-carpe-diem.markdown)0
-rw-r--r--data/example/posts/2015-12-07-tu-quoque.markdown (renamed from data/example/posts/2012-12-07-tu-quoque.markdown)0
-rw-r--r--hakyll.cabal132
-rw-r--r--src/Data/List/Extended.hs15
-rw-r--r--src/Data/Yaml/Extended.hs20
-rw-r--r--src/Hakyll/Check.hs61
-rw-r--r--src/Hakyll/Commands.hs3
-rw-r--r--src/Hakyll/Core/Compiler.hs1
-rw-r--r--src/Hakyll/Core/Compiler/Internal.hs6
-rw-r--r--src/Hakyll/Core/Compiler/Require.hs1
-rw-r--r--src/Hakyll/Core/Dependencies.hs1
-rw-r--r--src/Hakyll/Core/File.hs1
-rw-r--r--src/Hakyll/Core/Identifier.hs1
-rw-r--r--src/Hakyll/Core/Identifier/Pattern.hs2
-rw-r--r--src/Hakyll/Core/Item.hs2
-rw-r--r--src/Hakyll/Core/Logger.hs1
-rw-r--r--src/Hakyll/Core/Metadata.hs88
-rw-r--r--src/Hakyll/Core/Provider/Internal.hs2
-rw-r--r--src/Hakyll/Core/Provider/Metadata.hs134
-rw-r--r--src/Hakyll/Core/Provider/MetadataCache.hs9
-rw-r--r--src/Hakyll/Core/Routes.hs1
-rw-r--r--src/Hakyll/Core/Rules.hs2
-rw-r--r--src/Hakyll/Core/Rules/Internal.hs2
-rw-r--r--src/Hakyll/Core/Runtime.hs8
-rw-r--r--src/Hakyll/Core/Store.hs1
-rw-r--r--src/Hakyll/Core/UnixFilter.hs3
-rw-r--r--src/Hakyll/Core/Util/File.hs1
-rw-r--r--src/Hakyll/Core/Util/Parser.hs2
-rw-r--r--src/Hakyll/Web/CompressCss.hs1
-rw-r--r--src/Hakyll/Web/Feed.hs1
-rw-r--r--src/Hakyll/Web/Paginate.hs1
-rw-r--r--src/Hakyll/Web/Pandoc.hs2
-rw-r--r--src/Hakyll/Web/Pandoc/Biblio.hs17
-rw-r--r--src/Hakyll/Web/Tags.hs12
-rw-r--r--src/Hakyll/Web/Template.hs15
-rw-r--r--src/Hakyll/Web/Template/Context.hs10
-rw-r--r--src/Hakyll/Web/Template/Internal.hs2
-rw-r--r--stack.yaml12
-rw-r--r--tests/Hakyll/Core/Provider/Metadata/Tests.hs27
-rw-r--r--tests/Hakyll/Core/Provider/Tests.hs13
-rw-r--r--tests/Hakyll/Core/Routes/Tests.hs12
-rw-r--r--tests/Hakyll/Core/Rules/Tests.hs13
-rw-r--r--web/examples.markdown6
-rw-r--r--web/index.markdown6
-rw-r--r--web/releases.markdown8
-rw-r--r--web/site.hs57
-rw-r--r--web/templates/default.html4
-rw-r--r--web/templates/tutorial-item.html1
-rw-r--r--web/templates/tutorials.html45
-rw-r--r--web/tutorials/01-installation.markdown32
-rw-r--r--web/tutorials/02-basics.markdown22
-rw-r--r--web/tutorials/04-compilers.markdown8
-rw-r--r--web/tutorials/external-add-tags-to-your-hakyll-blog.md6
-rw-r--r--web/tutorials/external-clean-urls-with-hakyll.md6
-rw-r--r--web/tutorials/external-deploying-with-widely.md6
-rw-r--r--web/tutorials/external-functionfield.md6
-rw-r--r--web/tutorials/external-inlining-and-compressing-css.md6
-rw-r--r--web/tutorials/faq.markdown12
-rw-r--r--web/tutorials/github-pages-tutorial.md174
64 files changed, 715 insertions, 338 deletions
diff --git a/.gitignore b/.gitignore
index 7e47278..e670565 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,4 @@
-# Ignore swap files and cabal output.
+# Ignore swap files and stack/cabal output.
*.hi
*.o
*.swo
@@ -10,6 +10,7 @@ dist
tags
cabal.sandbox.config
.cabal-sandbox/
+.stack-work
# Ignore test builds.
tests/Main
diff --git a/.travis.yml b/.travis.yml
index 999bd37..7d5fedb 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1 +1,2 @@
language: haskell
+ghc: '7.10'
diff --git a/README.markdown b/README.markdown
index 87e5564..1c1d108 100644
--- a/README.markdown
+++ b/README.markdown
@@ -1,6 +1,6 @@
# hakyll
-[![Build Status](https://secure.travis-ci.org/jaspervdj/hakyll.png?branch=master)](http://travis-ci.org/jaspervdj/hakyll)
+[![Build Status](https://img.shields.io/travis/jaspervdj/hakyll.svg)](http://travis-ci.org/jaspervdj/hakyll)
Hakyll is a static site generator library in Haskell. More information
(including a tutorial) can be found on
@@ -9,3 +9,7 @@ Hakyll is a static site generator library in Haskell. More information
You can install this library using cabal:
cabal install hakyll
+
+Or using stack:
+
+ stack install hakyll
diff --git a/data/example/posts/2012-08-12-spqr.markdown b/data/example/posts/2015-08-12-spqr.markdown
index 3704aa5..3704aa5 100644
--- a/data/example/posts/2012-08-12-spqr.markdown
+++ b/data/example/posts/2015-08-12-spqr.markdown
diff --git a/data/example/posts/2012-10-07-rosa-rosa-rosam.markdown b/data/example/posts/2015-10-07-rosa-rosa-rosam.markdown
index bbda8fd..bbda8fd 100644
--- a/data/example/posts/2012-10-07-rosa-rosa-rosam.markdown
+++ b/data/example/posts/2015-10-07-rosa-rosa-rosam.markdown
diff --git a/data/example/posts/2012-11-28-carpe-diem.markdown b/data/example/posts/2015-11-28-carpe-diem.markdown
index bd115da..bd115da 100644
--- a/data/example/posts/2012-11-28-carpe-diem.markdown
+++ b/data/example/posts/2015-11-28-carpe-diem.markdown
diff --git a/data/example/posts/2012-12-07-tu-quoque.markdown b/data/example/posts/2015-12-07-tu-quoque.markdown
index bdf2ea4..bdf2ea4 100644
--- a/data/example/posts/2012-12-07-tu-quoque.markdown
+++ b/data/example/posts/2015-12-07-tu-quoque.markdown
diff --git a/hakyll.cabal b/hakyll.cabal
index 7bb79cf..9701ddc 100644
--- a/hakyll.cabal
+++ b/hakyll.cabal
@@ -1,5 +1,5 @@
Name: hakyll
-Version: 4.7.5.1
+Version: 4.7.5.2
Synopsis: A static website compiler library
Description:
@@ -42,10 +42,10 @@ Build-Type: Simple
Data-Dir: data
Data-files:
- example/posts/2012-11-28-carpe-diem.markdown
- example/posts/2012-10-07-rosa-rosa-rosam.markdown
- example/posts/2012-12-07-tu-quoque.markdown
- example/posts/2012-08-12-spqr.markdown
+ example/posts/2015-11-28-carpe-diem.markdown
+ example/posts/2015-10-07-rosa-rosa-rosam.markdown
+ example/posts/2015-12-07-tu-quoque.markdown
+ example/posts/2015-08-12-spqr.markdown
example/site.hs
example/images/haskell-logo.png
example/templates/post-list.html
@@ -121,6 +121,8 @@ Library
Hakyll.Web.Template.List
Other-Modules:
+ Data.List.Extended
+ Data.Yaml.Extended
Hakyll.Check
Hakyll.Commands
Hakyll.Core.Compiler.Internal
@@ -140,33 +142,37 @@ Library
Paths_hakyll
Build-Depends:
- base >= 4 && < 5,
- binary >= 0.5 && < 0.8,
- blaze-html >= 0.5 && < 0.9,
- blaze-markup >= 0.5.1 && < 0.8,
- bytestring >= 0.9 && < 0.11,
- cmdargs >= 0.10 && < 0.11,
- containers >= 0.3 && < 0.6,
- cryptohash >= 0.7 && < 0.12,
- data-default >= 0.4 && < 0.6,
- deepseq >= 1.3 && < 1.5,
- directory >= 1.0 && < 1.3,
- filepath >= 1.0 && < 1.5,
- lrucache >= 1.1.1 && < 1.3,
- mtl >= 1 && < 2.3,
- network >= 2.6 && < 2.7,
- network-uri >= 2.6 && < 2.7,
- pandoc >= 1.14 && < 1.17,
- pandoc-citeproc >= 0.4 && < 0.10,
- parsec >= 3.0 && < 3.2,
- process >= 1.0 && < 1.3,
- random >= 1.0 && < 1.2,
- regex-base >= 0.93 && < 0.94,
- regex-tdfa >= 1.1 && < 1.3,
- tagsoup >= 0.13.1 && < 0.14,
- text >= 0.11 && < 1.3,
- time >= 1.4 && < 1.6,
- time-locale-compat >= 0.1 && < 0.2
+ base >= 4.8 && < 5,
+ binary >= 0.5 && < 0.8,
+ blaze-html >= 0.5 && < 0.9,
+ blaze-markup >= 0.5.1 && < 0.8,
+ bytestring >= 0.9 && < 0.11,
+ cmdargs >= 0.10 && < 0.11,
+ containers >= 0.3 && < 0.6,
+ cryptohash >= 0.7 && < 0.12,
+ data-default >= 0.4 && < 0.6,
+ deepseq >= 1.3 && < 1.5,
+ directory >= 1.0 && < 1.3,
+ filepath >= 1.0 && < 1.5,
+ lrucache >= 1.1.1 && < 1.3,
+ mtl >= 1 && < 2.3,
+ network >= 2.6 && < 2.7,
+ network-uri >= 2.6 && < 2.7,
+ pandoc >= 1.14 && < 1.18,
+ pandoc-citeproc >= 0.4 && < 0.10,
+ parsec >= 3.0 && < 3.2,
+ process >= 1.0 && < 1.3,
+ random >= 1.0 && < 1.2,
+ regex-base >= 0.93 && < 0.94,
+ regex-tdfa >= 1.1 && < 1.3,
+ resourcet >= 1.1 && < 1.2,
+ tagsoup >= 0.13.1 && < 0.14,
+ text >= 0.11 && < 1.3,
+ time >= 1.4 && < 1.6,
+ time-locale-compat >= 0.1 && < 0.2,
+ unordered-containers >= 0.2 && < 0.3,
+ vector >= 0.11 && < 0.12,
+ yaml >= 0.8 && < 0.9
If flag(previewServer)
Build-depends:
@@ -226,33 +232,37 @@ Test-suite hakyll-tests
test-framework-hunit >= 0.3 && < 0.4,
test-framework-quickcheck2 >= 0.3 && < 0.4,
-- Copy pasted from hakyll dependencies:
- base >= 4 && < 5,
- binary >= 0.5 && < 0.8,
- blaze-html >= 0.5 && < 0.9,
- blaze-markup >= 0.5.1 && < 0.8,
- bytestring >= 0.9 && < 0.11,
- cmdargs >= 0.10 && < 0.11,
- containers >= 0.3 && < 0.6,
- cryptohash >= 0.7 && < 0.12,
- data-default >= 0.4 && < 0.6,
- deepseq >= 1.3 && < 1.5,
- directory >= 1.0 && < 1.3,
- filepath >= 1.0 && < 1.5,
- lrucache >= 1.1.1 && < 1.3,
- mtl >= 1 && < 2.3,
- network >= 2.6 && < 2.7,
- network-uri >= 2.6 && < 2.7,
- pandoc >= 1.14 && < 1.17,
- pandoc-citeproc >= 0.4 && < 0.10,
- parsec >= 3.0 && < 3.2,
- process >= 1.0 && < 1.3,
- random >= 1.0 && < 1.2,
- regex-base >= 0.93 && < 0.94,
- regex-tdfa >= 1.1 && < 1.3,
- tagsoup >= 0.13.1 && < 0.14,
- text >= 0.11 && < 1.3,
- time >= 1.5 && < 1.6,
- time-locale-compat >= 0.1 && < 0.2
+ base >= 4.8 && < 5,
+ binary >= 0.5 && < 0.8,
+ blaze-html >= 0.5 && < 0.9,
+ blaze-markup >= 0.5.1 && < 0.8,
+ bytestring >= 0.9 && < 0.11,
+ cmdargs >= 0.10 && < 0.11,
+ containers >= 0.3 && < 0.6,
+ cryptohash >= 0.7 && < 0.12,
+ data-default >= 0.4 && < 0.6,
+ deepseq >= 1.3 && < 1.5,
+ directory >= 1.0 && < 1.3,
+ filepath >= 1.0 && < 1.5,
+ lrucache >= 1.1.1 && < 1.3,
+ mtl >= 1 && < 2.3,
+ network >= 2.6 && < 2.7,
+ network-uri >= 2.6 && < 2.7,
+ pandoc >= 1.14 && < 1.18,
+ pandoc-citeproc >= 0.4 && < 0.10,
+ parsec >= 3.0 && < 3.2,
+ process >= 1.0 && < 1.3,
+ random >= 1.0 && < 1.2,
+ regex-base >= 0.93 && < 0.94,
+ regex-tdfa >= 1.1 && < 1.3,
+ resourcet >= 1.1 && < 1.2,
+ tagsoup >= 0.13.1 && < 0.14,
+ text >= 0.11 && < 1.3,
+ time >= 1.4 && < 1.6,
+ time-locale-compat >= 0.1 && < 0.2,
+ unordered-containers >= 0.2 && < 0.3,
+ vector >= 0.11 && < 0.12,
+ yaml >= 0.8 && < 0.9
If flag(previewServer)
Build-depends:
@@ -291,3 +301,7 @@ Executable hakyll-init
base >= 4 && < 5,
directory >= 1.0 && < 1.3,
filepath >= 1.0 && < 1.5
+
+ Other-modules:
+ Hakyll.Core.Util.File
+ Paths_hakyll
diff --git a/src/Data/List/Extended.hs b/src/Data/List/Extended.hs
new file mode 100644
index 0000000..485cba8
--- /dev/null
+++ b/src/Data/List/Extended.hs
@@ -0,0 +1,15 @@
+module Data.List.Extended
+ ( module Data.List
+ , breakWhen
+ ) where
+
+import Data.List
+
+-- | Like 'break', but can act on the entire tail of the list.
+breakWhen :: ([a] -> Bool) -> [a] -> ([a], [a])
+breakWhen predicate = go []
+ where
+ go buf [] = (reverse buf, [])
+ go buf (x : xs)
+ | predicate (x : xs) = (reverse buf, x : xs)
+ | otherwise = go (x : buf) xs
diff --git a/src/Data/Yaml/Extended.hs b/src/Data/Yaml/Extended.hs
new file mode 100644
index 0000000..099e945
--- /dev/null
+++ b/src/Data/Yaml/Extended.hs
@@ -0,0 +1,20 @@
+module Data.Yaml.Extended
+ ( module Data.Yaml
+ , toString
+ , toList
+ ) where
+
+import qualified Data.Text as T
+import qualified Data.Vector as V
+import Data.Yaml
+
+toString :: Value -> Maybe String
+toString (String t) = Just (T.unpack t)
+toString (Bool True) = Just "true"
+toString (Bool False) = Just "false"
+toString (Number d) = Just (show d)
+toString _ = Nothing
+
+toList :: Value -> Maybe [Value]
+toList (Array a) = Just (V.toList a)
+toList _ = Nothing
diff --git a/src/Hakyll/Check.hs b/src/Hakyll/Check.hs
index 8e808ba..8bfc2aa 100644
--- a/src/Hakyll/Check.hs
+++ b/src/Hakyll/Check.hs
@@ -8,42 +8,44 @@ module Hakyll.Check
--------------------------------------------------------------------------------
-import Control.Applicative ((<$>))
-import Control.Monad (forM_)
-import Control.Monad.Reader (ask)
-import Control.Monad.RWS (RWST, runRWST)
-import Control.Monad.Trans (liftIO)
-import Control.Monad.Writer (tell)
-import Data.List (isPrefixOf)
-import Data.Monoid (Monoid (..))
-import Data.Set (Set)
-import qualified Data.Set as S
-import Network.URI (unEscapeString)
-import System.Directory (doesDirectoryExist, doesFileExist)
-import System.Exit (ExitCode (..))
-import System.FilePath (takeDirectory, takeExtension, (</>))
-import qualified Text.HTML.TagSoup as TS
+import Control.Monad (forM_)
+import Control.Monad.Reader (ask)
+import Control.Monad.RWS (RWST, runRWST)
+import Control.Monad.Trans (liftIO)
+import Control.Monad.Trans.Resource (runResourceT)
+import Control.Monad.Writer (tell)
+import Data.List (isPrefixOf)
+import Data.Set (Set)
+import qualified Data.Set as S
+import Network.URI (unEscapeString)
+import System.Directory (doesDirectoryExist,
+ doesFileExist)
+import System.Exit (ExitCode (..))
+import System.FilePath (takeDirectory, takeExtension,
+ (</>))
+import qualified Text.HTML.TagSoup as TS
--------------------------------------------------------------------------------
#ifdef CHECK_EXTERNAL
-import Control.Exception (AsyncException (..),
- SomeException (..), handle, throw)
-import Control.Monad.State (get, modify)
-import Data.List (intercalate)
-import Data.Typeable (cast)
-import Data.Version (versionBranch)
-import GHC.Exts (fromString)
-import qualified Network.HTTP.Conduit as Http
-import qualified Network.HTTP.Types as Http
-import qualified Paths_hakyll as Paths_hakyll
+import Control.Exception (AsyncException (..),
+ SomeException (..), handle,
+ throw)
+import Control.Monad.State (get, modify)
+import Data.List (intercalate)
+import Data.Typeable (cast)
+import Data.Version (versionBranch)
+import GHC.Exts (fromString)
+import qualified Network.HTTP.Conduit as Http
+import qualified Network.HTTP.Types as Http
+import qualified Paths_hakyll as Paths_hakyll
#endif
--------------------------------------------------------------------------------
import Hakyll.Core.Configuration
-import Hakyll.Core.Logger (Logger)
-import qualified Hakyll.Core.Logger as Logger
+import Hakyll.Core.Logger (Logger)
+import qualified Hakyll.Core.Logger as Logger
import Hakyll.Core.Util.File
import Hakyll.Web.Html
@@ -196,8 +198,9 @@ checkExternalUrl url = do
if not needsCheck || checked
then Logger.debug logger "Already checked, skipping"
else do
- isOk <- liftIO $ handle (failure logger) $
- Http.withManager $ \mgr -> do
+ isOk <- liftIO $ handle (failure logger) $ do
+ mgr <- Http.newManager Http.tlsManagerSettings
+ runResourceT $ do
request <- Http.parseUrl urlToCheck
response <- Http.http (settings request) mgr
let code = Http.statusCode (Http.responseStatus response)
diff --git a/src/Hakyll/Commands.hs b/src/Hakyll/Commands.hs
index 6f81080..5906247 100644
--- a/src/Hakyll/Commands.hs
+++ b/src/Hakyll/Commands.hs
@@ -14,11 +14,8 @@ module Hakyll.Commands
--------------------------------------------------------------------------------
-import Control.Applicative
import Control.Concurrent
-import Control.Monad (void)
import System.Exit (ExitCode, exitWith)
-import System.IO.Error (catchIOError)
--------------------------------------------------------------------------------
import qualified Hakyll.Check as Check
diff --git a/src/Hakyll/Core/Compiler.hs b/src/Hakyll/Core/Compiler.hs
index f99f93b..ae9fbf1 100644
--- a/src/Hakyll/Core/Compiler.hs
+++ b/src/Hakyll/Core/Compiler.hs
@@ -28,7 +28,6 @@ module Hakyll.Core.Compiler
--------------------------------------------------------------------------------
-import Control.Applicative ((<$>))
import Control.Monad (when)
import Data.Binary (Binary)
import Data.ByteString.Lazy (ByteString)
diff --git a/src/Hakyll/Core/Compiler/Internal.hs b/src/Hakyll/Core/Compiler/Internal.hs
index 61fb640..7b1df83 100644
--- a/src/Hakyll/Core/Compiler/Internal.hs
+++ b/src/Hakyll/Core/Compiler/Internal.hs
@@ -28,12 +28,10 @@ module Hakyll.Core.Compiler.Internal
--------------------------------------------------------------------------------
-import Control.Applicative (Alternative (..),
- Applicative (..), (<$>))
+import Control.Applicative (Alternative (..))
import Control.Exception (SomeException, handle)
import Control.Monad (forM_)
-import Control.Monad.Error (MonadError (..))
-import Data.Monoid (Monoid (..))
+import Control.Monad.Except (MonadError (..))
import Data.Set (Set)
import qualified Data.Set as S
diff --git a/src/Hakyll/Core/Compiler/Require.hs b/src/Hakyll/Core/Compiler/Require.hs
index d7635a9..c9373bf 100644
--- a/src/Hakyll/Core/Compiler/Require.hs
+++ b/src/Hakyll/Core/Compiler/Require.hs
@@ -13,7 +13,6 @@ module Hakyll.Core.Compiler.Require
--------------------------------------------------------------------------------
-import Control.Applicative ((<$>))
import Control.Monad (when)
import Data.Binary (Binary)
import qualified Data.Set as S
diff --git a/src/Hakyll/Core/Dependencies.hs b/src/Hakyll/Core/Dependencies.hs
index ebb6fd0..4a51b9c 100644
--- a/src/Hakyll/Core/Dependencies.hs
+++ b/src/Hakyll/Core/Dependencies.hs
@@ -8,7 +8,6 @@ module Hakyll.Core.Dependencies
--------------------------------------------------------------------------------
-import Control.Applicative ((<$>), (<*>))
import Control.Monad (foldM, forM_, unless, when)
import Control.Monad.Reader (ask)
import Control.Monad.RWS (RWS, runRWS)
diff --git a/src/Hakyll/Core/File.hs b/src/Hakyll/Core/File.hs
index 26724e1..1c3a9df 100644
--- a/src/Hakyll/Core/File.hs
+++ b/src/Hakyll/Core/File.hs
@@ -11,7 +11,6 @@ module Hakyll.Core.File
--------------------------------------------------------------------------------
-import Control.Applicative ((<$>))
import Data.Binary (Binary (..))
import Data.Typeable (Typeable)
import System.Directory (copyFile, doesFileExist,
diff --git a/src/Hakyll/Core/Identifier.hs b/src/Hakyll/Core/Identifier.hs
index 7ac06d8..777811c 100644
--- a/src/Hakyll/Core/Identifier.hs
+++ b/src/Hakyll/Core/Identifier.hs
@@ -19,7 +19,6 @@ module Hakyll.Core.Identifier
--------------------------------------------------------------------------------
-import Control.Applicative ((<$>), (<*>))
import Control.DeepSeq (NFData (..))
import Data.List (intercalate)
import System.FilePath (dropTrailingPathSeparator, splitPath)
diff --git a/src/Hakyll/Core/Identifier/Pattern.hs b/src/Hakyll/Core/Identifier/Pattern.hs
index 92d7705..47ad21b 100644
--- a/src/Hakyll/Core/Identifier/Pattern.hs
+++ b/src/Hakyll/Core/Identifier/Pattern.hs
@@ -57,13 +57,11 @@ module Hakyll.Core.Identifier.Pattern
--------------------------------------------------------------------------------
-import Control.Applicative (pure, (<$>), (<*>))
import Control.Arrow ((&&&), (>>>))
import Control.Monad (msum)
import Data.Binary (Binary (..), getWord8, putWord8)
import Data.List (inits, isPrefixOf, tails)
import Data.Maybe (isJust)
-import Data.Monoid (Monoid, mappend, mempty)
import Data.Set (Set)
import qualified Data.Set as S
diff --git a/src/Hakyll/Core/Item.hs b/src/Hakyll/Core/Item.hs
index 840b6a8..e05df42 100644
--- a/src/Hakyll/Core/Item.hs
+++ b/src/Hakyll/Core/Item.hs
@@ -10,10 +10,8 @@ module Hakyll.Core.Item
--------------------------------------------------------------------------------
-import Control.Applicative ((<$>), (<*>))
import Data.Binary (Binary (..))
import Data.Foldable (Foldable (..))
-import Data.Traversable (Traversable (..))
import Data.Typeable (Typeable)
import Prelude hiding (foldr)
diff --git a/src/Hakyll/Core/Logger.hs b/src/Hakyll/Core/Logger.hs
index 4731c20..6f950a6 100644
--- a/src/Hakyll/Core/Logger.hs
+++ b/src/Hakyll/Core/Logger.hs
@@ -13,7 +13,6 @@ module Hakyll.Core.Logger
--------------------------------------------------------------------------------
-import Control.Applicative (pure, (<$>), (<*>))
import Control.Concurrent (forkIO)
import Control.Concurrent.Chan (Chan, newChan, readChan, writeChan)
import Control.Concurrent.MVar (MVar, newEmptyMVar, putMVar, takeMVar)
diff --git a/src/Hakyll/Core/Metadata.hs b/src/Hakyll/Core/Metadata.hs
index 3ce854f..1cf536e 100644
--- a/src/Hakyll/Core/Metadata.hs
+++ b/src/Hakyll/Core/Metadata.hs
@@ -1,28 +1,46 @@
--------------------------------------------------------------------------------
module Hakyll.Core.Metadata
( Metadata
+ , lookupString
+ , lookupStringList
+
, MonadMetadata (..)
, getMetadataField
, getMetadataField'
, makePatternDependency
+
+ , BinaryMetadata (..)
) where
--------------------------------------------------------------------------------
+import Control.Arrow (second)
import Control.Monad (forM)
-import Data.Map (Map)
-import qualified Data.Map as M
+import Data.Binary (Binary (..), getWord8,
+ putWord8, Get)
+import qualified Data.HashMap.Strict as HMS
import qualified Data.Set as S
-
-
---------------------------------------------------------------------------------
+import qualified Data.Text as T
+import qualified Data.Vector as V
+import qualified Data.Yaml.Extended as Yaml
import Hakyll.Core.Dependencies
import Hakyll.Core.Identifier
import Hakyll.Core.Identifier.Pattern
--------------------------------------------------------------------------------
-type Metadata = Map String String
+type Metadata = Yaml.Object
+
+
+--------------------------------------------------------------------------------
+lookupString :: String -> Metadata -> Maybe String
+lookupString key meta = HMS.lookup (T.pack key) meta >>= Yaml.toString
+
+
+--------------------------------------------------------------------------------
+lookupStringList :: String -> Metadata -> Maybe [String]
+lookupStringList key meta =
+ HMS.lookup (T.pack key) meta >>= Yaml.toList >>= mapM Yaml.toString
--------------------------------------------------------------------------------
@@ -42,7 +60,7 @@ class Monad m => MonadMetadata m where
getMetadataField :: MonadMetadata m => Identifier -> String -> m (Maybe String)
getMetadataField identifier key = do
metadata <- getMetadata identifier
- return $ M.lookup key metadata
+ return $ lookupString key metadata
--------------------------------------------------------------------------------
@@ -62,3 +80,59 @@ makePatternDependency :: MonadMetadata m => Pattern -> m Dependency
makePatternDependency pattern = do
matches' <- getMatches pattern
return $ PatternDependency pattern (S.fromList matches')
+
+
+--------------------------------------------------------------------------------
+-- | Newtype wrapper for serialization.
+newtype BinaryMetadata = BinaryMetadata
+ {unBinaryMetadata :: Metadata}
+
+
+instance Binary BinaryMetadata where
+ put (BinaryMetadata obj) = put (BinaryYaml $ Yaml.Object obj)
+ get = do
+ BinaryYaml (Yaml.Object obj) <- get
+ return $ BinaryMetadata obj
+
+
+--------------------------------------------------------------------------------
+newtype BinaryYaml = BinaryYaml {unBinaryYaml :: Yaml.Value}
+
+
+--------------------------------------------------------------------------------
+instance Binary BinaryYaml where
+ put (BinaryYaml yaml) = case yaml of
+ Yaml.Object obj -> do
+ putWord8 0
+ let list :: [(T.Text, BinaryYaml)]
+ list = map (second BinaryYaml) $ HMS.toList obj
+ put list
+
+ Yaml.Array arr -> do
+ putWord8 1
+ let list = map BinaryYaml (V.toList arr) :: [BinaryYaml]
+ put list
+
+ Yaml.String s -> putWord8 2 >> put s
+ Yaml.Number n -> putWord8 3 >> put n
+ Yaml.Bool b -> putWord8 4 >> put b
+ Yaml.Null -> putWord8 5
+
+ get = do
+ tag <- getWord8
+ case tag of
+ 0 -> do
+ list <- get :: Get [(T.Text, BinaryYaml)]
+ return $ BinaryYaml $ Yaml.Object $
+ HMS.fromList $ map (second unBinaryYaml) list
+
+ 1 -> do
+ list <- get :: Get [BinaryYaml]
+ return $ BinaryYaml $
+ Yaml.Array $ V.fromList $ map unBinaryYaml list
+
+ 2 -> BinaryYaml . Yaml.String <$> get
+ 3 -> BinaryYaml . Yaml.Number <$> get
+ 4 -> BinaryYaml . Yaml.Bool <$> get
+ 5 -> return $ BinaryYaml Yaml.Null
+ _ -> fail "Data.Binary.get: Invalid Binary Metadata"
diff --git a/src/Hakyll/Core/Provider/Internal.hs b/src/Hakyll/Core/Provider/Internal.hs
index 34400fd..c298653 100644
--- a/src/Hakyll/Core/Provider/Internal.hs
+++ b/src/Hakyll/Core/Provider/Internal.hs
@@ -20,7 +20,6 @@ module Hakyll.Core.Provider.Internal
--------------------------------------------------------------------------------
-import Control.Applicative ((<$>), (<*>))
import Control.DeepSeq (NFData (..), deepseq)
import Control.Monad (forM)
import Data.Binary (Binary (..))
@@ -28,7 +27,6 @@ import qualified Data.ByteString.Lazy as BL
import Data.Map (Map)
import qualified Data.Map as M
import Data.Maybe (fromMaybe)
-import Data.Monoid (mempty)
import Data.Set (Set)
import qualified Data.Set as S
import Data.Time (Day (..), UTCTime (..))
diff --git a/src/Hakyll/Core/Provider/Metadata.hs b/src/Hakyll/Core/Provider/Metadata.hs
index 7e4d7ed..0b0291c 100644
--- a/src/Hakyll/Core/Provider/Metadata.hs
+++ b/src/Hakyll/Core/Provider/Metadata.hs
@@ -1,33 +1,31 @@
--------------------------------------------------------------------------------
-- | Internal module to parse metadata
+{-# LANGUAGE BangPatterns #-}
+{-# LANGUAGE RecordWildCards #-}
module Hakyll.Core.Provider.Metadata
( loadMetadata
- , metadata
- , page
+ , parsePage
- -- This parser can be reused in some places
- , metadataKey
+ , MetadataException (..)
) where
--------------------------------------------------------------------------------
-import Control.Applicative
import Control.Arrow (second)
+import Control.Exception (Exception, throwIO)
+import Control.Monad (guard)
import qualified Data.ByteString.Char8 as BC
-import Data.List (intercalate)
+import Data.List.Extended (breakWhen)
import qualified Data.Map as M
-import System.IO as IO
-import Text.Parsec ((<?>))
-import qualified Text.Parsec as P
-import Text.Parsec.String (Parser)
-
-
---------------------------------------------------------------------------------
+import Data.Maybe (fromMaybe)
+import Data.Monoid ((<>))
+import qualified Data.Text as T
+import qualified Data.Text.Encoding as T
+import qualified Data.Yaml as Yaml
import Hakyll.Core.Identifier
import Hakyll.Core.Metadata
import Hakyll.Core.Provider.Internal
-import Hakyll.Core.Util.Parser
-import Hakyll.Core.Util.String
+import System.IO as IO
--------------------------------------------------------------------------------
@@ -36,13 +34,13 @@ loadMetadata p identifier = do
hasHeader <- probablyHasMetadataHeader fp
(md, body) <- if hasHeader
then second Just <$> loadMetadataHeader fp
- else return (M.empty, Nothing)
+ else return (mempty, Nothing)
emd <- case mi of
- Nothing -> return M.empty
+ Nothing -> return mempty
Just mi' -> loadMetadataFile $ resourceFilePath p mi'
- return (M.union md emd, body)
+ return (md <> emd, body)
where
normal = setVersion Nothing identifier
fp = resourceFilePath p identifier
@@ -52,19 +50,17 @@ loadMetadata p identifier = do
--------------------------------------------------------------------------------
loadMetadataHeader :: FilePath -> IO (Metadata, String)
loadMetadataHeader fp = do
- contents <- readFile fp
- case P.parse page fp contents of
- Left err -> error (show err)
- Right (md, b) -> return (M.fromList md, b)
+ fileContent <- readFile fp
+ case parsePage fileContent of
+ Right x -> return x
+ Left err -> throwIO $ MetadataException fp err
--------------------------------------------------------------------------------
loadMetadataFile :: FilePath -> IO Metadata
loadMetadataFile fp = do
- contents <- readFile fp
- case P.parse metadata fp contents of
- Left err -> error (show err)
- Right md -> return $ M.fromList md
+ errOrMeta <- Yaml.decodeFileEither fp
+ either (fail . show) return errOrMeta
--------------------------------------------------------------------------------
@@ -83,53 +79,71 @@ probablyHasMetadataHeader fp = do
--------------------------------------------------------------------------------
--- | Space or tab, no newline
-inlineSpace :: Parser Char
-inlineSpace = P.oneOf ['\t', ' '] <?> "space"
+-- | Parse the page metadata and body.
+splitMetadata :: String -> (Maybe String, String)
+splitMetadata str0 = fromMaybe (Nothing, str0) $ do
+ guard $ leading >= 3
+ let !str1 = drop leading str0
+ guard $ all isNewline (take 1 str1)
+ let !(!meta, !content0) = breakWhen isTrailing str1
+ guard $ not $ null content0
+ let !content1 = drop (leading + 1) content0
+ !content2 = dropWhile isNewline $ dropWhile isInlineSpace content1
+ -- Adding this newline fixes the line numbers reported by the YAML parser.
+ -- It's a bit ugly but it works.
+ return (Just ('\n' : meta), content2)
+ where
+ -- Parse the leading "---"
+ !leading = length $ takeWhile (== '-') str0
+
+ -- Predicate to recognize the trailing "---" or "..."
+ isTrailing [] = False
+ isTrailing (x : xs) =
+ isNewline x && length (takeWhile isDash xs) == leading
+
+ -- Characters
+ isNewline c = c == '\n' || c == '\r'
+ isDash c = c == '-' || c == '.'
+ isInlineSpace c = c == '\t' || c == ' '
--------------------------------------------------------------------------------
--- | Parse Windows newlines as well (i.e. "\n" or "\r\n")
-newline :: Parser String
-newline = P.string "\n" <|> P.string "\r\n"
+parseMetadata :: String -> Either Yaml.ParseException Metadata
+parseMetadata = Yaml.decodeEither' . T.encodeUtf8 . T.pack
--------------------------------------------------------------------------------
--- | Parse a single metadata field
-metadataField :: Parser (String, String)
-metadataField = do
- key <- metadataKey
- _ <- P.char ':'
- P.skipMany1 inlineSpace <?> "space followed by metadata for: " ++ key
- value <- P.manyTill P.anyChar newline
- trailing' <- P.many trailing
- return (key, trim $ intercalate " " $ value : trailing')
+parsePage :: String -> Either Yaml.ParseException (Metadata, String)
+parsePage fileContent = case mbMetaBlock of
+ Nothing -> return (mempty, content)
+ Just metaBlock -> case parseMetadata metaBlock of
+ Left err -> Left err
+ Right meta -> return (meta, content)
where
- trailing = P.many1 inlineSpace *> P.manyTill P.anyChar newline
+ !(!mbMetaBlock, !content) = splitMetadata fileContent
--------------------------------------------------------------------------------
--- | Parse a metadata block
-metadata :: Parser [(String, String)]
-metadata = P.many metadataField
+-- | Thrown in the IO monad if things go wrong. Provides a nice-ish error
+-- message.
+data MetadataException = MetadataException FilePath Yaml.ParseException
--------------------------------------------------------------------------------
--- | Parse a metadata block, including delimiters and trailing newlines
-metadataBlock :: Parser [(String, String)]
-metadataBlock = do
- open <- P.many1 (P.char '-') <* P.many inlineSpace <* newline
- metadata' <- metadata
- _ <- P.choice $ map (P.string . replicate (length open)) ['-', '.']
- P.skipMany inlineSpace
- P.skipMany1 newline
- return metadata'
+instance Exception MetadataException
--------------------------------------------------------------------------------
--- | Parse a page consisting of a metadata header and a body
-page :: Parser ([(String, String)], String)
-page = do
- metadata' <- P.option [] metadataBlock
- body <- P.many P.anyChar
- return (metadata', body)
+instance Show MetadataException where
+ show (MetadataException fp err) =
+ fp ++ ": " ++ Yaml.prettyPrintParseException err ++ hint
+
+ where
+ hint = case err of
+ Yaml.InvalidYaml (Just (Yaml.YamlParseException {..}))
+ | yamlProblem == problem -> "\n" ++
+ "Hint: if the metadata value contains characters such\n" ++
+ "as ':' or '-', try enclosing it in quotes."
+ _ -> ""
+
+ problem = "mapping values are not allowed in this context"
diff --git a/src/Hakyll/Core/Provider/MetadataCache.hs b/src/Hakyll/Core/Provider/MetadataCache.hs
index 28d2bd5..46dbf3e 100644
--- a/src/Hakyll/Core/Provider/MetadataCache.hs
+++ b/src/Hakyll/Core/Provider/MetadataCache.hs
@@ -8,9 +8,6 @@ module Hakyll.Core.Provider.MetadataCache
--------------------------------------------------------------------------------
import Control.Monad (unless)
-import qualified Data.Map as M
-
---------------------------------------------------------------------------------
import Hakyll.Core.Identifier
import Hakyll.Core.Metadata
import Hakyll.Core.Provider.Internal
@@ -21,11 +18,11 @@ import qualified Hakyll.Core.Store as Store
--------------------------------------------------------------------------------
resourceMetadata :: Provider -> Identifier -> IO Metadata
resourceMetadata p r
- | not (resourceExists p r) = return M.empty
+ | not (resourceExists p r) = return mempty
| otherwise = do
-- TODO keep time in md cache
load p r
- Store.Found md <- Store.get (providerStore p)
+ Store.Found (BinaryMetadata md) <- Store.get (providerStore p)
[name, toFilePath r, "metadata"]
return md
@@ -52,7 +49,7 @@ load p r = do
mmof <- Store.isMember store mdk
unless mmof $ do
(md, body) <- loadMetadata p r
- Store.set store mdk md
+ Store.set store mdk (BinaryMetadata md)
Store.set store bk body
where
store = providerStore p
diff --git a/src/Hakyll/Core/Routes.hs b/src/Hakyll/Core/Routes.hs
index 470d727..513725f 100644
--- a/src/Hakyll/Core/Routes.hs
+++ b/src/Hakyll/Core/Routes.hs
@@ -42,7 +42,6 @@ module Hakyll.Core.Routes
--------------------------------------------------------------------------------
-import Data.Monoid (Monoid, mappend, mempty)
import System.FilePath (replaceExtension)
diff --git a/src/Hakyll/Core/Rules.hs b/src/Hakyll/Core/Rules.hs
index 14befde..41b9a73 100644
--- a/src/Hakyll/Core/Rules.hs
+++ b/src/Hakyll/Core/Rules.hs
@@ -33,13 +33,11 @@ module Hakyll.Core.Rules
--------------------------------------------------------------------------------
-import Control.Applicative ((<$>))
import Control.Monad.Reader (ask, local)
import Control.Monad.State (get, modify, put)
import Control.Monad.Trans (liftIO)
import Control.Monad.Writer (censor, tell)
import Data.Maybe (fromMaybe)
-import Data.Monoid (mempty)
import qualified Data.Set as S
diff --git a/src/Hakyll/Core/Rules/Internal.hs b/src/Hakyll/Core/Rules/Internal.hs
index a7c2059..0641dcf 100644
--- a/src/Hakyll/Core/Rules/Internal.hs
+++ b/src/Hakyll/Core/Rules/Internal.hs
@@ -12,12 +12,10 @@ module Hakyll.Core.Rules.Internal
--------------------------------------------------------------------------------
-import Control.Applicative (Applicative, (<$>))
import Control.Monad.Reader (ask)
import Control.Monad.RWS (RWST, runRWST)
import Control.Monad.Trans (liftIO)
import qualified Data.Map as M
-import Data.Monoid (Monoid, mappend, mempty)
import Data.Set (Set)
diff --git a/src/Hakyll/Core/Runtime.hs b/src/Hakyll/Core/Runtime.hs
index e85d60d..16a5d9e 100644
--- a/src/Hakyll/Core/Runtime.hs
+++ b/src/Hakyll/Core/Runtime.hs
@@ -5,9 +5,8 @@ module Hakyll.Core.Runtime
--------------------------------------------------------------------------------
-import Control.Applicative ((<$>))
import Control.Monad (unless)
-import Control.Monad.Error (ErrorT, runErrorT, throwError)
+import Control.Monad.Except (ExceptT, runExceptT, throwError)
import Control.Monad.Reader (ask)
import Control.Monad.RWS (RWST, runRWST)
import Control.Monad.State (get, modify)
@@ -15,7 +14,6 @@ import Control.Monad.Trans (liftIO)
import Data.List (intercalate)
import Data.Map (Map)
import qualified Data.Map as M
-import Data.Monoid (mempty)
import Data.Set (Set)
import qualified Data.Set as S
import System.Exit (ExitCode (..))
@@ -77,7 +75,7 @@ run config logger rules = do
}
-- Run the program and fetch the resulting state
- result <- runErrorT $ runRWST build read' state
+ result <- runExceptT $ runRWST build read' state
case result of
Left e -> do
Logger.error logger e
@@ -117,7 +115,7 @@ data RuntimeState = RuntimeState
--------------------------------------------------------------------------------
-type Runtime a = RWST RuntimeRead () RuntimeState (ErrorT String IO) a
+type Runtime a = RWST RuntimeRead () RuntimeState (ExceptT String IO) a
--------------------------------------------------------------------------------
diff --git a/src/Hakyll/Core/Store.hs b/src/Hakyll/Core/Store.hs
index 5c3667d..fdbcf11 100644
--- a/src/Hakyll/Core/Store.hs
+++ b/src/Hakyll/Core/Store.hs
@@ -16,7 +16,6 @@ module Hakyll.Core.Store
--------------------------------------------------------------------------------
-import Control.Applicative ((<$>))
import Control.Exception (IOException, handle)
import qualified Crypto.Hash.MD5 as MD5
import Data.Binary (Binary, decode, encodeFile)
diff --git a/src/Hakyll/Core/UnixFilter.hs b/src/Hakyll/Core/UnixFilter.hs
index edc8eac..734d8d8 100644
--- a/src/Hakyll/Core/UnixFilter.hs
+++ b/src/Hakyll/Core/UnixFilter.hs
@@ -16,7 +16,6 @@ import Control.Monad (forM_)
import Data.ByteString.Lazy (ByteString)
import qualified Data.ByteString.Lazy as LB
import Data.IORef (newIORef, readIORef, writeIORef)
-import Data.Monoid (Monoid, mempty)
import System.Exit (ExitCode (..))
import System.IO (Handle, hClose, hFlush, hGetContents,
hPutStr, hSetEncoding, localeEncoding)
@@ -30,7 +29,7 @@ import Hakyll.Core.Compiler
-- | Use a unix filter as compiler. For example, we could use the 'rev' program
-- as a compiler.
--
--- > rev :: Compiler String
+-- > rev :: Compiler (Item String)
-- > rev = getResourceString >>= withItemBody (unixFilter "rev" [])
--
-- A more realistic example: one can use this to call, for example, the sass
diff --git a/src/Hakyll/Core/Util/File.hs b/src/Hakyll/Core/Util/File.hs
index b20576f..9db6b11 100644
--- a/src/Hakyll/Core/Util/File.hs
+++ b/src/Hakyll/Core/Util/File.hs
@@ -8,7 +8,6 @@ module Hakyll.Core.Util.File
--------------------------------------------------------------------------------
-import Control.Applicative ((<$>))
import Control.Monad (filterM, forM, when)
import System.Directory (createDirectoryIfMissing,
doesDirectoryExist, getDirectoryContents,
diff --git a/src/Hakyll/Core/Util/Parser.hs b/src/Hakyll/Core/Util/Parser.hs
index c5789ed..e958b76 100644
--- a/src/Hakyll/Core/Util/Parser.hs
+++ b/src/Hakyll/Core/Util/Parser.hs
@@ -7,7 +7,7 @@ module Hakyll.Core.Util.Parser
--------------------------------------------------------------------------------
-import Control.Applicative ((<$>), (<*>), (<|>))
+import Control.Applicative ((<|>))
import Control.Monad (mzero)
import qualified Text.Parsec as P
import Text.Parsec.String (Parser)
diff --git a/src/Hakyll/Web/CompressCss.hs b/src/Hakyll/Web/CompressCss.hs
index f3290f3..0534b9f 100644
--- a/src/Hakyll/Web/CompressCss.hs
+++ b/src/Hakyll/Web/CompressCss.hs
@@ -8,7 +8,6 @@ module Hakyll.Web.CompressCss
--------------------------------------------------------------------------------
-import Control.Applicative ((<$>))
import Data.Char (isSpace)
import Data.List (isPrefixOf)
diff --git a/src/Hakyll/Web/Feed.hs b/src/Hakyll/Web/Feed.hs
index 1d18430..8598f8a 100644
--- a/src/Hakyll/Web/Feed.hs
+++ b/src/Hakyll/Web/Feed.hs
@@ -25,7 +25,6 @@ module Hakyll.Web.Feed
--------------------------------------------------------------------------------
import Control.Monad ((<=<))
-import Data.Monoid (mconcat)
--------------------------------------------------------------------------------
diff --git a/src/Hakyll/Web/Paginate.hs b/src/Hakyll/Web/Paginate.hs
index cd35a2d..65b4525 100644
--- a/src/Hakyll/Web/Paginate.hs
+++ b/src/Hakyll/Web/Paginate.hs
@@ -13,7 +13,6 @@ module Hakyll.Web.Paginate
--------------------------------------------------------------------------------
import Control.Monad (forM_)
import qualified Data.Map as M
-import Data.Monoid (mconcat)
import qualified Data.Set as S
diff --git a/src/Hakyll/Web/Pandoc.hs b/src/Hakyll/Web/Pandoc.hs
index f6e9ff1..eec0a8a 100644
--- a/src/Hakyll/Web/Pandoc.hs
+++ b/src/Hakyll/Web/Pandoc.hs
@@ -22,9 +22,7 @@ module Hakyll.Web.Pandoc
--------------------------------------------------------------------------------
-import Control.Applicative ((<$>))
import qualified Data.Set as S
-import Data.Traversable (traverse)
import Text.Pandoc
import Text.Pandoc.Error (PandocError (..))
diff --git a/src/Hakyll/Web/Pandoc/Biblio.hs b/src/Hakyll/Web/Pandoc/Biblio.hs
index 53e3419..dfe6d93 100644
--- a/src/Hakyll/Web/Pandoc/Biblio.hs
+++ b/src/Hakyll/Web/Pandoc/Biblio.hs
@@ -23,22 +23,19 @@ module Hakyll.Web.Pandoc.Biblio
--------------------------------------------------------------------------------
-import Control.Applicative ((<$>))
-import Control.Monad (replicateM, liftM)
-import Data.Binary (Binary (..))
-import Data.Default (def)
-import Data.Typeable (Typeable)
-import qualified Text.CSL as CSL
-import Text.CSL.Pandoc (processCites)
-import Text.Pandoc (Pandoc, ReaderOptions (..))
-
---------------------------------------------------------------------------------
+import Control.Monad (liftM, replicateM)
+import Data.Binary (Binary (..))
+import Data.Default (def)
+import Data.Typeable (Typeable)
import Hakyll.Core.Compiler
import Hakyll.Core.Identifier
import Hakyll.Core.Item
import Hakyll.Core.Writable
import Hakyll.Web.Pandoc
import Hakyll.Web.Pandoc.Binary ()
+import qualified Text.CSL as CSL
+import Text.CSL.Pandoc (processCites)
+import Text.Pandoc (Pandoc, ReaderOptions (..))
--------------------------------------------------------------------------------
diff --git a/src/Hakyll/Web/Tags.hs b/src/Hakyll/Web/Tags.hs
index 0887856..b5b44fc 100644
--- a/src/Hakyll/Web/Tags.hs
+++ b/src/Hakyll/Web/Tags.hs
@@ -63,13 +63,12 @@ module Hakyll.Web.Tags
--------------------------------------------------------------------------------
import Control.Arrow ((&&&))
-import Control.Monad (foldM, forM, forM_)
+import Control.Monad (foldM, forM, forM_, mplus)
import Data.Char (toLower)
import Data.List (intercalate, intersperse,
sortBy)
import qualified Data.Map as M
import Data.Maybe (catMaybes, fromMaybe)
-import Data.Monoid (mconcat)
import Data.Ord (comparing)
import qualified Data.Set as S
import System.FilePath (takeBaseName, takeDirectory)
@@ -88,8 +87,8 @@ import Hakyll.Core.Item
import Hakyll.Core.Metadata
import Hakyll.Core.Rules
import Hakyll.Core.Util.String
-import Hakyll.Web.Template.Context
import Hakyll.Web.Html
+import Hakyll.Web.Template.Context
--------------------------------------------------------------------------------
@@ -103,11 +102,14 @@ data Tags = Tags
--------------------------------------------------------------------------------
-- | Obtain tags from a page in the default way: parse them from the @tags@
--- metadata field.
+-- metadata field. This can either be a list or a comma-separated string.
getTags :: MonadMetadata m => Identifier -> m [String]
getTags identifier = do
metadata <- getMetadata identifier
- return $ maybe [] (map trim . splitAll ",") $ M.lookup "tags" metadata
+ return $ fromMaybe [] $
+ (lookupStringList "tags" metadata) `mplus`
+ (map trim . splitAll "," <$> lookupString "tags" metadata)
+
--------------------------------------------------------------------------------
-- | Obtain categories from a page.
diff --git a/src/Hakyll/Web/Template.hs b/src/Hakyll/Web/Template.hs
index 194949d..65c4ac9 100644
--- a/src/Hakyll/Web/Template.hs
+++ b/src/Hakyll/Web/Template.hs
@@ -54,7 +54,7 @@
-- The @for@ macro is used for enumerating 'Context' elements that are
-- lists, i.e. constructed using the 'listField' function. Assume that
-- in a context we have an element @listField \"key\" c itms@. Then
--- the snippet
+-- the snippet
--
-- > $for(key)$
-- > $x$
@@ -70,21 +70,21 @@
--
-- > listField "things" (field "thing" (return . itemBody))
-- > (sequence [makeItem "fruits", makeItem "vegetables"])
---
+--
-- and a template
--
-- > I like
-- > $for(things)$
--- > fresh $thing$$sep$, and
+-- > fresh $thing$$sep$, and
-- > $endfor$
--
-- the resulting page would look like
--
-- > <p>
-- > I like
--- >
--- > fresh fruits, and
--- >
+-- >
+-- > fresh fruits, and
+-- >
-- > fresh vegetables
-- > </p>
--
@@ -129,9 +129,8 @@ module Hakyll.Web.Template
--------------------------------------------------------------------------------
import Control.Monad (liftM)
-import Control.Monad.Error (MonadError (..))
+import Control.Monad.Except (MonadError (..))
import Data.List (intercalate)
-import Data.Monoid (mappend)
import Prelude hiding (id)
diff --git a/src/Hakyll/Web/Template/Context.hs b/src/Hakyll/Web/Template/Context.hs
index 28e2ec5..b6c7994 100644
--- a/src/Hakyll/Web/Template/Context.hs
+++ b/src/Hakyll/Web/Template/Context.hs
@@ -32,18 +32,13 @@ module Hakyll.Web.Template.Context
--------------------------------------------------------------------------------
-import Control.Applicative (Alternative (..), pure, (<$>))
+import Control.Applicative (Alternative (..))
import Control.Monad (msum)
import Data.List (intercalate)
-import qualified Data.Map as M
-import Data.Monoid (Monoid (..))
import Data.Time.Clock (UTCTime (..))
import Data.Time.Format (formatTime)
import qualified Data.Time.Format as TF
import Data.Time.Locale.Compat (TimeLocale, defaultTimeLocale)
-import System.FilePath (splitDirectories, takeBaseName)
-
---------------------------------------------------------------------------------
import Hakyll.Core.Compiler
import Hakyll.Core.Compiler.Internal
import Hakyll.Core.Identifier
@@ -52,6 +47,7 @@ import Hakyll.Core.Metadata
import Hakyll.Core.Provider
import Hakyll.Core.Util.String (needlePrefix, splitAll)
import Hakyll.Web.Html
+import System.FilePath (splitDirectories, takeBaseName)
--------------------------------------------------------------------------------
@@ -291,7 +287,7 @@ getItemUTC :: MonadMetadata m
-> m UTCTime -- ^ Parsed UTCTime
getItemUTC locale id' = do
metadata <- getMetadata id'
- let tryField k fmt = M.lookup k metadata >>= parseTime' fmt
+ let tryField k fmt = lookupString k metadata >>= parseTime' fmt
paths = splitDirectories $ toFilePath id'
maybe empty' return $ msum $
diff --git a/src/Hakyll/Web/Template/Internal.hs b/src/Hakyll/Web/Template/Internal.hs
index 2d9de5e..45db2e4 100644
--- a/src/Hakyll/Web/Template/Internal.hs
+++ b/src/Hakyll/Web/Template/Internal.hs
@@ -12,7 +12,7 @@ module Hakyll.Web.Template.Internal
--------------------------------------------------------------------------------
-import Control.Applicative (pure, (<$), (<$>), (<*>), (<|>))
+import Control.Applicative ((<|>))
import Control.Monad (void)
import Data.Binary (Binary, get, getWord8, put, putWord8)
import Data.Typeable (Typeable)
diff --git a/stack.yaml b/stack.yaml
new file mode 100644
index 0000000..b85cc63
--- /dev/null
+++ b/stack.yaml
@@ -0,0 +1,12 @@
+resolver: lts-5.11
+extra-deps: []
+extra-package-dbs: []
+
+flags:
+ hakyll:
+ previewServer: True
+ watchServer: True
+ checkExternal: True
+
+packages:
+ - '.'
diff --git a/tests/Hakyll/Core/Provider/Metadata/Tests.hs b/tests/Hakyll/Core/Provider/Metadata/Tests.hs
index 1217180..fc609f2 100644
--- a/tests/Hakyll/Core/Provider/Metadata/Tests.hs
+++ b/tests/Hakyll/Core/Provider/Metadata/Tests.hs
@@ -5,14 +5,13 @@ module Hakyll.Core.Provider.Metadata.Tests
--------------------------------------------------------------------------------
+import qualified Data.HashMap.Strict as HMS
+import qualified Data.Text as T
+import qualified Data.Yaml as Yaml
+import Hakyll.Core.Metadata
+import Hakyll.Core.Provider.Metadata
import Test.Framework (Test, testGroup)
import Test.HUnit (Assertion, (@=?))
-import Text.Parsec as P
-import Text.Parsec.String (Parser)
-
-
---------------------------------------------------------------------------------
-import Hakyll.Core.Provider.Metadata
import TestSuite.Util
@@ -22,9 +21,11 @@ tests = testGroup "Hakyll.Core.Provider.Metadata.Tests" $
fromAssertions "page" [testPage01, testPage02]
+
--------------------------------------------------------------------------------
testPage01 :: Assertion
-testPage01 = testParse page ([("foo", "bar")], "qux\n")
+testPage01 =
+ Right (meta [("foo", "bar")], "qux\n") @=? parsePage
"---\n\
\foo: bar\n\
\---\n\
@@ -33,21 +34,21 @@ testPage01 = testParse page ([("foo", "bar")], "qux\n")
--------------------------------------------------------------------------------
testPage02 :: Assertion
-testPage02 = testParse page
- ([("description", descr)], "Hello I am dog\n")
+testPage02 =
+ Right (meta [("description", descr)], "Hello I am dog\n") @=?
+ parsePage
"---\n\
\description: A long description that would look better if it\n\
\ spanned multiple lines and was indented\n\
\---\n\
\Hello I am dog\n"
where
+ descr :: String
descr =
"A long description that would look better if it \
\spanned multiple lines and was indented"
--------------------------------------------------------------------------------
-testParse :: (Eq a, Show a) => Parser a -> a -> String -> Assertion
-testParse parser expected input = case P.parse parser "<inline>" input of
- Left err -> error $ show err
- Right x -> expected @=? x
+meta :: Yaml.ToJSON a => [(String, a)] -> Metadata
+meta pairs = HMS.fromList [(T.pack k, Yaml.toJSON v) | (k, v) <- pairs]
diff --git a/tests/Hakyll/Core/Provider/Tests.hs b/tests/Hakyll/Core/Provider/Tests.hs
index abe5c1d..8a505d2 100644
--- a/tests/Hakyll/Core/Provider/Tests.hs
+++ b/tests/Hakyll/Core/Provider/Tests.hs
@@ -6,14 +6,11 @@ module Hakyll.Core.Provider.Tests
--------------------------------------------------------------------------------
-import qualified Data.Map as M
+import Hakyll.Core.Metadata
+import Hakyll.Core.Provider
import Test.Framework (Test, testGroup)
import Test.Framework.Providers.HUnit (testCase)
import Test.HUnit (Assertion, assert, (@=?))
-
-
---------------------------------------------------------------------------------
-import Hakyll.Core.Provider
import TestSuite.Util
@@ -32,9 +29,9 @@ case01 = do
assert $ resourceExists provider "example.md"
metadata <- resourceMetadata provider "example.md"
- Just "An example" @=? M.lookup "title" metadata
- Just "External data" @=? M.lookup "external" metadata
+ Just "An example" @=? lookupString "title" metadata
+ Just "External data" @=? lookupString "external" metadata
doesntExist <- resourceMetadata provider "doesntexist.md"
- M.empty @=? doesntExist
+ mempty @=? doesntExist
cleanTestEnv
diff --git a/tests/Hakyll/Core/Routes/Tests.hs b/tests/Hakyll/Core/Routes/Tests.hs
index 4f975ae..5a833b0 100644
--- a/tests/Hakyll/Core/Routes/Tests.hs
+++ b/tests/Hakyll/Core/Routes/Tests.hs
@@ -6,15 +6,13 @@ module Hakyll.Core.Routes.Tests
--------------------------------------------------------------------------------
-import qualified Data.Map as M
+import Data.Maybe (fromMaybe)
+import Hakyll.Core.Identifier
+import Hakyll.Core.Metadata
+import Hakyll.Core.Routes
import System.FilePath ((</>))
import Test.Framework (Test, testGroup)
import Test.HUnit (Assertion, (@=?))
-
-
---------------------------------------------------------------------------------
-import Hakyll.Core.Identifier
-import Hakyll.Core.Routes
import TestSuite.Util
@@ -37,7 +35,7 @@ tests = testGroup "Hakyll.Core.Routes.Tests" $ fromAssertions "runRoutes"
"tags/rss/bar"
, testRoutes "food/example.md" (metadataRoute $ \md -> customRoute $ \id' ->
- M.findWithDefault "?" "subblog" md </> toFilePath id')
+ fromMaybe "?" (lookupString "subblog" md) </> toFilePath id')
"example.md"
]
diff --git a/tests/Hakyll/Core/Rules/Tests.hs b/tests/Hakyll/Core/Rules/Tests.hs
index dbd077d..ec81c1c 100644
--- a/tests/Hakyll/Core/Rules/Tests.hs
+++ b/tests/Hakyll/Core/Rules/Tests.hs
@@ -8,22 +8,19 @@ module Hakyll.Core.Rules.Tests
--------------------------------------------------------------------------------
import Data.IORef (IORef, newIORef, readIORef,
writeIORef)
-import qualified Data.Map as M
import qualified Data.Set as S
-import System.FilePath ((</>))
-import Test.Framework (Test, testGroup)
-import Test.HUnit (Assertion, assert, (@=?))
-
-
---------------------------------------------------------------------------------
import Hakyll.Core.Compiler
import Hakyll.Core.File
import Hakyll.Core.Identifier
import Hakyll.Core.Identifier.Pattern
+import Hakyll.Core.Metadata
import Hakyll.Core.Routes
import Hakyll.Core.Rules
import Hakyll.Core.Rules.Internal
import Hakyll.Web.Pandoc
+import System.FilePath ((</>))
+import Test.Framework (Test, testGroup)
+import Test.HUnit (Assertion, assert, (@=?))
import TestSuite.Util
@@ -89,7 +86,7 @@ rules01 ioref = do
compile getResourceString
version "metadataMatch" $
- matchMetadata "*.md" (\md -> M.lookup "subblog" md == Just "food") $ do
+ matchMetadata "*.md" (\md -> lookupString "subblog" md == Just "food") $ do
route $ customRoute $ \id' -> "food" </> toFilePath id'
compile getResourceString
diff --git a/web/examples.markdown b/web/examples.markdown
index 61659f5..a84a9bc 100644
--- a/web/examples.markdown
+++ b/web/examples.markdown
@@ -104,8 +104,6 @@ directly with the default Hakyll site.
[source](https://github.com/rbros/reichertbrothers.com)
- <http://alemedeiros.sdf.org>,
[source](https://github.com/alemedeiros/homepage)
-- <http://blog.ssanj.net/>,
- [source](https://github.com/ssanj/babyloncandle)
- <https://ruudvanasseldonk.com/>,
[source](https://github.com/ruud-v-a/ruudvanasseldonk.com)
- <http://www.haskell.mn>,
@@ -144,6 +142,10 @@ directly with the default Hakyll site.
[source](https://github.com/curry-club-aux/curry-club-augsburg.de)
- <http://ismailmustafa.com/>,
[source](https://github.com/ismailmustafa/ismailmustafa.github.io/tree/hakyll)
+- <http://lettier.github.io/>,
+ [source](https://github.com/lettier/lettier.github.io)
+- <http://jozefg.bitbucket.org/>,
+ [source](https://github.com/jozefg/blog)
## Hakyll 3.X
diff --git a/web/index.markdown b/web/index.markdown
index 06c2780..bde5289 100644
--- a/web/index.markdown
+++ b/web/index.markdown
@@ -31,8 +31,10 @@ and TeX support, including syntax highlighting and other goodies.
# Getting Started
-You can get the latest version from hackage using `cabal install hakyll`. Then,
-you can:
+You can get the latest version from hackage using `cabal install hakyll`, or
+using [stack] by using `stack install hakyll`. Then, you can:
+
+[stack]: http://www.haskellstack.org/
- read the [tutorials](/tutorials.html);
- mail the [google discussion group](http://groups.google.com/group/hakyll);
diff --git a/web/releases.markdown b/web/releases.markdown
index 01bf499..4f136ad 100644
--- a/web/releases.markdown
+++ b/web/releases.markdown
@@ -4,6 +4,14 @@ title: Releases
# Releases
+## Hakyll 4.7.5.2
+
+- Bump pandoc dependency to 1.17 (contribution by Felix Yan)
+- Fix `unixFilter` documentation (contribution by Richard Cook)
+- Bump example posts (contribution by Andrew Barchuk)
+- Add a template compiler that only uses the template body (contribution by
+ Bergi)
+
## Hakyll 4.7.5.1
- Bump pandoc and pandoc-citeproc dependencies to 1.16 and 0.9 respectively
diff --git a/web/site.hs b/web/site.hs
index a1c2a49..bf84f1e 100644
--- a/web/site.hs
+++ b/web/site.hs
@@ -1,14 +1,13 @@
--------------------------------------------------------------------------------
{-# LANGUAGE OverloadedStrings #-}
-import Control.Applicative ((<$>))
-import Control.Arrow (second)
-import Control.Monad (forM_)
-import Data.Char (isDigit)
-import Data.List (isPrefixOf, partition, sortBy)
-import Data.Monoid (mappend)
-import Data.Ord (comparing)
+import Control.Arrow (second)
+import Control.Monad (forM_)
+import Data.Char (isDigit)
+import Data.List (isPrefixOf, sortBy)
+import Data.Monoid ((<>))
+import Data.Ord (comparing)
import Hakyll
-import System.FilePath (dropTrailingPathSeparator, splitPath)
+import System.FilePath (dropTrailingPathSeparator, splitPath)
import Text.Pandoc
@@ -53,18 +52,12 @@ main = hakyllWith config $ do
create ["tutorials.html"] $ do
route idRoute
compile $ do
- tutorials <- loadAll "tutorials/*"
- itemTpl <- loadBody "templates/tutorial-item.html"
- let (series, articles) = partitionTutorials $
- sortBy (comparing itemIdentifier) tutorials
-
- series' <- applyTemplateList itemTpl defaultContext series
- articles' <- applyTemplateList itemTpl defaultContext articles
+ tuts <-
+ sortBy (comparing itemIdentifier) <$> loadAll "tutorials/*"
let tutorialsCtx =
constField "title" "Tutorials" `mappend`
- constField "series" series' `mappend`
- constField "articles" articles' `mappend`
+ listField "tutorials" tutorialCtx (return tuts) `mappend`
defaultContext
makeItem ""
@@ -112,9 +105,27 @@ hackage url
--------------------------------------------------------------------------------
--- | Partition tutorials into tutorial series & other articles
-partitionTutorials :: [Item a] -> ([Item a], [Item a])
-partitionTutorials = partition $ \i ->
- case splitPath (toFilePath $ itemIdentifier i) of
- [_, (x : _)] -> isDigit x
- _ -> False
+data TutorialType = SeriesTutorial | ArticlesTutorial | ExternalTutorial
+ deriving (Eq)
+
+
+--------------------------------------------------------------------------------
+-- | Partition tutorials into tutorial series, other articles, external articles
+tutorialCtx :: Context String
+tutorialCtx =
+ field "isSeries" (isTutorialType SeriesTutorial) <>
+ field "isArticle" (isTutorialType ArticlesTutorial) <>
+ field "isExternal" (isTutorialType ExternalTutorial) <>
+ defaultContext
+ where
+ getTutorialType item = do
+ mbExternal <- getMetadataField (itemIdentifier item) "external"
+ return $ case mbExternal of
+ Just _ -> ExternalTutorial
+ _ -> case splitPath (toFilePath $ itemIdentifier item) of
+ [_, (x : _)] -> if isDigit x then SeriesTutorial else ArticlesTutorial
+ _ -> ArticlesTutorial
+
+ isTutorialType ty0 item = do
+ ty1 <- getTutorialType item
+ if ty0 == ty1 then return "yes" else fail "no"
diff --git a/web/templates/default.html b/web/templates/default.html
index b9fd2ed..6d64eb5 100644
--- a/web/templates/default.html
+++ b/web/templates/default.html
@@ -38,8 +38,8 @@
<!-- Flattr button -->
<h1>Donate</h1>
- <a href="http://flattr.com/thing/291889/Hakyll" target="_blank">
- <img src="http://api.flattr.com/button/flattr-badge-large.png"
+ <a href="https://flattr.com/thing/291889/Hakyll" target="_blank">
+ <img src="https://api.flattr.com/button/flattr-badge-large.png"
alt="Flattr this" title="Flattr this" border="0" /></a>
</div>
diff --git a/web/templates/tutorial-item.html b/web/templates/tutorial-item.html
deleted file mode 100644
index e0d7866..0000000
--- a/web/templates/tutorial-item.html
+++ /dev/null
@@ -1 +0,0 @@
-<li><a href="$url$">$title$</a> by <em>$author$</em></li>
diff --git a/web/templates/tutorials.html b/web/templates/tutorials.html
index 36c808c..cafdacb 100644
--- a/web/templates/tutorials.html
+++ b/web/templates/tutorials.html
@@ -1,9 +1,41 @@
<h1>Tutorials about Hakyll</h1>
-<h2>Tutorial series</h2>
-<ul>$series$</ul>
-<h2>Other articles</h2>
+<h2 id="series">Tutorial series</h2>
+<ul>
+ $for(tutorials)$
+ $if(isSeries)$
+ <li>
+ <a href="$url$">$title$</a>
+ </li>
+ $endif$
+ $endfor$
+</ul>
+<h2 id="articles">Other articles</h2>
<p>In no particular order:</p>
-<ul>$articles$</ul>
+<ul>
+ $for(tutorials)$
+ $if(isArticle)$
+ <li>
+ <a href="$url$">$title$</a> by <em>$author$</em>
+ </li>
+ $endif$
+ $endfor$
+</ul>
+<h2 id="external-articles">External articles</h2>
+<p>
+ These are articles on external blogs. If you wrote a similar article, feel
+ free to <a href="http://jaspervdj.be/contact.html">shoot me an email</a> so
+ I can add it to the list.
+</p>
+<p>In no particular order:</p>
+<ul>
+ $for(tutorials)$
+ $if(isExternal)$
+ <li>
+ <a href="$url$">$title$</a> by <em>$author$</em>
+ </li>
+ $endif$
+ $endfor$
+</ul>
<p>
All these tutorials assume you are using the latest stable version of
Hakyll. If this is not the case, you might want to update using:
@@ -11,3 +43,8 @@
<pre><code>$$ ghc-pkg unregister hakyll
$$ cabal update
$$ cabal install hakyll</code></pre>
+
+<p>
+ Or using stack:
+</p>
+<pre><code>$$ stack install hakyll</code></pre>
diff --git a/web/tutorials/01-installation.markdown b/web/tutorials/01-installation.markdown
index cede105..ce4e30b 100644
--- a/web/tutorials/01-installation.markdown
+++ b/web/tutorials/01-installation.markdown
@@ -6,17 +6,13 @@ author: Jasper Van der Jeugt
Installation
------------
-Installation is provided using [cabal], and some packages are available for
-different distributions.
+Installation is provided via Hackage, and some packages are available for
+different distributions. For installation from source (i.e. via Hackage),
+[stack] is recommended:
- $ cabal install hakyll
+ $ stack install hakyll
-[cabal]: http://www.haskell.org/cabal/
-
-If you have a recent installation of `cabal` and your time is somewhat valuable,
-use:
-
- $ cabal install -j hakyll
+[stack]: http://www.haskellstack.org/
Linux distro packages:
@@ -34,27 +30,21 @@ started:
This creates a folder `my-site` in the current directory, with some example
content and a generic configuration.
-If `hakyll-init` is not found, you should make sure `$HOME/.cabal/bin` is in
-your `$PATH`.
-
-(If you're on OS X you may not have a bin directory in `$HOME/.cabal`. In this
-case, check `$HOME/Library/Haskell/bin` and put it on your path if you find
-`hakyll-init` there. See [here] for more information on installation paths on
-OS X.)
-
-[here]: http://www.haskell.org/haskellwiki/Mac_OS_X_Common_Installation_Paths
+If `hakyll-init` is not found, you should make sure your stack bin path
+(usually `$HOME/.local/bin`) is in your `$PATH`. You can check your stack local
+bin path by running `stack path --local-bin-path`.
The file `site.hs` holds the configuration of your site, as an executable
haskell program. We can compile and run it like this:
$ cd my-site
- $ ghc --make -threaded site.hs
- $ ./site build
+ $ stack build
+ $ stack exec site build
If you installed `hakyll` with a preview server (this is the default), you can
now use
- $ ./site watch
+ $ stack exec site watch
and have a look at your site at
[http://localhost:8000/](http://localhost:8000/).
diff --git a/web/tutorials/02-basics.markdown b/web/tutorials/02-basics.markdown
index 2e224a7..b79d336 100644
--- a/web/tutorials/02-basics.markdown
+++ b/web/tutorials/02-basics.markdown
@@ -7,24 +7,24 @@ Building and cleaning
---------------------
If you followed along with the previous tutorial, you should now have the
-example site up and running. By running `./site build`, you created two
-directories:
+example site up and running. By running `stack exec site build`, you created
+two directories:
- `_site`, with your site as HTML files, ready to be deployed;
- `_cache`, which Hakyll uses internally.
-`./site clean` removes these directories, and `./site rebuild` performs a
-`clean` and then a `build`.
+`stack exec site clean` removes these directories, and `stack exec site
+rebuild` performs a `clean` and then a `build`.
-In general, you want to use `./site build` when you just made changes to the
-contents of your website. If you made important changes to `site.hs`, you need
-to recompile `site.hs` followed by a rebuild:
+In general, you want to use `stack exec site build` when you just made changes
+to the contents of your website. If you made changes to `site.hs`, you need to
+recompile `site.hs` followed by a rebuild:
- ghc --make site.hs
- ./site rebuild
+ stack build
+ stack exec site rebuild
-At this point, feel free to change some files, `./site build` and see what
-happens!
+At this point, feel free to change some files, `stack exec site build` and see
+what happens!
Pages and metadata
------------------
diff --git a/web/tutorials/04-compilers.markdown b/web/tutorials/04-compilers.markdown
index d283e9a..a9423dc 100644
--- a/web/tutorials/04-compilers.markdown
+++ b/web/tutorials/04-compilers.markdown
@@ -1,5 +1,5 @@
---
-title: More on compilers: load, and templates
+title: 'More on compilers: load, and templates'
author: Jasper Van der Jeugt
---
@@ -115,8 +115,10 @@ you to use:
- `$path$` for the original filepath of the page;
- `$foo$` where foo is specified in the metadata.
-`$date$` is not provided by default, you can see how we add it in the definition
-of `postCtx` in `site.hs`:
+`$date$` is not provided by default. In the scaffold, we use the convenience
+context function `dateField`, which will parse an `Item`'s filename to check if
+it begins with a date. You can see how we add it in the definition of `postCtx`
+in `site.hs`:
```haskell
postCtx :: Context String
diff --git a/web/tutorials/external-add-tags-to-your-hakyll-blog.md b/web/tutorials/external-add-tags-to-your-hakyll-blog.md
new file mode 100644
index 0000000..188e2ea
--- /dev/null
+++ b/web/tutorials/external-add-tags-to-your-hakyll-blog.md
@@ -0,0 +1,6 @@
+---
+title: Add tags to your Hakyll blog
+author: Javran Cheng
+url: https://javran.github.io/posts/2014-03-01-add-tags-to-your-hakyll-blog.html
+external: true
+---
diff --git a/web/tutorials/external-clean-urls-with-hakyll.md b/web/tutorials/external-clean-urls-with-hakyll.md
new file mode 100644
index 0000000..127a192
--- /dev/null
+++ b/web/tutorials/external-clean-urls-with-hakyll.md
@@ -0,0 +1,6 @@
+---
+title: Clean URLs with Hakyll
+author: Rohan Jain
+url: http://www.rohanjain.in/hakyll-clean-urls/
+external: true
+---
diff --git a/web/tutorials/external-deploying-with-widely.md b/web/tutorials/external-deploying-with-widely.md
new file mode 100644
index 0000000..635ce09
--- /dev/null
+++ b/web/tutorials/external-deploying-with-widely.md
@@ -0,0 +1,6 @@
+---
+title: Deploying with Widely
+author: Kyle Marek-Spartz
+url: http://kyle.marek-spartz.org/posts/2013-12-09-widely-and-hakyll.html
+external: true
+---
diff --git a/web/tutorials/external-functionfield.md b/web/tutorials/external-functionfield.md
new file mode 100644
index 0000000..91df254
--- /dev/null
+++ b/web/tutorials/external-functionfield.md
@@ -0,0 +1,6 @@
+---
+title: Hakyll's functionField
+author: Beerend Lauwers
+url: http://beerendlauwers.be/posts/2015-09-21-hakylls-functionfield.html
+external: true
+---
diff --git a/web/tutorials/external-inlining-and-compressing-css.md b/web/tutorials/external-inlining-and-compressing-css.md
new file mode 100644
index 0000000..30beca5
--- /dev/null
+++ b/web/tutorials/external-inlining-and-compressing-css.md
@@ -0,0 +1,6 @@
+---
+title: Inlining and compressing CSS
+author: Kyle Marek-Spartz
+url: http://kyle.marek-spartz.org/posts/2014-12-09-hakyll-css-template-compiler.html
+external: true
+---
diff --git a/web/tutorials/faq.markdown b/web/tutorials/faq.markdown
index 87c1a92..e978c8d 100644
--- a/web/tutorials/faq.markdown
+++ b/web/tutorials/faq.markdown
@@ -27,6 +27,18 @@ You should also add this to your `.profile`, or whatever config file you use.
On Windows, running `chcp 65001` before running your Hakyll executable has been
reported to work.
+Alternatively, you can specify this in your `site.hs`:
+
+```haskell
+import qualified GHC.IO.Encoding as E
+
+main :: IO ()
+main = do
+ E.setLocaleEncoding E.utf8
+ hakyll $ do
+ ...
+```
+
## "File name does not match module name" on Mac OS
Hakyll.hs:1:1:
diff --git a/web/tutorials/github-pages-tutorial.md b/web/tutorials/github-pages-tutorial.md
new file mode 100644
index 0000000..77d370f
--- /dev/null
+++ b/web/tutorials/github-pages-tutorial.md
@@ -0,0 +1,174 @@
+---
+title: Using Hakyll with GitHub Pages
+author: Erik Stevenson
+---
+
+## Introduction
+
+[GitHub Pages](http://pages.github.com) has become a popular static website hosting solution due to its simplicity. Simply push a couple files to a repository and it's off to the races.
+
+Working with Hakyll on a GitHub Pages-hosted website is complicated slightly due to Hakyll outputting files to a ```_site``` subdirectory, our desire to have the source code as well as the compiled site stored in a single repository, and our desire to automate it.
+
+This guide will walkthrough the creation and setup of a GitHub site that has two independent branches.
+
+1. ```master``` - This is where your site lives. It's what you see when you go to ```https://<your repo>.github.io```. This branch *needs* to be called master.
+2. ```develop``` - This is where your website's source is. That's all your Haskell code, your posts and templates, etc, and it's where you do work from. This name was chosen arbitrarily and can be freely substituted for any name of your choosing.
+
+When you're finished, you will be able to, with one command, refresh your website's contents and send any changes to your GitHub Page.
+
+In the interest of keeping this guide as simple as possible, I'll be making a few assumptions.
+
+1. Haskell is being used with [Stack](http://docs.haskellstack.org/en/stable/README/).
+2. Creating a user/organization site (vice a project site).
+3. You haven't changed Hakyll's default output directory of '_site/'.
+
+These instructions should be easy to adapt for any situation though.
+
+## GitHub Setup
+
+1. If required, create a new repository for your blog.
+2. If required, create a ```master``` branch.
+2. If applicable/desired, create/add to your repository any files that your site needs that will not be produced by your Hakyll project. For example, ```CNAME``` as outlined [here](https://help.github.com/articles/setting-up-your-pages-site-repository/).
+3. Create a ```.gitignore``` file with at a minimum, the following entries:
+
+```
+_cache/
+_site/
+.stack-work/
+```
+
+## Local setup
+
+1. If required, create a new Hakyll project. If you're a stack user, there is a Hakyll template available that makes this step easy.
+
+```stack new myblog hakyll-template```
+
+2. Create a ```.gitignore``` file in your blog's directory with at a minimum, the same directories listed as in the GitHub repository.
+3. Use the following git commands to setup your local repository.
+
+```
+git init
+# create new branch called develop and switch to it.
+git checkout -b develop
+# track all the source files for our blog.
+git add .
+# make our first commit
+git commit -m "initial commit."
+# and add the GitHub repository as a remote.
+git remote add origin <URL to your GitHub pages repository>
+```
+
+## Deployment
+
+So everything's all setup and we're ready to deploy.
+
+> **Note:** Performing the following commands from your ```develop``` branch is recommended since you will end up back in that branch at the end.
+
+Temporarily save any uncommitted changes that may exist in the current branch.
+
+```
+git stash
+```
+
+Ensure we are in the correct branch.
+
+```
+git checkout develop
+```
+
+Get a clean build of our site.
+
+```
+stack exec myblog clean
+stack exec myblog build
+```
+
+Update the local list of remote branches to ensure we're able to checkout the branch we want in the next step.
+
+```
+git fetch -all
+```
+
+Switch to the `master` branch.
+
+> **Note:** Checking out this branch does not overwrite the files that Hakyll just produced because we have '_site' listed in both .gitignore files.
+
+```
+git checkout -b master --track origin/master
+```
+
+Next, copy the freshly made contents of '_site' over the old ones.
+
+> **Note:** Deleting a file from your site's source will not remove it from your `master` repository if it has already been published. An alternative to `cp` is discussed at the end of this guide.
+
+```
+cp -a _site/. .
+```
+
+Commit our changes.
+
+```
+git add -A
+git commit -m "Publish."
+```
+
+And send them to GitHub.
+
+```
+git push origin master:master
+```
+
+Final clean up and return to the original state.
+
+```
+git checkout develop
+git branch -D master
+git stash pop
+```
+
+## Putting it all together
+
+Below is a complete listing of all the commands used to automate deployment to Github Pages. A `deployCommand` can be set as part of Hakyll's configuration options. More information and an example is provided [here](https://jaspervdj.be/hakyll/reference/Hakyll-Core-Configuration.html).
+
+```
+# Temporarily store uncommited changes
+git stash
+
+# Verify correct branch
+git checkout develop
+
+# Build new files
+stack exec myblog clean
+stack exec myblog build
+
+# Get previous files
+git fetch -all
+git checkout -b master --track origin/master
+
+# Overwrite existing files with new files
+cp -a _site/. .
+
+# Commit
+git add -A
+git commit -m "Publish."
+
+# Push
+git push origin master:master
+
+# Restoration
+git checkout develop
+git branch -D master
+git stash pop
+```
+
+*And that's it.*
+
+## Removing old files with `rsync`
+
+Earlier it was mentioned a flaw is that deleted files will persist in the published site until deleted manually. This is easily overcome by using `rsync` instead of `cp`.
+
+```
+rsync -a --filter='P _site/' --delete-excluded _site/ .
+```
+
+The only drawback this approach has is the requirement that *every* file in your site "go through" Hakyll. Fortunately, in many cases this is not an issue.