----------------------------------------------------------------------------- -- | -- Module : Static.Resources.Generation -- Copyright : (c) Scrive 2012 -- License : BSD-style (see the LICENSE file in the distribution) -- -- Maintainer : mariusz@scrive.com -- Stability : development -- Portability : portable -- -- Module for generaing joined js and css files from spec -- module Static.Resources.Generation (generateResources) where import Control.Monad import Data.Functor import Data.Hash.MD5 import Data.Time import Static.Resources.Types import System.Process -- | Concatenate a the files of a given type together, from the resource set. generateAgregatedResourceTypeContent :: String -> ResourceType -> ResourceSet -> IO String generateAgregatedResourceTypeContent dir rt rs = do let files = map path $ filterByType (== rt) rs s <- forM files $ \p -> readFile $ dir ++ "/" ++ p return $ (concat s) -- | Generate one big file from all the CSS Files. generateAgregatedCSSFile :: String -> ResourceSet -> IO FilePath generateAgregatedCSSFile dir rs = do c <- generateAgregatedResourceTypeContent dir CSS rs let fn = (name rs) ++ "-" ++ (md5s $ Str $ c) ++ ".css" writeFile (dir ++ "/" ++ fn) c return fn -- | Generate one big file of CSS from all the LESS files. Requires -- lessc to be installed. generateAgregatedLESSFile :: String -> ResourceSet -> IO FilePath generateAgregatedLESSFile dir rs = do let files = map path $ filterByType (== LESS) rs c <- fmap concat $ forM files $ \file -> readProcess "lessc" [dir ++ "/" ++ file] "" let fn = name rs ++ "-" ++ md5s (Str c) ++ ".less.css" writeFile (dir ++ "/" ++ fn) c return fn -- | Generate seperate CSS files from the given LESS files: -- -- The point is for these to be easy to map to in development. So -- the generated files will be like this: -- -- /less/foo.less → /foo.less.css -- generateSeparateLESSFiles :: String -> ResourceSet -> IO [FilePath] generateSeparateLESSFiles dir rs = do forM (map path (filterByType (== LESS) rs)) $ \file -> do contents <- readProcess "lessc" [dir ++ "/" ++ file] "" let fn = file ++ ".css" writeFile (dir ++ "/" ++ fn) contents return fn -- | Aggregate the JS files in the given resource set. generateAgregatedJSFile :: String -> ResourceSet -> IO FilePath generateAgregatedJSFile dir rs = do c <- generateAgregatedResourceTypeContent dir JS rs let fn = (name rs) ++ "-" ++ (md5s $ Str $ c) ++ ".js" writeFile (dir ++ "/" ++ fn) c return fn -- | Generate each set of resources. generateResources :: ImportType -> String -> ResourceSpec -> FilePath -> IO ResourceSetsForImport generateResources it dir spec pathPrefix = liftM2 ResourceSetsForImport (forM (sets spec) (generateResourcesForSet it dir pathPrefix)) getCurrentTime -- | Change 'ResourceSet' to 'ResourceSetForImport'. Will generate files if needed. generateResourcesForSet :: ImportType -> String -> String -> ResourceSet -> IO ResourceSetForImport generateResourcesForSet Development dir pathPrefix rs = do lessF <- generateSeparateLESSFiles dir rs return $ ResourceSetForImport { set = rs , cssFiles = prefixize (filterByType (== CSS) rs) , jsFiles = prefixize (filterByType (\t -> t == JS || t == JSX) rs) , lessFiles = map (appendPath pathPrefix) lessF } where prefixize = map (appendPath pathPrefix . path) generateResourcesForSet Production dir pathPrefix rs = do cssF <- generateAgregatedCSSFile dir rs lessF <- generateAgregatedLESSFile dir rs jsF <- generateAgregatedJSFile dir rs return $ ResourceSetForImport { set = rs , cssFiles = prefixize [cssF] , jsFiles = prefixize (jsF : (path <$> filterByType (== JSX) rs)) , lessFiles = prefixize [lessF] } where prefixize = map (appendPath pathPrefix) -- | Append to path parts with a single /. appendPath :: String -> String -> String appendPath left right = reverse (strip (reverse left)) ++ "/" ++ strip right where strip = dropWhile (=='/')