{-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE FlexibleContexts #-} module Html.Static ( static , optimize ) where import Language.Haskell.TH import Data.Proxy import Html -- | Template haskell function to annote which parts of a html document are static. -- -- >>> :t $(static (div_ "<>")) -- Proxy "
<>
" -- -- Note that you can annote any part of the document which is itself a -- valid document and doesn't contain variables which are defined in -- the same file. -- -- >>> :t \x -> div_ ($(static (div_ "<>")) # x) -- \x -> 'Div > (Proxy "
<>
" # x) static :: Document a => a -> Q Exp static x = pure (SigE (ConE 'Proxy) (AppT (ConT ''Proxy) (LitT (StrTyLit (renderString x))))) -- | Try to optimize a quoted html document at compiletime. -- -- >>> :t \x -> $(optimize [| div_ "a" # x |]) -- \x -> 'Div > Proxy "a" # x -- -- This function recurses into an Q Exp and turns every literal -- expression which is applied to an element or to an attribute into a -- Proxy Symbol. It stops recursing a branch if any other expression -- than an element, attribute or (#) is found to avoid non -- typechecking code, like an if statement where only one branch is -- converted into a Proxy Symbol or an function, which wants a 'Div > -- String as argument, receiving a Proxy Symbol. -- -- If you don't mind changing type signatures, this function is safe -- to apply to any document and generally increases performance -- substantially. -- -- Note that it's different to `static`: it doesn't evaluate any -- function but it can be applied to documents with variables. It is -- less powerfull but more convenient because you can simply apply it -- to your entire html document. `static` you'd have to apply only to -- parts which can be calculated statically. Be aware, that it stops -- as well on known variables, so you have to apply it to the -- definition of your page and not to the variable to which your -- definition is bound. optimize :: Q Exp -> Q Exp optimize = fmap f where f (AppE (VarE a) b) | nameModule a == Just "Html.Element" || nameModule a == Just "Html.Attribute" = AppE (VarE a) (f b) f (AppE (AppE (VarE a) b) c) | nameModule a == Just "Html.Element" || nameModule a == Just "Html.Type.Internal" && nameBase a == "#" = AppE (AppE (VarE a) (f b)) (f c) f (InfixE (Just a) (VarE b) (Just c)) | nameModule b == Just "Html.Type.Internal" && nameBase b == "#" = InfixE (Just (f a)) (VarE b) (Just (f c)) f (LitE (CharL x)) = SigE (ConE 'Proxy) (AppT (ConT ''Proxy) (LitT (StrTyLit (renderString x)))) f (LitE (StringL x)) = SigE (ConE 'Proxy) (AppT (ConT ''Proxy) (LitT (StrTyLit (renderString x)))) f (LitE (IntegerL x)) = SigE (ConE 'Proxy) (AppT (ConT ''Proxy) (LitT (StrTyLit (renderString x)))) f x = x