-- | A Compiler manages targets and dependencies between targets---- The most distinguishing property of a 'Compiler' is that it is an Arrow. A-- compiler of the type @Compiler a b@ is simply a compilation phase which takes-- an @a@ as input, and produces a @b@ as output.---- Compilers are chained using the '>>>' arrow operation. If we have a compiler---- > getResourceString :: Compiler Resource String---- which reads the resource, and a compiler---- > readPage :: Compiler String (Page String)---- we can chain these two compilers to get a---- > (getResourceString >>> readPage) :: Compiler Resource (Page String)---- Most compilers can be created by combining smaller compilers using '>>>'.---- More advanced constructions are also possible using arrow, and sometimes-- these are needed. For a good introduction to arrow, you can refer to---- <http://en.wikibooks.org/wiki/Haskell/Understanding_arrows>---- A construction worth writing a few paragraphs about here are the 'require'-- functions. Different variants of this function are exported here, but they-- all serve more or less the same goal.---- When you use only '>>>' to chain your compilers, you get a linear pipeline ---- it is not possible to add extra items from other compilers along the way.-- This is where the 'require' functions come in.---- This function allows you to reference other items, which are then added to-- the pipeline. Let's look at this crappy ASCII illustration which represents-- a pretty common scenario:---- > read resource >>> pandoc render >>> layout >>> relativize URL's-- >-- > @templates/fancy.html@---- We want to construct a pipeline of compilers to go from our resource to a-- proper webpage. However, the @layout@ compiler takes more than just the-- rendered page as input: it needs the @templates/fancy.html@ template as well.---- This is an example of where we need the @require@ function. We can solve-- this using a construction that looks like:---- > ... >>> pandoc render >>> require >>> layout >>> ...-- > |-- > @templates/fancy.html@ ------/---- This illustration can help us understand the type signature of 'require'.---- > require :: (Binary a, Typeable a, Writable a)-- > => Identifier-- > -> (b -> a -> c)-- > -> Compiler b c---- Let's look at it in detail:---- > (Binary a, Typeable a, Writable a)-- -- These are constraints for the @a@ type. @a@ (the template) needs to have-- certain properties for it to be required.---- > Identifier---- This is simply @templates/fancy.html@: the 'Identifier' of the item we want-- to 'require', in other words, the name of the item we want to add to the-- pipeline somehow.---- > (b -> a -> c)---- This is a function given by the user, specifying /how/ the two items shall be-- merged. @b@ is the output of the previous compiler, and @a@ is the item we-- just required -- the template. This means @c@ will be the final output of the-- 'require' combinator.---- > Compiler b c---- Indeed, we have now constructed a compiler which takes a @b@ and produces a-- @c@. This means that we have a linear pipeline again, thanks to the 'require'-- function. So, the 'require' function actually helps to reduce to complexity-- of Hakyll applications!---- Note that require will fetch a previously compiled item: in our example of-- the type @a@. It is /very/ important that the compiler which produced this-- value, produced the right type as well!--{-# LANGUAGE GeneralizedNewtypeDeriving #-}moduleHakyll.Core.Compiler(Compiler,runCompiler,getIdentifier,getRoute,getRouteFor,getResourceString,getResourceLBS,fromDependency,require_,require,requireA,requireAll_,requireAll,requireAllA,cached,unsafeCompiler,traceShowCompiler,mapCompiler,timedCompiler,byExtension)whereimportPreludehiding((.),id)importControl.Arrow((>>>),(&&&),arr)importControl.Applicative((<$>))importControl.Monad.Reader(ask)importControl.Monad.Trans(liftIO)importControl.Monad.Error(throwError)importControl.Category(Category,(.),id)importData.Maybe(fromMaybe)importSystem.FilePath(takeExtension)importData.Binary(Binary)importData.Typeable(Typeable)importData.ByteString.Lazy(ByteString)importHakyll.Core.IdentifierimportHakyll.Core.Identifier.PatternimportHakyll.Core.CompiledItemimportHakyll.Core.WritableimportHakyll.Core.ResourceimportHakyll.Core.Resource.ProviderimportHakyll.Core.Compiler.InternalimportHakyll.Core.StoreimportHakyll.Core.Rules.InternalimportHakyll.Core.RoutesimportHakyll.Core.Logger-- | Run a compiler, yielding the resulting target and it's dependencies. This-- version of 'runCompilerJob' also stores the result--runCompiler::Compiler()CompileRule-- ^ Compiler to run->Identifier-- ^ Target identifier->ResourceProvider-- ^ Resource provider->[Identifier]-- ^ Universe->Routes-- ^ Route->Store-- ^ Store->Bool-- ^ Was the resource modified?->Logger-- ^ Logger->IO(ThrowingCompileRule)-- ^ Resulting itemrunCompilercompilerid'provideruniverseroutesstoremodifiedlogger=do-- Run the compiler jobresult<-runCompilerJobcompilerid'provideruniverseroutesstoremodifiedlogger-- Inspect the resultcaseresultof-- In case we compiled an item, we will store a copy in the cache first,-- before we return control. This makes sure the compiled item can later-- be accessed by e.g. require.Right(CompileRule(CompiledItemx))->storeSetstore"Hakyll.Core.Compiler.runCompiler"id'x-- Otherwise, we do nothing here_->return()returnresult-- | Get the identifier of the item that is currently being compiled--getIdentifier::CompileraIdentifiergetIdentifier=fromJob$const$CompilerM$compilerIdentifier<$>ask-- | Get the route we are using for this item--getRoute::Compilera(MaybeFilePath)getRoute=getIdentifier>>>getRouteFor-- | Get the route for a specified item--getRouteFor::CompilerIdentifier(MaybeFilePath)getRouteFor=fromJob$\identifier->CompilerM$doroutes<-compilerRoutes<$>askreturn$runRoutesroutesidentifier-- | Get the resource we are compiling as a string--getResourceString::CompilerResourceStringgetResourceString=getResourceWithresourceString-- | Get the resource we are compiling as a lazy bytestring--getResourceLBS::CompilerResourceByteStringgetResourceLBS=getResourceWithresourceLBS-- | Overloadable function for 'getResourceString' and 'getResourceLBS'--getResourceWith::(ResourceProvider->Resource->IOa)->CompilerResourceagetResourceWithreader=fromJob$\resource->CompilerM$doletidentifier=unResourceresourceprovider<-compilerResourceProvider<$>askifresourceExistsproviderresourcethenliftIO$readerproviderresourceelsethrowError$error'identifierwhereerror'id'="Hakyll.Core.Compiler.getResourceWith: resource "++showid'++" not found"-- | Auxiliary: get a dependency--getDependency::(Binarya,Writablea,Typeablea)=>Identifier->CompilerMagetDependencyid'=CompilerM$dostore<-compilerStore<$>askresult<-liftIO$storeGetstore"Hakyll.Core.Compiler.runCompiler"id'caseresultofNotFound->throwErrornotFoundWrongTypeer->throwError$wrongTypeerFoundx->returnxwherenotFound="Hakyll.Core.Compiler.getDependency: "++showid'++" not found "++"not found in the cache, the cache might be corrupted or "++"the item you are referring to might not exist"wrongTypeer="Hakyll.Core.Compiler.getDependency: "++showid'++" was found "++"in the cache, but does not have the right type: expected "++showe++" but got "++showr-- | Variant of 'require' which drops the current value--require_::(Binarya,Typeablea,Writablea)=>Identifier->Compilerbarequire_identifier=fromDependencyidentifier>>>fromJob(const$getDependencyidentifier)-- | Require another target. Using this function ensures automatic handling of-- dependencies--require::(Binarya,Typeablea,Writablea)=>Identifier->(b->a->c)->Compilerbcrequireidentifier=requireAidentifier.arr.uncurry-- | Arrow-based variant of 'require'--requireA::(Binarya,Typeablea,Writablea)=>Identifier->Compiler(b,a)c->CompilerbcrequireAidentifier=(id&&&require_identifier>>>)-- | Variant of 'requireAll' which drops the current value--requireAll_::(Binarya,Typeablea,Writablea)=>Pattern->Compilerb[a]requireAll_pattern=fromDependencies(constgetDeps)>>>fromJobrequireAll_'wheregetDeps=filterMatchespatternrequireAll_'=const$CompilerM$dodeps<-getDeps.compilerUniverse<$>askmapM(unCompilerM.getDependency)deps-- | Require a number of targets. Using this function ensures automatic handling-- of dependencies--requireAll::(Binarya,Typeablea,Writablea)=>Pattern->(b->[a]->c)->CompilerbcrequireAllpattern=requireAllApattern.arr.uncurry-- | Arrow-based variant of 'requireAll'--requireAllA::(Binarya,Typeablea,Writablea)=>Pattern->Compiler(b,[a])c->CompilerbcrequireAllApattern=(id&&&requireAll_pattern>>>)cached::(Binarya,Typeablea,Writablea)=>String->CompilerResourcea->CompilerResourceacachedname(Compilerdj)=Compilerd$const$CompilerM$dologger<-compilerLogger<$>askidentifier<-compilerIdentifier<$>askstore<-compilerStore<$>askmodified<-compilerResourceModified<$>askreportlogger$"Checking cache: "++ifmodifiedthen"modified"else"OK"ifmodifiedthendov<-unCompilerM$j$fromIdentifieridentifierliftIO$storeSetstorenameidentifiervreturnvelsedov<-liftIO$storeGetstorenameidentifiercasevofFoundv'->returnv'_->throwErrorerror'whereerror'="Hakyll.Core.Compiler.cached: Cache corrupt!"-- | Create an unsafe compiler from a function in IO--unsafeCompiler::(a->IOb)-- ^ Function to lift->Compilerab-- ^ Resulting compilerunsafeCompilerf=fromJob$CompilerM.liftIO.f-- | Compiler for debugging purposes--traceShowCompiler::Showa=>CompileraatraceShowCompiler=fromJob$\x->CompilerM$dologger<-compilerLogger<$>askreportlogger$showxreturnx-- | Map over a compiler--mapCompiler::Compilerab->Compiler[a][b]mapCompiler(Compilerdj)=Compilerd$mapMj-- | Log and time a compiler--timedCompiler::String-- ^ Message->Compilerab-- ^ Compiler to time->Compilerab-- ^ Resulting compilertimedCompilermsg(Compilerdj)=Compilerd$\x->CompilerM$dologger<-compilerLogger<$>asktimedloggermsg$unCompilerM$jx-- | Choose a compiler by extension---- Example:---- > route "css/*" $ setExtension "css"-- > compile "css/*" $ byExtension (error "Not a (S)CSS file")-- > [ (".css", compressCssCompiler)-- > , (".scss", sass)-- > ]---- This piece of code will select the @compressCssCompiler@ for @.css@ files,-- and the @sass@ compiler (defined elsewhere) for @.scss@ files.--byExtension::Compilerab-- ^ Default compiler->[(String,Compilerab)]-- ^ Choices->Compilerab-- ^ Resulting compilerbyExtensiondefaultCompilerchoices=Compilerdepsjobwhere-- Lookup the compiler, give an error when it is not foundlookup'identifier=letextension=takeExtension$toFilePathidentifierinfromMaybedefaultCompiler$lookupextensionchoices-- Collect the dependencies of the choicedeps=doidentifier<-dependencyIdentifier<$>askcompilerDependencies$lookup'identifier-- Collect the job of the choicejobx=CompilerM$doidentifier<-compilerIdentifier<$>askunCompilerM$compilerJob(lookup'identifier)x