Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow the use of the Google Closure compiler for minifying and optimizing generated javascript #126

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions rest-gen/rest-gen.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ library
Rest.Gen.Base.Link
Rest.Gen.Base.XML
Rest.Gen.Utils
Rest.Gen.JSCompiler
build-depends:
base >= 4.5 && < 4.9
, Cabal >= 1.16 && < 1.23
Expand All @@ -71,6 +72,9 @@ library
, uniplate >= 1.6.12 && < 1.7
, unordered-containers == 0.2.*
, vector == 0.10.*
, HTTP <= 4000.2.16
, network == 2.5.*
--Network isn't actually needed, but build will fail w/o it
if impl(ghc < 7.8)
build-depends: tagged >= 0.2 && < 0.9

Expand Down
3 changes: 2 additions & 1 deletion rest-gen/src/Rest/Gen.hs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import Rest.Gen.JavaScript (mkJsApi)
import Rest.Gen.Ruby (mkRbApi)
import Rest.Gen.Types
import Rest.Gen.Utils
import Rest.Gen.JSCompiler

generate :: Config -> String -> Api m -> [H.ModuleName] -> [H.ImportDecl] -> [(H.ModuleName, H.ModuleName)] -> IO ()
generate config name api sources imports rewrites =
Expand All @@ -31,7 +32,7 @@ generate config name api sources imports rewrites =
let context = DocsContext root ver (fromMaybe "./templates" (getSourceLocation config))
writeDocs context r loc
exitSuccess
Just MakeJS -> mkJsApi (overModuleName (++ "Api") moduleName) (get apiPrivate config) ver r >>= toTarget config
Just MakeJS -> mkJsApi (overModuleName (++ "Api") moduleName) (get apiPrivate config) ver r >>= withCompiler (get compiler config)>>= toTarget config
Just MakeRb -> mkRbApi (overModuleName (++ "Api") moduleName) (get apiPrivate config) ver r >>= toTarget config
Just MakeHS ->
do loc <- getTargetDir config "./client"
Expand Down
12 changes: 11 additions & 1 deletion rest-gen/src/Rest/Gen/Config.hs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ module Rest.Gen.Config
, target
, apiVersion
, apiPrivate

, compiler
, defaultConfig
, parseLocation
, options
Expand All @@ -26,6 +26,7 @@ import Control.Category
import Data.Label
import System.Console.GetOpt
import System.Environment (getArgs)
import Rest.Gen.JSCompiler

data Action = MakeDocs String | MakeJS | MakeRb | MakeHS
data Location = Default | Stream | Location String
Expand All @@ -36,6 +37,7 @@ data Config = Config
, _target :: Location
, _apiVersion :: String
, _apiPrivate :: Bool
, _compiler :: Maybe Compiler
}

mkLabels [''Config]
Expand All @@ -47,12 +49,19 @@ defaultConfig = Config
, _target = Default
, _apiVersion = "latest"
, _apiPrivate = True
, _compiler = Nothing
}

parseLocation :: String -> Location
parseLocation "-" = Stream
parseLocation s = Location s

parseCompiler:: String -> Maybe Compiler
parseCompiler "closure-simple" = Just ClosureSimple
parseCompiler "closure-advanced" = Just ClosureAdvanced
parseCompiler "whitespace" = Just ClosureWhitespace
parseCompiler _ = Nothing

options :: (a :-> Config) -> [OptDescr (a -> a)]
options parent =
[ Option ['d'] ["documentation"] (ReqArg (set (action . parent) . Just . MakeDocs) "URLROOT") "Generate API documentation, available under the provided URL root."
Expand All @@ -63,6 +72,7 @@ options parent =
, Option ['t'] ["target"] (ReqArg (set (target . parent) . parseLocation) "LOCATION") "The target location for generation."
, Option ['v'] ["version"] (ReqArg (set (apiVersion . parent)) "VERSION") "The version of the API under generation. Default latest."
, Option ['p'] ["hide-private"] (NoArg (set (apiPrivate . parent) False)) "Generate API for the public, hiding private resources. Not default."
, Option ['c'] ["compiler"] (ReqArg (set (compiler . parent) . parseCompiler) "COMPILER") "Choose a compiler for javascript. \nDefaults to None. \nOptions are closure-simple, closure-advanced, whitespace and command. \nHas no effect if not generating javascript api"
]

configFromArgs :: String -> IO Config
Expand Down
28 changes: 28 additions & 0 deletions rest-gen/src/Rest/Gen/JSCompiler.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
module Rest.Gen.JSCompiler (Compiler (..), withCompiler) where

import qualified Network.HTTP as HTTP
import Network.URI (parseURI)
import Data.Maybe

-- | Represents the Compiler options
data Compiler = ClosureWhitespace | ClosureSimple | ClosureAdvanced

-- | Use a compiler. Returns code compiled under the compiler.
-- To be use after generation and before returning javascript code
withCompiler:: Maybe Compiler -> String -> IO String
withCompiler Nothing = return . id
withCompiler (Just compiler) = useCompiler compiler

useCompiler:: Compiler -> String -> IO String
useCompiler ClosureSimple = closureCompile "SIMPLE_OPTIMIZATIONS"
useCompiler ClosureAdvanced = closureCompile "ADVANCED_OPTIMIZATIONS"
useCompiler ClosureWhitespace = closureCompile "WHITESPACE_ONLY"


closureCompile:: String -> String -> IO String
closureCompile compilationlevel code_str = HTTP.simpleHTTP mkPostRequest >>= HTTP.getResponseBody
where payload = HTTP.urlEncodeVars [("js_code",code_str), ("compilation_level",compilationlevel),("output_format","text"),("output_info","compiled_code")]
headers = [HTTP.mkHeader HTTP.HdrContentType "application/x-www-form-urlencoded", HTTP.mkHeader HTTP.HdrContentLength (show $ length payload)]
url = parseURI "http://www.closure-compiler.appspot.com/compile"
mkPostRequest = HTTP.Request {HTTP.rqURI = fromJust url, HTTP.rqMethod = HTTP.POST, HTTP.rqHeaders = headers, HTTP.rqBody = payload }