module Yesod.EmbeddedStatic.AngularJsMangle (
parseDirectiveFiles
, parseDirectiveFile
, cleanupDIFile
) where
import Blaze.ByteString.Builder (toLazyByteString)
import Control.Applicative
import Data.Maybe (catMaybes)
import Language.JavaScript.Parser
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 :: JSNode -> JSNode
cleanupDI (NN (JSSourceElementsTop exprs)) = NN $ JSSourceElementsTop $ map diTopExpr exprs
cleanupDI x = x
diTopExpr :: JSNode -> JSNode
diTopExpr (NN (JSExpression
[ call@(NN (JSMemberDot
[NT (JSIdentifier "module") _ _]
_
(NT (JSIdentifier func) _ _)))
, NN (JSArguments open args close)
])) = NN $ JSExpression [call, NN $ JSArguments open (map (diFuncExpr directive) args) close]
where directive = func == "directive"
diTopExpr x = x
diFuncExpr :: Bool -> JSNode -> JSNode
diFuncExpr isDir (NN (JSFunctionExpression a b c params d body)) =
case params of
[] -> func
_ -> NN $ JSArrayLiteral aopen elems aclose
where
aopen = NT (JSLiteral "[") tokenPosnEmpty [NoComment]
aclose = NT (JSLiteral "]") tokenPosnEmpty [NoComment]
comma = NN (JSElision (NT (JSLiteral ",") tokenPosnEmpty [NoComment]))
body' = if isDir then diBody body else body
func = NN $ JSFunctionExpression a b c params d body'
elems = map diParam params ++ [comma, func]
diFuncExpr _ x = x
diParam :: JSNode -> JSNode
diParam (NT (JSIdentifier name) _ _) = NT (JSStringLiteral '"' name) tokenPosnEmpty [NoComment]
diParam (NT (JSLiteral ",") _ _) = NN $ JSElision $ NT (JSLiteral ",") tokenPosnEmpty [NoComment]
diParam x = x
diBody :: JSNode -> JSNode
diBody (NN (JSBlock begin statements end)) = NN $ JSBlock begin (map diReturn statements) end
diBody x = x
diReturn :: JSNode -> JSNode
diReturn (NN (JSReturn ret [NN (JSExpression [NN (JSObjectLiteral begin props end)])] colon))
= NN $ JSReturn ret [NN (JSExpression [NN (JSObjectLiteral begin (map diProperty props) end)])] colon
diReturn x = x
testPropName :: String -> JSNode -> Bool
testPropName n (NT (JSIdentifier n') _ _) = n == n'
testPropName n (NT (JSStringLiteral _ n') _ _) = n == n'
testPropName _ _ = False
diProperty :: JSNode -> JSNode
diProperty (NN (JSPropertyNameandValue prop _ args))
| testPropName "controller" prop = NN $ JSPropertyNameandValue ctrl colon args'
where
ctrl = NT (JSStringLiteral '"' "controller") tokenPosnEmpty [NoComment]
colon = NT (JSLiteral ":") tokenPosnEmpty [NoComment]
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 :: JSNode -> Maybe String
parseDirId (NN (JSSourceElementsTop es)) = firstJust $ map parseDirectiveCall es
parseDirId _ = Nothing
parseDirectiveCall :: JSNode -> Maybe String
parseDirectiveCall (NN (JSExpression
[NN (JSMemberDot
[NT (JSIdentifier "module") _ _]
_
(NT (JSIdentifier "directive") _ _)),
NN (JSArguments _ args _)])) = firstJust $ map parseDirArg args
parseDirectiveCall _ = Nothing
parseDirArg :: JSNode -> Maybe String
parseDirArg (NN (JSFunctionExpression _ _ _ _ _ (NN (JSBlock _ statements _))))
= firstJust $ map parseDirSt statements
parseDirArg _ = Nothing
parseDirSt :: JSNode -> Maybe String
parseDirSt (NN (JSReturn _ [NN (JSExpression [NN (JSObjectLiteral _ props _)])] _))
= firstJust $ map parseDirTUrl props
parseDirSt _ = Nothing
parseDirTUrl :: JSNode -> Maybe String
parseDirTUrl (NN (JSPropertyNameandValue prop _ [NT (JSStringLiteral _ tUrl) _ _]))
| testPropName "templateUrl" prop = Just tUrl
parseDirTUrl _ = Nothing