diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,5 +1,8 @@
 # Changelog for sitepipe-shake
 
+## 0.2.0.0
+- Allow IO in `makePandocReader` and `makePandocWriter` to allow use of complex filters, etc.
+
 ## 0.1.1.0
 - Add gfm markdown options on writer extensions
     - this should allow pandoc to auto-generate id's on header tags
diff --git a/slick.cabal b/slick.cabal
--- a/slick.cabal
+++ b/slick.cabal
@@ -1,11 +1,13 @@
--- This file has been generated from package.yaml by hpack version 0.28.2.
+cabal-version: 1.12
+
+-- This file has been generated from package.yaml by hpack version 0.31.0.
 --
 -- see: https://github.com/sol/hpack
 --
--- hash: 75e40e0c9806b58cbbc105f1d144866a4fac557689b2b4a7b7ba48a065a3dfca
+-- hash: 3cc544477064cc4c1a83dee37ce64b9559692a238162f8704a62c1fb10265bed
 
 name:           slick
-version:        0.1.1.0
+version:        0.2.0.0
 description:    Please see the README on GitHub at <https://github.com/ChrisPenner/slick#readme>
 homepage:       https://github.com/ChrisPenner/slick#readme
 bug-reports:    https://github.com/ChrisPenner/slick/issues
@@ -15,10 +17,9 @@
 license:        BSD3
 license-file:   LICENSE
 build-type:     Simple
-cabal-version:  >= 1.10
 extra-source-files:
-    ChangeLog.md
     README.md
+    ChangeLog.md
 
 source-repository head
   type: git
diff --git a/src/Slick.hs b/src/Slick.hs
--- a/src/Slick.hs
+++ b/src/Slick.hs
@@ -7,12 +7,16 @@
     compileTemplate'
 
   -- ** Pandoc
+  , PandocReader
+  , PandocWriter
   , markdownToHTML
   , markdownToHTML'
   , makePandocReader
   , makePandocReader'
   , loadUsing
   , loadUsing'
+  , markdownOptions
+  , html5Options
 
   -- ** Aeson
   , convert
@@ -25,9 +29,10 @@
 
   -- ** Re-exported
   , module Text.Mustache
-  ) where
+  )
+where
 
-import Slick.Caching
-import Slick.Mustache
-import Slick.Pandoc
-import Text.Mustache hiding ((~>))
+import           Slick.Caching
+import           Slick.Mustache
+import           Slick.Pandoc
+import           Text.Mustache                     hiding ( (~>) )
diff --git a/src/Slick/Caching.hs b/src/Slick/Caching.hs
--- a/src/Slick/Caching.hs
+++ b/src/Slick/Caching.hs
@@ -8,14 +8,16 @@
   , simpleJsonCache'
   , jsonCache
   , jsonCache'
-  ) where
+  )
+where
 
-import Data.Aeson as A
-import Data.ByteString.Lazy
-import Development.Shake hiding (Resource)
-import Development.Shake.Classes
-import GHC.Generics (Generic)
+import           Data.Aeson                    as A
+import           Data.ByteString.Lazy
+import           Development.Shake                 hiding ( Resource )
+import           Development.Shake.Classes
+import           GHC.Generics                             ( Generic )
 
+
 newtype CacheQuery q =
   CacheQuery q
   deriving (Show, Eq, Generic, Binary, NFData, Hashable)
@@ -38,29 +40,26 @@
 -- > postCache <- jsonCache $ \(PostFilePath path) ->
 -- >   readFile' path >>= markdownToHTML . Text.pack
 -- > -- Now use postCache inside an Action to load your post with caching!
-jsonCache ::
-     ShakeValue q
-  => (q -> Action Value)
-  -> Rules (q -> Action Value)
+jsonCache :: ShakeValue q => (q -> Action Value) -> Rules (q -> Action Value)
 jsonCache = jsonCache'
 
 -- | Like 'jsonCache' but allows caching/retrieving any JSON serializable
 -- objects.
-jsonCache' ::
-     forall a q. (ToJSON a, FromJSON a, ShakeValue q)
+jsonCache'
+  :: forall a q
+   . (ToJSON a, FromJSON a, ShakeValue q)
   => (q -> Action a)
   -> Rules (q -> Action a)
-jsonCache' loader =
-  unpackJSON <$> addOracleCache (\(CacheQuery q) -> A.encode <$> loader q)
-  where
-    unpackJSON ::
-         FromJSON a => (CacheQuery q -> Action ByteString) -> q -> Action a
-    unpackJSON runCacheQuery =
-      \q -> do
-        bytes <- runCacheQuery $ CacheQuery q
-        case A.eitherDecode bytes of
-          Left err -> fail err
-          Right res -> pure res
+jsonCache' loader = unpackJSON
+  <$> addOracleCache (\(CacheQuery q) -> A.encode <$> loader q)
+ where
+  unpackJSON
+    :: FromJSON a => (CacheQuery q -> Action ByteString) -> q -> Action a
+  unpackJSON runCacheQuery = \q -> do
+    bytes <- runCacheQuery $ CacheQuery q
+    case A.eitherDecode bytes of
+      Left  err -> fail err
+      Right res -> pure res
 
 -- | A wrapper around 'jsonCache' which simplifies caching of values which do NOT
 -- depend on an input parameter. Unfortunately Shake still requires that the
@@ -76,15 +75,13 @@
 --
 -- > projectCache = simpleJsonCache (ProjectList ()) $ do
 -- >   -- load your project list here; returning it as a Value
-simpleJsonCache :: ShakeValue q
-  => q
-  -> Action Value
-  -> Rules (Action Value)
+simpleJsonCache :: ShakeValue q => q -> Action Value -> Rules (Action Value)
 simpleJsonCache = simpleJsonCache'
 
 -- | Like 'simpleJsonCache' but allows caching any JSON serializable object.
-simpleJsonCache' ::
-     forall q a. (ToJSON a, FromJSON a, ShakeValue q)
+simpleJsonCache'
+  :: forall q a
+   . (ToJSON a, FromJSON a, ShakeValue q)
   => q
   -> Action a
   -> Rules (Action a)
diff --git a/src/Slick/Mustache.hs b/src/Slick/Mustache.hs
--- a/src/Slick/Mustache.hs
+++ b/src/Slick/Mustache.hs
@@ -1,10 +1,12 @@
 module Slick.Mustache
   ( compileTemplate'
-  ) where
+  )
+where
 
-import Development.Shake
-import Text.Mustache
-import Text.Mustache.Compile
+import           Development.Shake
+import           Text.Mustache
+import           Text.Mustache.Compile
+
 
 -- | Like 'compileTemplate' but tracks changes to template files and partials
 -- within Shake.
diff --git a/src/Slick/Pandoc.hs b/src/Slick/Pandoc.hs
--- a/src/Slick/Pandoc.hs
+++ b/src/Slick/Pandoc.hs
@@ -1,4 +1,3 @@
-{-# LANGUAGE DeriveFunctor #-}
 {-# LANGUAGE OverloadedStrings #-}
 
 module Slick.Pandoc
@@ -9,6 +8,10 @@
   , loadUsing
   , loadUsing'
   , convert
+  , html5Options
+  , markdownOptions
+  , PandocReader
+  , PandocWriter
   ) where
 
 import Control.Lens
@@ -23,23 +26,28 @@
 
 -- | Reasonable options for reading a markdown file
 markdownOptions :: ReaderOptions
-markdownOptions = def {readerExtensions = exts}
-  where
-    exts =
-      mconcat
-        [extensionsFromList [Ext_yaml_metadata_block], githubMarkdownExtensions]
+markdownOptions = def { readerExtensions = exts }
+ where
+  exts = mconcat
+    [ extensionsFromList
+      [ Ext_yaml_metadata_block
+      , Ext_fenced_code_attributes
+      , Ext_auto_identifiers
+      ]
+    , githubMarkdownExtensions
+    ]
 
 -- | Reasonable options for rendering to HTML
 html5Options :: WriterOptions
-html5Options =
-  def
-    { writerHighlightStyle = Just tango
-    , writerExtensions = writerExtensions def `mappend` githubMarkdownExtensions
-    }
+html5Options = def { writerHighlightStyle = Just tango
+                   , writerExtensions     = writerExtensions def
+                   }
 
 -- | Handle possible pandoc failure within the Action Monad
-unPandocM :: PandocPure a -> Action a
-unPandocM = either (fail . show) return . runPure
+unPandocM :: PandocIO a -> Action a
+unPandocM p = do
+  result <- liftIO $ runIO p
+  either (fail . show) return result
 
 -- | Convert markdown text into a 'Value';
 -- The 'Value'  has a "content" key containing rendered HTML
@@ -52,9 +60,9 @@
 markdownToHTML' :: (FromJSON a) => T.Text -> Action a
 markdownToHTML' = markdownToHTML >=> convert
 
-type PandocReader textType = textType -> PandocPure Pandoc
+type PandocReader textType = textType -> PandocIO Pandoc
 
-type PandocWriter = Pandoc -> PandocPure T.Text
+type PandocWriter = Pandoc -> PandocIO T.Text
 
 -- | Given a reader from 'Text.Pandoc.Readers' this creates a loader which
 -- given the source document will read its metadata into a 'Value'
@@ -67,13 +75,10 @@
 -- | Like 'makePandocReader' but will deserialize the metadata into any object
 -- which implements 'FromJSON'. Failure to deserialize will fail the Shake
 -- build.
-makePandocReader' ::
-     (FromJSON a)
-  => PandocReader textType
-  -> textType
-  -> Action (Pandoc, a)
+makePandocReader'
+  :: (FromJSON a) => PandocReader textType -> textType -> Action (Pandoc, a)
 makePandocReader' readerFunc text = do
-  (pdoc, meta) <- makePandocReader readerFunc text
+  (pdoc, meta)  <- makePandocReader readerFunc text
   convertedMeta <- convert meta
   return (pdoc, convertedMeta)
 
@@ -84,33 +89,37 @@
 loadUsing :: PandocReader textType -> PandocWriter -> textType -> Action Value
 loadUsing reader writer text = do
   (pdoc, meta) <- makePandocReader reader text
-  outText <- unPandocM $ writer pdoc
+  outText      <- unPandocM $ writer pdoc
   let withContent = meta & _Object . at "content" ?~ String outText
   return withContent
 
 -- | Like 'loadUsing' but allows also deserializes the 'Value' into any object
 -- which implements 'FromJSON'.  Failure to deserialize will fail the Shake
 -- build.
-loadUsing' :: (FromJSON a) => PandocReader textType -> PandocWriter -> textType -> Action a
+loadUsing'
+  :: (FromJSON a)
+  => PandocReader textType
+  -> PandocWriter
+  -> textType
+  -> Action a
 loadUsing' reader writer text = loadUsing reader writer text >>= convert
 
 -- | Attempt to convert between two JSON serializable objects (or 'Value's).
 -- Failure to deserialize fails the Shake build.
 convert :: (FromJSON a, ToJSON a, FromJSON b) => a -> Action b
-convert a =
-  case fromJSON (toJSON a) of
-    Success r -> pure r
-    Error err -> fail $ "json conversion error:" ++ err
+convert a = case fromJSON (toJSON a) of
+  Success r   -> pure r
+  Error   err -> fail $ "json conversion error:" ++ err
 
 -- | Flatten a Pandoc 'Meta' into a well-structured JSON object, rendering Pandoc
 -- text objects into plain strings along the way.
 flattenMeta :: Meta -> Value
 flattenMeta (Meta meta) = toJSON $ fmap go meta
-  where
-    go :: MetaValue -> Value
-    go (MetaMap m) = toJSON $ fmap go m
-    go (MetaList m) = toJSONList $ fmap go m
-    go (MetaBool m) = toJSON m
-    go (MetaString m) = toJSON m
-    go (MetaInlines m) = toJSON $ stringify m
-    go (MetaBlocks m) = toJSON $ stringify m
+ where
+  go :: MetaValue -> Value
+  go (MetaMap     m) = toJSON $ fmap go m
+  go (MetaList    m) = toJSONList $ fmap go m
+  go (MetaBool    m) = toJSON m
+  go (MetaString  m) = toJSON m
+  go (MetaInlines m) = toJSON $ stringify m
+  go (MetaBlocks  m) = toJSON $ stringify m
