hakyll 4.15.1.0 → 4.15.1.1
raw patch · 7 files changed
+359/−124 lines, 7 filesdep ~aesondep ~fsnotifydep ~optparse-applicativePVP ok
version bump matches the API change (PVP)
Dependency ranges changed: aeson, fsnotify, optparse-applicative, pandoc, resourcet, template-haskell, text, vector
API changes (from Hackage documentation)
Files
- CHANGELOG.md +10/−1
- hakyll.cabal +3/−3
- lib/Hakyll/Core/Identifier.hs +97/−4
- lib/Hakyll/Core/Routes.hs +232/−78
- lib/Hakyll/Core/Runtime.hs +5/−2
- lib/Hakyll/Web/Pandoc.hs +12/−6
- web/site.hs +0/−30
CHANGELOG.md view
@@ -4,6 +4,16 @@ # Releases +## Hakyll 4.15.1.1 (2022-01-20)++- Extend the documentation for `Hakyll.Core.Identifier` (contribution by+ malteneuss)+- Fix yet another regression caused by new dependency checking code+ (contribution by Laurent P. René de Cotret)+- Bump `pandoc` upper bound to allow 2.17 (contribution by Alexander Batischev)+- Website now points to Hackage rather than its own (often outdated) version of+ the docs (contribution by Jasper Van der Jeugt)+ ## Hakyll 4.15.1.0 (2021-10-25) - Add `Hakyll.Web.Pandoc.Biblio` functions `readPandocBiblios` and@@ -19,7 +29,6 @@ Batischev) - Bump `pandoc` upper bound to allow 2.15 (contribution by Alexander Batischev) - Bump `aeson` bounds to allow 2.0 (contribution by Alexander Batischev)- ## Hakyll 4.15.0.1 (2021-10-02)
hakyll.cabal view
@@ -1,5 +1,5 @@ Name: hakyll-Version: 4.15.1.0+Version: 4.15.1.1 Synopsis: A static website compiler library Description:@@ -246,7 +246,7 @@ Other-Modules: Hakyll.Web.Pandoc.Binary Build-Depends:- pandoc >= 2.11 && < 2.16+ pandoc >= 2.11 && < 2.18 Cpp-options: -DUSE_PANDOC @@ -344,4 +344,4 @@ base >= 4 && < 5, directory >= 1.0 && < 1.4, filepath >= 1.0 && < 1.5,- pandoc >= 2.11 && < 2.16+ pandoc >= 2.11 && < 2.18
lib/Hakyll/Core/Identifier.hs view
@@ -1,12 +1,16 @@ ----------------------------------------------------------------------------------- | An identifier is a type used to uniquely identify an item. An identifier is--- conceptually similar to a file path. Examples of identifiers are:+-- | An identifier is a type used to uniquely name an item. An identifier+-- is similar to a file path, but can contain additional details (e.g. +-- item's version). Examples of identifiers are: -- -- * @posts/foo.markdown@ -- -- * @index@ -- -- * @error/404@+--+-- See 'Identifier' for details.+ {-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} module Hakyll.Core.Identifier@@ -32,6 +36,70 @@ --------------------------------------------------------------------------------+{- | A key data type to identify a compiled 'Hakyll.Core.Item.Item' in the 'Hakyll.Core.Store.Store'.+Conceptually, it's a combination of a file path and a version name.+The version is used only when a file is+compiled within a rule using the 'version' wrapper function+(the same source file+can be compiled into several items in the store, so the version exists to distinguish+them).+Use functions like 'fromFilePath', 'setVersion', 'Hakyll.Core.Metadata.getMatches' to build an 'Identifier'.++=== __Usage Examples__+Normally, compiled items are saved to the store by 'Hakyll.Core.Rules.Rules' with an automatic, implicit identifier+and loaded from the store by the user in another rule with a manual, explicit identifier.++__Identifiers when using match__.+Using 'Hakyll.Core.Rules.match' builds an implicit identifier that corresponds to the expanded, relative path+of the source file on disk (relative to the project directory configured+with 'Hakyll.Core.Configuration.providerDirectory'):++@+-- e.g. file on disk: 'posts\/hakyll.md'+match "posts/*" $ do -- saved with implicit identifier 'posts\/hakyll.md'+ compile pandocCompiler++match "about/*" $ do+ compile $ do+ compiledPost <- load (fromFilePath "posts/hakyll.md") -- load with explicit identifier+ ...+@+Normally, the identifier is only explicitly created to pass to one of the 'Hakyll.Core.Compiler.load' functions.++__Identifiers when using create__.+Using 'Hakyll.Core.Rules.create' (thereby inventing a file path with no underlying file on disk)+builds an implicit identifier that corresponds to the invented file path:++@+create ["index.html"] $ do -- saved with implicit identifier 'index.html'+ compile $ makeItem ("Hello world" :: String)++match "about/*" $ do+ compile $ do+ compiledIndex <- load (fromFilePath "index.html") -- load with an explicit identifier+ ...+@++__Identifiers when using versions__.+With 'Hakyll.Core.Rules.version' the same file can be compiled into several items in the store.+A version name is needed to distinguish them:++@+-- e.g. file on disk: 'posts\/hakyll.md'+match "posts/*" $ do -- saved with implicit identifier ('posts\/hakyll.md', no-version)+ compile pandocCompiler++match "posts/*" $ version "raw" $ do -- saved with implicit identifier ('posts\/hakyll.md', version 'raw')+ compile getResourceBody++match "about/*" $ do+ compile $ do+ compiledPost <- load (fromFilePath "posts/hakyll.md") -- load no-version version+ rawPost <- load . setVersion (Just "raw") $ fromFilePath "posts/hakyll.md" -- load version 'raw'+ ...+@+Use 'setVersion' to set (or replace) the version of an identifier like @fromFilePath "posts/hakyll.md"@.+-} data Identifier = Identifier { identifierVersion :: Maybe String , identifierPath :: String@@ -62,17 +130,42 @@ ----------------------------------------------------------------------------------- | Parse an identifier from a string+{- | Parse an identifier from a file path string. For example, ++@+-- e.g. file on disk: 'posts\/hakyll.md'+match "posts/*" $ do -- saved with implicit identifier 'posts\/hakyll.md'+ compile pandocCompiler++match "about/*" $ do+ compile $ do+ compiledPost <- load (fromFilePath "posts/hakyll.md") -- load with explicit identifier+ ...+@+-} fromFilePath :: FilePath -> Identifier fromFilePath = Identifier Nothing . normalise ----------------------------------------------------------------------------------- | Convert an identifier to a relative 'FilePath'+-- | Convert an identifier back to a relative 'FilePath'. toFilePath :: Identifier -> FilePath toFilePath = normalise . identifierPath --------------------------------------------------------------------------------+{- | Set or override the version of an identifier in order to specify which version of an 'Hakyll.Core.Item.Item' +to 'Hakyll.Core.Compiler.load' from the 'Hakyll.Core.Store.Store'. For example,++@+match "posts/*" $ version "raw" $ do -- saved with implicit identifier ('posts\/hakyll.md', version 'raw')+ compile getResourceBody++match "about/*" $ do+ compile $ do+ rawPost <- load . setVersion (Just "raw") $ fromFilePath "posts/hakyll.md" -- load version 'raw'+ ...+@+-} setVersion :: Maybe String -> Identifier -> Identifier setVersion v i = i {identifierVersion = v}
lib/Hakyll/Core/Routes.hs view
@@ -1,35 +1,49 @@ ----------------------------------------------------------------------------------- | Once a target is compiled, the user usually wants to save it to the disk.--- This is where the 'Routes' type comes in; it determines where a certain--- target should be written.------ Suppose we have an item @foo\/bar.markdown@. We can render this to--- @foo\/bar.html@ using:------ > route "foo/bar.markdown" (setExtension ".html")------ If we do not want to change the extension, we can use 'idRoute', the simplest--- route available:------ > route "foo/bar.markdown" idRoute------ That will route @foo\/bar.markdown@ to @foo\/bar.markdown@.------ Note that the extension says nothing about the content! If you set the--- extension to @.html@, it is your own responsibility to ensure that the--- content is indeed HTML.------ Finally, some special cases:------ * If there is no route for an item, this item will not be routed, so it will--- not appear in your site directory.------ * If an item matches multiple routes, the first rule will be chosen.+{- | 'Routes' is part of the 'Hakyll.Core.Rules.Rules' processing pipeline.+It determines if and where the compilation result of the underlying+'Hakyll.Core.Item.Item' being processed is written out to+(relative to the destination directory as configured in+'Hakyll.Core.Configuration.destinationDirectory').++* __If there is no route for an item, the compiled item won't be written+out to a file__ and so won't appear in the destination (site) directory.++* If an item matches multiple routes, the first route will be chosen.++__Examples__++Suppose we have a markdown file @posts\/hakyll.md@. We can route its+compilation result to @posts\/hakyll.html@ using 'setExtension':++> -- file on disk: '<project-directory>/posts/hakyll.md'+> match "posts/*" $ do+> -- compilation result is written to '<destination-directory>/posts/hakyll.html'+> route (setExtension "html")+> compile pandocCompiler+Hint: You can configure the destination directory with+'Hakyll.Core.Configuration.destinationDirectory'.++If we do not want to change the extension, we can replace 'setExtension' with+'idRoute' (the simplest route available):++> -- compilation result is written to '<destination-directory>/posts/hakyll.md'+> route idRoute++That will route the file @posts\/hakyll.md@ from the project directory to+@posts\/hakyll.md@ in the destination directory.++Note: __The extension of the destination filepath says nothing about the+content!__ If you set the extension to @.html@, you have to ensure that the+compilation result is indeed HTML (for example with the+'Hakyll.Web.Pandoc.pandocCompiler' to transform Markdown to HTML).++Take a look at the built-in routes here for detailed usage examples.+-} {-# LANGUAGE CPP #-} {-# LANGUAGE Rank2Types #-} module Hakyll.Core.Routes- ( UsedMetadata- , Routes+ ( Routes+ , UsedMetadata , runRoutes , idRoute , setExtension@@ -109,30 +123,60 @@ ----------------------------------------------------------------------------------- | A route that uses the identifier as filepath. For example, the target with--- ID @foo\/bar@ will be written to the file @foo\/bar@.+{- | An "identity" route that interprets the identifier (of the item being+processed) as the destination filepath. This identifier is normally the+filepath of the source file being processed.+See 'Hakyll.Core.Identifier.Identifier' for details.++=== __Examples__+__Route when using match__++> -- e.g. file on disk: '<project-directory>/posts/hakyll.md'+>+> -- 'hakyll.md' source file implicitly gets filepath as identifier:+> -- 'posts/hakyll.md'+> match "posts/*" $ do+>+> -- compilation result is written to '<destination-directory>/posts/hakyll.md'+> route idRoute+>+> compile getResourceBody+-} idRoute :: Routes idRoute = customRoute toFilePath ----------------------------------------------------------------------------------- | Set (or replace) the extension of a route.------ Example:------ > runRoutes (setExtension "html") "foo/bar"------ Result:------ > Just "foo/bar.html"------ Example:------ > runRoutes (setExtension "html") "posts/the-art-of-trolling.markdown"------ Result:------ > Just "posts/the-art-of-trolling.html"+{- | Create a route like 'idRoute' that interprets the identifier (of the item+being processed) as the destination filepath but also sets (or replaces) the+extension suffix of that path. This identifier is normally the filepath of the+source file being processed.+See 'Hakyll.Core.Identifier.Identifier' for details.++=== __Examples__+__Route with an existing extension__++> -- e.g. file on disk: '<project-directory>/posts/hakyll.md'+>+> -- 'hakyll.md' source file implicitly gets filepath as identifier:+> -- 'posts/hakyll.md'+> match "posts/*" $ do+>+> -- compilation result is written to '<destination-directory>/posts/hakyll.html'+> route (setExtension "html")+>+> compile pandocCompiler++__Route without an existing extension__++> -- implicitly gets identifier: 'about'+> create ["about"] $ do+>+> -- compilation result is written to '<destination-directory>/about.html'+> route (setExtension "html")+>+> compile $ makeItem ("Hello world" :: String)+-} setExtension :: String -> Routes setExtension extension = customRoute $ (`replaceExtension` extension) . toFilePath@@ -147,31 +191,94 @@ ----------------------------------------------------------------------------------- | Create a custom route. This should almost always be used with--- 'matchRoute'-customRoute :: (Identifier -> FilePath) -> Routes+{- | Create a route where the destination filepath is built with the given+construction function. The provided identifier for that function is normally the+filepath of the source file being processed.+See 'Hakyll.Core.Identifier.Identifier' for details.++=== __Examples__+__Route that appends a custom extension__++> -- e.g. file on disk: '<project-directory>/posts/hakyll.md'+>+> -- 'hakyll.md' source file implicitly gets filepath as identifier:+> -- 'posts/hakyll.md'+> match "posts/*" $ do+>+> -- compilation result is written to '<destination-directory>/posts/hakyll.md.html'+> route $ customRoute ((<> ".html") . toFilePath)+>+> compile pandocCompiler+Note that the last part of the destination filepath becomes @.md.html@+-}+customRoute :: (Identifier -> FilePath) -- ^ Destination filepath construction function+ -> Routes -- ^ Resulting route customRoute f = Routes $ const $ \id' -> return (Just (f id'), False) ----------------------------------------------------------------------------------- | A route that always gives the same result. Obviously, you should only use--- this for a single compilation rule.+{- | Create a route that writes the compiled item to the given destination+filepath (ignoring any identifier or other data about the item being processed).+Warning: you should __use a specific destination path only for a single file in+a single compilation rule__. Otherwise it's unclear which of the contents should+be written to that route.++=== __Examples__+__Route to a specific filepath__++> -- implicitly gets identifier: 'main' (ignored on next line)+> create ["main"] $ do+>+> -- compilation result is written to '<destination-directory>/index.html'+> route $ constRoute "index.html"+>+> compile $ makeItem ("<h1>Hello World</h1>" :: String)+-} constRoute :: FilePath -> Routes constRoute = customRoute . const ----------------------------------------------------------------------------------- | Create a gsub route------ Example:------ > runRoutes (gsubRoute "rss/" (const "")) "tags/rss/bar.xml"------ Result:------ > Just "tags/bar.xml"-gsubRoute :: String -- ^ Pattern- -> (String -> String) -- ^ Replacement+{- | Create a "substituting" route that searches for substrings (in the+underlying identifier) that match the given pattern and transforms them+according to the given replacement function.+The identifier here is that of the underlying item being processed and is+interpreted as an destination filepath. It's normally the filepath of the+source file being processed.+See 'Hakyll.Core.Identifier.Identifier' for details.++Hint: The name "gsub" comes from a similar function in +[R](https://www.r-project.org) and can be read as "globally substituting" +(globally in the Unix sense of repeated, not just once).++=== __Examples__+__Route that replaces part of the filepath__++> -- e.g. file on disk: '<project-directory>/posts/hakyll.md'+>+> -- 'hakyll.md' source file implicitly gets filepath as identifier:+> -- 'posts/hakyll.md'+> match "posts/*" $ do+>+> -- compilation result is written to '<destination-directory>/haskell/hakyll.md'+> route $ gsubRoute "posts/" (const "haskell/")+>+> compile getResourceBody+Note that "posts\/" is replaced with "haskell\/" in the destination filepath.++__Route that removes part of the filepath__++> -- implicitly gets identifier: 'tags/rss/bar.xml'+> create ["tags/rss/bar.xml"] $ do+>+> -- compilation result is written to '<destination-directory>/tags/bar.xml'+> route $ gsubRoute "rss/" (const "")+>+> compile ...+Note that "rss\/" is removed from the destination filepath.+-}+gsubRoute :: String -- ^ Pattern to repeatedly match against in the underlying identifier+ -> (String -> String) -- ^ Replacement function to apply to the matched substrings -> Routes -- ^ Resulting route gsubRoute pattern replacement = customRoute $ normalise . replaceAll pattern (replacement . removeWinPathSeparator) . removeWinPathSeparator . toFilePath@@ -182,27 +289,74 @@ ----------------------------------------------------------------------------------- | Get access to the metadata in order to determine the route-metadataRoute :: (Metadata -> Routes) -> Routes+{- | Wrapper function around other route construction functions to get+access to the metadata (of the underlying item being processed) and use that for+the destination filepath construction.+Warning: you have to __ensure that the accessed metadata fields actually+exists__.++=== __Examples__+__Route that uses a custom slug markdown metadata field__++To create a search engine optimized yet human-readable url, we can introduce+a [slug](https://en.wikipedia.org/wiki/Clean_URL#Slug) metadata field to our+files, e.g. like in the following Markdown file: 'posts\/hakyll.md'++> ---+> title: Hakyll Post+> slug: awesome-post+> ...+> ---+> In this blog post we learn about Hakyll ...++Then we can construct a route whose destination filepath is based on that field:++> match "posts/*" $ do+>+> -- compilation result is written to '<destination-directory>/awesome-post.html'+> route $ metadataRoute $ \meta ->+> constRoute $ fromJust (lookupString "slug" meta) <> ".html"+>+> compile pandocCompiler+Note how we wrap 'metadataRoute' around the 'constRoute' function and how the+slug is looked up from the markdown field to construct the destination filepath.+You can use helper functions like 'Hakyll.Core.Metadata.lookupString' to access+a specific metadata field.+-}+metadataRoute :: (Metadata -> Routes) -- ^ Wrapped route construction function+ -> Routes -- ^ Resulting route metadataRoute f = Routes $ \r i -> do metadata <- resourceMetadata (routesProvider r) (routesUnderlying r) unRoutes (f metadata) r i ----------------------------------------------------------------------------------- | Compose routes so that @f \`composeRoutes\` g@ is more or less equivalent--- with @g . f@.------ Example:------ > let routes = gsubRoute "rss/" (const "") `composeRoutes` setExtension "xml"--- > in runRoutes routes "tags/rss/bar"------ Result:------ > Just "tags/bar.xml"------ If the first route given fails, Hakyll will not apply the second route.+{- | Compose two routes where __the first route is applied before the second__.+So @f \`composeRoutes\` g@ is more or less equivalent with @g . f@.++Warning: If the first route fails (e.g. when using 'matchRoute'), Hakyll will+not apply the second route (if you need Hakyll to try the second route,+use '<>' on 'Routes' instead).++=== __Examples__+__Route that applies two transformations__++> -- e.g. file on disk: '<project-directory>/posts/hakyll.md'+>+> -- 'hakyll.md' source file implicitly gets filepath as identifier:+> -- 'posts/hakyll.md'+> match "posts/*" $ do+>+> -- compilation result is written to '<destination-directory>/hakyll.html'+> route $ gsubRoute "posts/" (const "") `composeRoutes` setExtension "html"+>+> compile pandocCompiler+The identifier here is that of the underlying item being processed and is+interpreted as an destination filepath.+See 'Hakyll.Core.Identifier.Identifier' for details.+Note how we first remove the "posts\/" substring from that destination filepath+ with 'gsubRoute' and then replace the extension with 'setExtension'.+-} composeRoutes :: Routes -- ^ First route to apply -> Routes -- ^ Second route to apply -> Routes -- ^ Resulting route
lib/Hakyll/Core/Runtime.hs view
@@ -334,7 +334,10 @@ -- Progress has been made if at least one of the -- requirements can move forwards at the next pass- let progress | length deps < length reqs = Progressed- | otherwise = Idled+ -- In some cases, dependencies have been processed in parallel in which case `deps` + -- can be empty, and we can progress to the next stage. See issue #907+ let progress | null deps = Progressed+ | deps == reqs = Idled+ | otherwise = Progressed return progress
lib/Hakyll/Web/Pandoc.hs view
@@ -109,14 +109,14 @@ -------------------------------------------------------------------------------- -- | An extension of `renderPandocWith`, which allows you to specify a custom -- Pandoc transformation on the input `Item`.--- Useful if you want to do your own transformations before running +-- Useful if you want to do your own transformations before running -- custom Pandoc transformations, e.g. using a `funcField` to transform raw content. renderPandocWithTransform :: ReaderOptions -> WriterOptions -> (Pandoc -> Pandoc) -> Item String -> Compiler (Item String)-renderPandocWithTransform ropt wopt f = - renderPandocWithTransformM ropt wopt (return . f) +renderPandocWithTransform ropt wopt f =+ renderPandocWithTransformM ropt wopt (return . f) --------------------------------------------------------------------------------@@ -128,8 +128,8 @@ -> (Pandoc -> Compiler Pandoc) -> Item String -> Compiler (Item String)-renderPandocWithTransformM ropt wopt f i = - writePandocWith wopt <$> (traverse f =<< readPandocWith ropt i) +renderPandocWithTransformM ropt wopt f i =+ writePandocWith wopt <$> (traverse f =<< readPandocWith ropt i) --------------------------------------------------------------------------------@@ -166,7 +166,7 @@ pandocCompilerWithTransformM :: ReaderOptions -> WriterOptions -> (Pandoc -> Compiler Pandoc) -> Compiler (Item String)-pandocCompilerWithTransformM ropt wopt f = +pandocCompilerWithTransformM ropt wopt f = getResourceBody >>= renderPandocWithTransformM ropt wopt f @@ -190,4 +190,10 @@ , -- We want to have hightlighting by default, to be compatible with earlier -- Hakyll releases writerHighlightStyle = Just pygments+ , -- Do not word-wrap produced HTML, and do not undo any word-wrapping+ -- that's already present in the markup. This is how Pandoc operated+ -- prior to 2.17, but the behaviour was changed for consistency with+ -- other Pandoc writers. We retain the old behaviour because it spares us+ -- the trouble of updating our golden tests.+ writerWrapText = WrapPreserve }
web/site.hs view
@@ -26,16 +26,6 @@ route idRoute compile copyFileCompiler - -- Haddock stuff- match "reference/**.html" $ do- route idRoute- compile $ fmap (withUrls hackage) <$> getResourceString-- -- Haddock stuff- match ("reference/**" `mappend` complement "**.html") $ do- route idRoute- compile copyFileCompiler- -- Pages match "*.markdown" $ do route $ setExtension "html"@@ -84,26 +74,6 @@ \_site/* \ \jaspervdj@jaspervdj.be:jaspervdj.be/hakyll/" }-------------------------------------------------------------------------------------- | Turns------ > /usr/share/doc/ghc/html/libraries/base-4.6.0.0/Data-String.html------ into------ > http://hackage.haskell.org/packages/archive/base/4.6.0.0/doc/html/Data-String.html-hackage :: String -> String-hackage url- | "/usr" `isPrefixOf` url =- "http://hackage.haskell.org/packages/archive/" ++- packageName ++ "/" ++ version' ++ "/doc/html/" ++ baseName- | otherwise = url- where- (packageName, version') = second (drop 1) $ break (== '-') package- (baseName : package : _) = map dropTrailingPathSeparator $- reverse $ splitPath url --------------------------------------------------------------------------------