module Yesod.EmbeddedStatic.AngularJsMangle (
parseDirectiveFiles
, parseDirectiveFile
, cleanupDIFile
) where
import Blaze.ByteString.Builder (toLazyByteString)
import Data.List (intersperse)
import Data.Maybe (catMaybes)
import Language.JavaScript.Parser
import Language.JavaScript.Parser.AST
import System.Directory (doesFileExist)
import System.FilePath
import qualified Data.ByteString.Lazy as BL
cleanupDIFile :: FilePath -> IO BL.ByteString
cleanupDIFile f = toLazyByteString . renderJS . cleanupDI <$> parseFile f
cleanupDI :: JSAST -> JSAST
cleanupDI (JSAstProgram exprs _) = JSAstProgram (map diTopExpr exprs) JSNoAnnot
cleanupDI x = x
diTopExpr :: JSStatement -> JSStatement
diTopExpr (JSMethodCall
call@(JSMemberDot
(JSIdentifier _ "module")
_
(JSIdentifier _ func))
_
argsCommaList
_
semi)
= JSMethodCall call JSNoAnnot (diCommaList (func == "directive") argsCommaList) JSNoAnnot semi
diTopExpr x = x
diCommaList :: Bool -> JSCommaList JSExpression -> JSCommaList JSExpression
diCommaList _ JSLNil = JSLNil
diCommaList isDir (JSLOne x) = JSLOne $ diFuncExpr isDir x
diCommaList isDir (JSLCons h _ x) = JSLCons (diCommaList isDir h) JSNoAnnot (diFuncExpr isDir x)
diFuncExpr :: Bool -> JSExpression -> JSExpression
diFuncExpr isDir (JSFunctionExpression _ ident _ params _ body) =
case params of
JSLNil -> func
_ -> JSArrayLiteral JSNoAnnot elems JSNoAnnot
where
body' = if isDir then diBody body else body
func = JSFunctionExpression JSNoAnnot ident JSNoAnnot params JSNoAnnot body'
elems :: [JSArrayElement]
elems = intersperse (JSArrayComma JSNoAnnot) $ map diParam (jsToList params) ++ [JSArrayElement func]
diFuncExpr _ x = x
diParam :: JSIdent -> JSArrayElement
diParam (JSIdentName _ name) = JSArrayElement $ JSStringLiteral JSNoAnnot ("\"" ++ name ++ "\"")
diParam JSIdentNone = JSArrayElement (JSDecimal JSNoAnnot "0")
diBody :: JSBlock -> JSBlock
diBody (JSBlock _ statements _) = JSBlock JSNoAnnot (map diReturn statements) JSNoAnnot
diReturn :: JSStatement -> JSStatement
diReturn (JSReturn _ (Just (JSObjectLiteral _ props _)) semi)
= JSReturn JSNoAnnot (Just (JSObjectLiteral JSNoAnnot (diPropertyList props) JSNoAnnot)) semi
diReturn x = x
diPropertyList :: JSObjectPropertyList -> JSObjectPropertyList
diPropertyList (JSCTLComma x _) = JSCTLNone $ diPropertyList' x
diPropertyList (JSCTLNone x) = JSCTLNone $ diPropertyList' x
diPropertyList' :: JSCommaList JSObjectProperty -> JSCommaList JSObjectProperty
diPropertyList' (JSLCons h _ x) = JSLCons (diPropertyList' h) JSNoAnnot (diProperty x)
diPropertyList' (JSLOne x) = JSLOne $ diProperty x
diPropertyList' JSLNil = JSLNil
testPropName :: String -> JSPropertyName -> Bool
testPropName n (JSPropertyIdent _ n') = n == n'
testPropName n (JSPropertyString _ n') = "'" ++ n ++ "'" == n' || "\"" ++ n ++ "\"" == n'
testPropName _ _ = False
diProperty :: JSObjectProperty -> JSObjectProperty
diProperty (JSPropertyNameandValue prop _ args)
| testPropName "controller" prop = JSPropertyNameandValue prop JSNoAnnot args'
where
args' = map (diFuncExpr False) args
diProperty x = x
parseDirectiveFiles :: FilePath -> [FilePath] -> IO [(String,FilePath)]
parseDirectiveFiles dir jsfiles = catMaybes <$> mapM parseDirectiveFile (map (dir</>) jsfiles)
parseDirectiveFile :: FilePath -> IO (Maybe (String, FilePath))
parseDirectiveFile f = do
j <- parseFile f
case parseDirId j of
Nothing -> return Nothing
Just i -> do
let hamlet = replaceExtension f "hamlet"
e <- doesFileExist hamlet
if e
then return $ Just (i, hamlet)
else return Nothing
firstJust :: [Maybe a] -> Maybe a
firstJust [] = Nothing
firstJust (x@(Just _):_) = x
firstJust (Nothing:xs) = firstJust xs
parseDirId :: JSAST -> Maybe String
parseDirId (JSAstProgram es _) = firstJust $ map parseDirectiveCall es
parseDirId _ = Nothing
jsToList :: JSCommaList a -> [a]
jsToList JSLNil = []
jsToList (JSLOne x) = [x]
jsToList (JSLCons h _ x) = jsToList h ++ [x]
parseDirectiveCall :: JSStatement -> Maybe String
parseDirectiveCall (JSMethodCall
(JSMemberDot
(JSIdentifier _ "module")
_
(JSIdentifier _ "directive"))
_
argsCommalist
_ --annotation
_)
= firstJust $ map parseDirArg $ jsToList argsCommalist
parseDirectiveCall _ = Nothing
parseDirArg :: JSExpression -> Maybe String
parseDirArg (JSFunctionExpression _ _ _ _ _ (JSBlock _ statements _)) = firstJust $ map parseDirSt statements
parseDirArg _ = Nothing
parseDirSt :: JSStatement -> Maybe String
parseDirSt (JSReturn _ (Just (JSObjectLiteral _ props _)) _) = result
where
propsLst = case props of
(JSCTLComma x _) -> jsToList x
(JSCTLNone x) -> jsToList x
result = firstJust $ map parseDirTUrl propsLst
parseDirSt _ = Nothing
parseDirTUrl :: JSObjectProperty -> Maybe String
parseDirTUrl (JSPropertyNameandValue prop _ [JSStringLiteral _ tUrl])
| testPropName "templateUrl" prop = Just (drop 1 $ take (length tUrl 1) tUrl)
parseDirTUrl _ = Nothing