on master help sign in
Convert servant-js to use text.
a624410Arian van Putten
servant-js/src/Servant/JS.hs+8−3
@@ -39,7 +39,7 @@
39-- Let's keep it simple and produce vanilla Javascript code with the default options.
39-- Let's keep it simple and produce vanilla Javascript code with the default options.
40--
40--
41-- @
41-- @
42−-- jsCode :: String
42+-- jsCode :: Text
43-- jsCode = 'jsForAPI' api 'vanillaJS'
43-- jsCode = 'jsForAPI' api 'vanillaJS'
44-- @
44-- @
45--
45--
@@ -60,7 +60,7 @@
60-- All you need to do to use it is to use 'vanillaJSWith' and pass it @myOptions@.
60-- All you need to do to use it is to use 'vanillaJSWith' and pass it @myOptions@.
61--
61--
62-- @
62-- @
63−-- jsCodeWithMyOptions :: String
63+-- jsCodeWithMyOptions :: Text
64-- jsCodeWithMyOptions = 'jsForAPI' api ('vanillaJSWith' myOptions)
64-- jsCodeWithMyOptions = 'jsForAPI' api ('vanillaJSWith' myOptions)
65-- @
65-- @
66--
66--
@@ -112,7 +112,10 @@ module Servant.JS
112 , GenerateList(..)
112 , GenerateList(..)
113 ) where
113 ) where
114
114
115+import Prelude hiding (writeFile)
115import Data.Proxy
116import Data.Proxy
117+import Data.Text
118+import Data.Text.IO (writeFile)
116import Servant.JS.Angular
119import Servant.JS.Angular
117import Servant.JS.Axios
120import Servant.JS.Axios
118import Servant.JS.Internal
121import Servant.JS.Internal
@@ -131,7 +134,7 @@ javascript p = foreignFor p defReq
131jsForAPI :: (HasForeign api, GenerateList (Foreign api))
134jsForAPI :: (HasForeign api, GenerateList (Foreign api))
132 => Proxy api -- ^ proxy for your API type
135 => Proxy api -- ^ proxy for your API type
133 -> JavaScriptGenerator -- ^ js code generator to use (angular, vanilla js, jquery, others)
136 -> JavaScriptGenerator -- ^ js code generator to use (angular, vanilla js, jquery, others)
134− -> String -- ^ a string that you can embed in your pages or write to a file
137+ -> Text -- ^ a text that you can embed in your pages or write to a file
135jsForAPI p gen = gen (listFromAPI p)
138jsForAPI p gen = gen (listFromAPI p)
136
139
137-- | Directly generate all the javascript functions for your API
140-- | Directly generate all the javascript functions for your API
@@ -142,6 +145,8 @@ writeJSForAPI :: (HasForeign api, GenerateList (Foreign api))
142 -> JavaScriptGenerator -- ^ js code generator to use (angular, vanilla js, jquery, others)
145 -> JavaScriptGenerator -- ^ js code generator to use (angular, vanilla js, jquery, others)
143 -> FilePath -- ^ path to the file you want to write the resulting javascript code into
146 -> FilePath -- ^ path to the file you want to write the resulting javascript code into
144 -> IO ()
147 -> IO ()
148+
149+-- TODO Data.Text
145writeJSForAPI p gen fp = writeFile fp (jsForAPI p gen)
150writeJSForAPI p gen fp = writeFile fp (jsForAPI p gen)
146
151
147-- | Utility class used by 'jsForAPI' which computes
152-- | Utility class used by 'jsForAPI' which computes
servant-js/src/Servant/JS/Angular.hs+24−22
@@ -1,17 +1,19 @@
1+{-#LANGUAGE OverloadedStrings #-}
1module Servant.JS.Angular where
2module Servant.JS.Angular where
2
3
3import Control.Lens
4import Control.Lens
4−import Data.List
5import Data.Monoid
5import Data.Monoid
6+import qualified Data.Text as T
7+import Data.Text (Text)
6import Servant.Foreign
8import Servant.Foreign
7import Servant.JS.Internal
9import Servant.JS.Internal
8
10
9-- | Options specific to the angular code generator
11-- | Options specific to the angular code generator
10data AngularOptions = AngularOptions
12data AngularOptions = AngularOptions
11− { serviceName :: String -- ^ When generating code with wrapInService,
13+ { serviceName :: Text -- ^ When generating code with wrapInService,
12 -- name of the service to generate
14 -- name of the service to generate
13− , prologue :: String -> String -> String -- ^ beginning of the service definition
15+ , prologue :: Text -> Text -> Text -- ^ beginning of the service definition
14− , epilogue :: String -- ^ end of the service definition
16+ , epilogue :: Text -- ^ end of the service definition
15 }
17 }
16
18
17-- | Default options for the Angular codegen. Used by 'wrapInService'.
19-- | Default options for the Angular codegen. Used by 'wrapInService'.
@@ -34,12 +36,12 @@ angularService ngOpts = angularServiceWith ngOpts defCommonGeneratorOptions
34angularServiceWith :: AngularOptions -> CommonGeneratorOptions -> JavaScriptGenerator
36angularServiceWith :: AngularOptions -> CommonGeneratorOptions -> JavaScriptGenerator
35angularServiceWith ngOpts opts reqs =
37angularServiceWith ngOpts opts reqs =
36 prologue ngOpts svc mName
38 prologue ngOpts svc mName
37− <> intercalate "," (map generator reqs) <>
39+ <> T.intercalate "," (map generator reqs) <>
38 epilogue ngOpts
40 epilogue ngOpts
39 where
41 where
40 generator req = generateAngularJSWith ngOpts opts req
42 generator req = generateAngularJSWith ngOpts opts req
41 svc = serviceName ngOpts
43 svc = serviceName ngOpts
42− mName = if null (moduleName opts)
44+ mName = if moduleName opts == ""
43 then "app."
45 then "app."
44 else moduleName opts <> "."
46 else moduleName opts <> "."
45
47
@@ -50,14 +52,14 @@ angular ngopts = angularWith ngopts defCommonGeneratorOptions
50
52
51-- | Generate regular javascript functions that use the $http service.
53-- | Generate regular javascript functions that use the $http service.
52angularWith :: AngularOptions -> CommonGeneratorOptions -> JavaScriptGenerator
54angularWith :: AngularOptions -> CommonGeneratorOptions -> JavaScriptGenerator
53−angularWith ngopts opts = intercalate "\n\n" . map (generateAngularJSWith ngopts opts)
55+angularWith ngopts opts = T.intercalate "\n\n" . map (generateAngularJSWith ngopts opts)
54
56
55-- | js codegen using $http service from Angular using default options
57-- | js codegen using $http service from Angular using default options
56−generateAngularJS :: AngularOptions -> AjaxReq -> String
58+generateAngularJS :: AngularOptions -> AjaxReq -> Text
57generateAngularJS ngOpts = generateAngularJSWith ngOpts defCommonGeneratorOptions
59generateAngularJS ngOpts = generateAngularJSWith ngOpts defCommonGeneratorOptions
58
60
59-- | js codegen using $http service from Angular
61-- | js codegen using $http service from Angular
60−generateAngularJSWith :: AngularOptions -> CommonGeneratorOptions -> AjaxReq -> String
62+generateAngularJSWith :: AngularOptions -> CommonGeneratorOptions -> AjaxReq -> Text
61generateAngularJSWith ngOptions opts req = "\n" <>
63generateAngularJSWith ngOptions opts req = "\n" <>
62 fname <> fsep <> " function(" <> argsStr <> ")\n"
64 fname <> fsep <> " function(" <> argsStr <> ")\n"
63 <> "{\n"
65 <> "{\n"
@@ -69,7 +71,7 @@ generateAngularJSWith ngOptions opts req = "\n" <>
69 <> " });\n"
71 <> " });\n"
70 <> "}\n"
72 <> "}\n"
71
73
72− where argsStr = intercalate ", " args
74+ where argsStr = T.intercalate ", " args
73 args = http
75 args = http
74 ++ captures
76 ++ captures
75 ++ map (view argName) queryparams
77 ++ map (view argName) queryparams
@@ -79,7 +81,7 @@ generateAngularJSWith ngOptions opts req = "\n" <>
79 -- If we want to generate Top Level Function, they must depend on
81 -- If we want to generate Top Level Function, they must depend on
80 -- the $http service, if we generate a service, the functions will
82 -- the $http service, if we generate a service, the functions will
81 -- inherit this dependency from the service
83 -- inherit this dependency from the service
82− http = case length (serviceName ngOptions) of
84+ http = case T.length (serviceName ngOptions) of
83 0 -> ["$http"]
85 0 -> ["$http"]
84 _ -> []
86 _ -> []
85
87
@@ -104,12 +106,12 @@ generateAngularJSWith ngOptions opts req = "\n" <>
104 reqheaders =
106 reqheaders =
105 if null hs
107 if null hs
106 then ""
108 then ""
107− else " , headers: { " ++ headersStr ++ " }\n"
109+ else " , headers: { " <> headersStr <> " }\n"
108
110
109− where headersStr = intercalate ", " $ map headerStr hs
111+ where headersStr = T.intercalate ", " $ map headerStr hs
110− headerStr header = "\"" ++
112+ headerStr header = "\"" <>
111− headerArgName header ++
113+ headerArgName header <>
112− "\": " ++ toJSHeader header
114+ "\": " <> toJSHeader header
113
115
114 namespace =
116 namespace =
115 if hasService
117 if hasService
@@ -118,9 +120,9 @@ generateAngularJSWith ngOptions opts req = "\n" <>
118 then "var "
120 then "var "
119 else (moduleName opts) <> "."
121 else (moduleName opts) <> "."
120 where
122 where
121− hasNoModule = null (moduleName opts)
123+ hasNoModule = moduleName opts == ""
122
124
123− hasService = not $ null (serviceName ngOptions)
125+ hasService = serviceName ngOptions /= ""
124
126
125 fsep = if hasService then ":" else " ="
127 fsep = if hasService then ":" else " ="
126
128
@@ -129,13 +131,13 @@ generateAngularJSWith ngOptions opts req = "\n" <>
129 method = req ^. reqMethod
131 method = req ^. reqMethod
130 url = if url' == "'" then "'/'" else url'
132 url = if url' == "'" then "'/'" else url'
131 url' = "'"
133 url' = "'"
132− ++ urlPrefix opts
134+ <> urlPrefix opts
133− ++ urlArgs
135+ <> urlArgs
134− ++ queryArgs
136+ <> queryArgs
135
137
136 urlArgs = jsSegments
138 urlArgs = jsSegments
137 $ req ^.. reqUrl.path.traverse
139 $ req ^.. reqUrl.path.traverse
138
140
139 queryArgs = if null queryparams
141 queryArgs = if null queryparams
140 then ""
142 then ""
141− else " + '?" ++ jsParams queryparams
143+ else " + '?" <> jsParams queryparams
servant-js/src/Servant/JS/Axios.hs+19−18
@@ -1,9 +1,10 @@
1+{-#LANGUAGE OverloadedStrings #-}
1module Servant.JS.Axios where
2module Servant.JS.Axios where
2
3
3import Control.Lens
4import Control.Lens
4−import Data.Char (toLower)
5−import Data.List
6import Data.Monoid
5import Data.Monoid
6+import Data.Text (Text)
7+import qualified Data.Text as T
7import Servant.Foreign
8import Servant.Foreign
8import Servant.JS.Internal
9import Servant.JS.Internal
9
10
@@ -14,9 +15,9 @@ data AxiosOptions = AxiosOptions
14 -- should be made using credentials
15 -- should be made using credentials
15 withCredentials :: !Bool
16 withCredentials :: !Bool
16 -- | the name of the cookie to use as a value for xsrf token
17 -- | the name of the cookie to use as a value for xsrf token
17− , xsrfCookieName :: !(Maybe String)
18+ , xsrfCookieName :: !(Maybe Text)
18 -- | the name of the header to use as a value for xsrf token
19 -- | the name of the header to use as a value for xsrf token
19− , xsrfHeaderName :: !(Maybe String)
20+ , xsrfHeaderName :: !(Maybe Text)
20 }
21 }
21
22
22-- | Default instance of the AxiosOptions
23-- | Default instance of the AxiosOptions
@@ -36,14 +37,14 @@ axios aopts = axiosWith aopts defCommonGeneratorOptions
36
37
37-- | Generate regular javascript functions that use the axios library.
38-- | Generate regular javascript functions that use the axios library.
38axiosWith :: AxiosOptions -> CommonGeneratorOptions -> JavaScriptGenerator
39axiosWith :: AxiosOptions -> CommonGeneratorOptions -> JavaScriptGenerator
39−axiosWith aopts opts = intercalate "\n\n" . map (generateAxiosJSWith aopts opts)
40+axiosWith aopts opts = T.intercalate "\n\n" . map (generateAxiosJSWith aopts opts)
40
41
41-- | js codegen using axios library using default options
42-- | js codegen using axios library using default options
42−generateAxiosJS :: AxiosOptions -> AjaxReq -> String
43+generateAxiosJS :: AxiosOptions -> AjaxReq -> Text
43generateAxiosJS aopts = generateAxiosJSWith aopts defCommonGeneratorOptions
44generateAxiosJS aopts = generateAxiosJSWith aopts defCommonGeneratorOptions
44
45
45-- | js codegen using axios library
46-- | js codegen using axios library
46−generateAxiosJSWith :: AxiosOptions -> CommonGeneratorOptions -> AjaxReq -> String
47+generateAxiosJSWith :: AxiosOptions -> CommonGeneratorOptions -> AjaxReq -> Text
47generateAxiosJSWith aopts opts req = "\n" <>
48generateAxiosJSWith aopts opts req = "\n" <>
48 fname <> " = function(" <> argsStr <> ")\n"
49 fname <> " = function(" <> argsStr <> ")\n"
49 <> "{\n"
50 <> "{\n"
@@ -57,7 +58,7 @@ generateAxiosJSWith aopts opts req = "\n" <>
57 <> " });\n"
58 <> " });\n"
58 <> "}\n"
59 <> "}\n"
59
60
60− where argsStr = intercalate ", " args
61+ where argsStr = T.intercalate ", " args
61 args = captures
62 args = captures
62 ++ map (view argName) queryparams
63 ++ map (view argName) queryparams
63 ++ body
64 ++ body
@@ -101,30 +102,30 @@ generateAxiosJSWith aopts opts req = "\n" <>
101 then ""
102 then ""
102 else " , headers: { " <> headersStr <> " }\n"
103 else " , headers: { " <> headersStr <> " }\n"
103
104
104− where headersStr = intercalate ", " $ map headerStr hs
105+ where headersStr = T.intercalate ", " $ map headerStr hs
105− headerStr header = "\"" ++
106+ headerStr header = "\"" <>
106− headerArgName header ++
107+ headerArgName header <>
107− "\": " ++ toJSHeader header
108+ "\": " <> toJSHeader header
108
109
109 namespace =
110 namespace =
110 if hasNoModule
111 if hasNoModule
111 then "var "
112 then "var "
112 else (moduleName opts) <> "."
113 else (moduleName opts) <> "."
113 where
114 where
114− hasNoModule = null (moduleName opts)
115+ hasNoModule = moduleName opts == ""
115
116
116 fname = namespace <> (functionNameBuilder opts $ req ^. funcName)
117 fname = namespace <> (functionNameBuilder opts $ req ^. funcName)
117
118
118− method = map toLower $ req ^. reqMethod
119+ method = T.toLower $ req ^. reqMethod
119 url = if url' == "'" then "'/'" else url'
120 url = if url' == "'" then "'/'" else url'
120 url' = "'"
121 url' = "'"
121− ++ urlPrefix opts
122+ <> urlPrefix opts
122− ++ urlArgs
123+ <> urlArgs
123− ++ queryArgs
124+ <> queryArgs
124
125
125 urlArgs = jsSegments
126 urlArgs = jsSegments
126 $ req ^.. reqUrl.path.traverse
127 $ req ^.. reqUrl.path.traverse
127
128
128 queryArgs = if null queryparams
129 queryArgs = if null queryparams
129 then ""
130 then ""
130− else " + '?" ++ jsParams queryparams
131+ else " + '?" <> jsParams queryparams
servant-js/src/Servant/JS/Internal.hs+64−39
@@ -1,3 +1,4 @@
1+{-#LANGUAGE OverloadedStrings #-}
1module Servant.JS.Internal
2module Servant.JS.Internal
2 ( JavaScriptGenerator
3 ( JavaScriptGenerator
3 , CommonGeneratorOptions(..)
4 , CommonGeneratorOptions(..)
@@ -34,28 +35,28 @@ module Servant.JS.Internal
34import Control.Lens ((^.))
35import Control.Lens ((^.))
35import qualified Data.CharSet as Set
36import qualified Data.CharSet as Set
36import qualified Data.CharSet.Unicode.Category as Set
37import qualified Data.CharSet.Unicode.Category as Set
37−import Data.List
38import Data.Monoid
38import Data.Monoid
39import qualified Data.Text as T
39import qualified Data.Text as T
40+import Data.Text (Text)
40import Servant.Foreign
41import Servant.Foreign
41
42
42type AjaxReq = Req
43type AjaxReq = Req
43
44
44-- A 'JavascriptGenerator' just takes the data found in the API type
45-- A 'JavascriptGenerator' just takes the data found in the API type
45−-- for each endpoint and generates Javascript code in a String. Several
46+-- for each endpoint and generates Javascript code in a Text. Several
46-- generators are available in this package.
47-- generators are available in this package.
47−type JavaScriptGenerator = [Req] -> String
48+type JavaScriptGenerator = [Req] -> Text
48
49
49-- | This structure is used by specific implementations to let you
50-- | This structure is used by specific implementations to let you
50-- customize the output
51-- customize the output
51data CommonGeneratorOptions = CommonGeneratorOptions
52data CommonGeneratorOptions = CommonGeneratorOptions
52 {
53 {
53− functionNameBuilder :: FunctionName -> String -- ^ function generating function names
54+ functionNameBuilder :: FunctionName -> Text -- ^ function generating function names
54− , requestBody :: String -- ^ name used when a user want to send the request body (to let you redefine it)
55+ , requestBody :: Text -- ^ name used when a user want to send the request body (to let you redefine it)
55− , successCallback :: String -- ^ name of the callback parameter when the request was successful
56+ , successCallback :: Text -- ^ name of the callback parameter when the request was successful
56− , errorCallback :: String -- ^ name of the callback parameter when the request reported an error
57+ , errorCallback :: Text -- ^ name of the callback parameter when the request reported an error
57− , moduleName :: String -- ^ namespace on which we define the foreign function (empty mean local var)
58+ , moduleName :: Text -- ^ namespace on which we define the foreign function (empty mean local var)
58− , urlPrefix :: String -- ^ a prefix we should add to the Url in the codegen
59+ , urlPrefix :: Text -- ^ a prefix we should add to the Url in the codegen
59 }
60 }
60
61
61-- | Default options.
62-- | Default options.
@@ -87,7 +88,34 @@ defCommonGeneratorOptions = CommonGeneratorOptions
87-- Couldn't work out how to handle zero-width characters.
88-- Couldn't work out how to handle zero-width characters.
88--
89--
89-- @TODO: specify better default function name, or throw error?
90-- @TODO: specify better default function name, or throw error?
90−toValidFunctionName :: String -> String
91+toValidFunctionName :: Text -> Text
92+-- @TODO: Cons text
93+--
94+
95+toValidFunctionName t =
96+ case T.uncons t of
97+ Just (x,xs) ->
98+ setFirstChar x `T.cons` T.filter remainder xs
99+ Nothing -> "_"
100+ where
101+ setFirstChar c = if firstChar c then c else '_'
102+ firstChar c = prefixOK c || any (Set.member c) firstLetterOK
103+ remainder c = prefixOK c || any (Set.member c) remainderOK
104+ -- Valid prefixes
105+ prefixOK c = c `elem` ['$','_']
106+ -- Unicode character sets
107+ firstLetterOK = [ Set.lowercaseLetter
108+ , Set.uppercaseLetter
109+ , Set.titlecaseLetter
110+ , Set.modifierLetter
111+ , Set.otherLetter
112+ , Set.letterNumber ]
113+ remainderOK = firstLetterOK
114+ <> [ Set.nonSpacingMark
115+ , Set.spacingCombiningMark
116+ , Set.decimalNumber
117+ , Set.connectorPunctuation ]
118+{-
91toValidFunctionName (x:xs) = [setFirstChar x] <> filter remainder xs
119toValidFunctionName (x:xs) = [setFirstChar x] <> filter remainder xs
92 where
120 where
93 setFirstChar c = if firstChar c then c else '_'
121 setFirstChar c = if firstChar c then c else '_'
@@ -108,59 +136,56 @@ toValidFunctionName (x:xs) = [setFirstChar x] <> filter remainder xs
108 , Set.decimalNumber
136 , Set.decimalNumber
109 , Set.connectorPunctuation ]
137 , Set.connectorPunctuation ]
110toValidFunctionName [] = "_"
138toValidFunctionName [] = "_"
139+-}
111
140
112−toJSHeader :: HeaderArg -> String
141+toJSHeader :: HeaderArg -> Text
113toJSHeader (HeaderArg n) = toValidFunctionName ("header" <> n)
142toJSHeader (HeaderArg n) = toValidFunctionName ("header" <> n)
114toJSHeader (ReplaceHeaderArg n p)
143toJSHeader (ReplaceHeaderArg n p)
115− | pn `isPrefixOf` p = pv <> " + \"" <> rp <> "\""
144+ | pn `T.isPrefixOf` p = pv <> " + \"" <> rp <> "\""
116− | pn `isSuffixOf` p = "\"" <> rp <> "\" + " <> pv
145+ | pn `T.isSuffixOf` p = "\"" <> rp <> "\" + " <> pv
117− | pn `isInfixOf` p = "\"" <> (replace pn ("\" + " <> pv <> " + \"") p)
146+ | pn `T.isInfixOf` p = "\"" <> (T.replace pn ("\" + " <> pv <> " + \"") p)
118 <> "\""
147 <> "\""
119 | otherwise = p
148 | otherwise = p
120 where
149 where
121 pv = toValidFunctionName ("header" <> n)
150 pv = toValidFunctionName ("header" <> n)
122 pn = "{" <> n <> "}"
151 pn = "{" <> n <> "}"
123− rp = replace pn "" p
152+ rp = T.replace pn "" p
124− -- Use replace method from Data.Text
125− replace old new = T.unpack
126− . T.replace (T.pack old) (T.pack new)
127− . T.pack
128
153
129−jsSegments :: [Segment] -> String
154+jsSegments :: [Segment] -> Text
130jsSegments [] = ""
155jsSegments [] = ""
131−jsSegments [x] = "/" ++ segmentToStr x False
156+jsSegments [x] = "/" <> segmentToStr x False
132−jsSegments (x:xs) = "/" ++ segmentToStr x True ++ jsSegments xs
157+jsSegments (x:xs) = "/" <> segmentToStr x True <> jsSegments xs
133
158
134−segmentToStr :: Segment -> Bool -> String
159+segmentToStr :: Segment -> Bool -> Text
135segmentToStr (Segment st ms) notTheEnd =
160segmentToStr (Segment st ms) notTheEnd =
136− segmentTypeToStr st ++ jsMParams ms ++ if notTheEnd then "" else "'"
161+ segmentTypeToStr st <> jsMParams ms <> if notTheEnd then "" else "'"
137
162
138−segmentTypeToStr :: SegmentType -> String
163+segmentTypeToStr :: SegmentType -> Text
139segmentTypeToStr (Static s) = s
164segmentTypeToStr (Static s) = s
140−segmentTypeToStr (Cap s) = "' + encodeURIComponent(" ++ s ++ ") + '"
165+segmentTypeToStr (Cap s) = "' + encodeURIComponent(" <> s <> ") + '"
141
166
142−jsGParams :: String -> [QueryArg] -> String
167+jsGParams :: Text -> [QueryArg] -> Text
143jsGParams _ [] = ""
168jsGParams _ [] = ""
144jsGParams _ [x] = paramToStr x False
169jsGParams _ [x] = paramToStr x False
145−jsGParams s (x:xs) = paramToStr x True ++ s ++ jsGParams s xs
170+jsGParams s (x:xs) = paramToStr x True <> s <> jsGParams s xs
146
171
147−jsParams :: [QueryArg] -> String
172+jsParams :: [QueryArg] -> Text
148jsParams = jsGParams "&"
173jsParams = jsGParams "&"
149
174
150−jsMParams :: [MatrixArg] -> String
175+jsMParams :: [MatrixArg] -> Text
151jsMParams [] = ""
176jsMParams [] = ""
152−jsMParams xs = ";" ++ jsGParams ";" xs
177+jsMParams xs = ";" <> jsGParams ";" xs
153
178
154−paramToStr :: QueryArg -> Bool -> String
179+paramToStr :: QueryArg -> Bool -> Text
155paramToStr qarg notTheEnd =
180paramToStr qarg notTheEnd =
156 case qarg ^. argType of
181 case qarg ^. argType of
157 Normal -> name
182 Normal -> name
158− ++ "=' + encodeURIComponent("
183+ <> "=' + encodeURIComponent("
159− ++ name
184+ <> name
160− ++ if notTheEnd then ") + '" else ")"
185+ <> if notTheEnd then ") + '" else ")"
161− Flag -> name ++ "="
186+ Flag -> name <> "="
162 List -> name
187 List -> name
163− ++ "[]=' + encodeURIComponent("
188+ <> "[]=' + encodeURIComponent("
164− ++ name
189+ <> name
165− ++ if notTheEnd then ") + '" else ")"
190+ <> if notTheEnd then ") + '" else ")"
166 where name = qarg ^. argName
191 where name = qarg ^. argName
servant-js/src/Servant/JS/JQuery.hs+18−16
@@ -1,8 +1,10 @@
1+{-#LANGUAGE OverloadedStrings #-}
1module Servant.JS.JQuery where
2module Servant.JS.JQuery where
2
3
3import Control.Lens
4import Control.Lens
4−import Data.List
5import Data.Monoid
5import Data.Monoid
6+import qualified Data.Text as T
7+import Data.Text (Text)
6import Servant.Foreign
8import Servant.Foreign
7import Servant.JS.Internal
9import Servant.JS.Internal
8
10
@@ -10,19 +12,19 @@ import Servant.JS.Internal
10-- to make the AJAX calls. Uses 'defCommonGeneratorOptions'
12-- to make the AJAX calls. Uses 'defCommonGeneratorOptions'
11-- for the generator options.
13-- for the generator options.
12jquery :: JavaScriptGenerator
14jquery :: JavaScriptGenerator
13−jquery = concatMap generateJQueryJS
15+jquery = mconcat . map generateJQueryJS
14
16
15-- | Generate javascript functions that use the /jQuery/ library
17-- | Generate javascript functions that use the /jQuery/ library
16-- to make the AJAX calls. Lets you specify your own 'CommonGeneratorOptions'.
18-- to make the AJAX calls. Lets you specify your own 'CommonGeneratorOptions'.
17jqueryWith :: CommonGeneratorOptions -> JavaScriptGenerator
19jqueryWith :: CommonGeneratorOptions -> JavaScriptGenerator
18−jqueryWith opts = concatMap (generateJQueryJSWith opts)
20+jqueryWith opts = mconcat . map (generateJQueryJSWith opts)
19
21
20-- | js codegen using JQuery using default options
22-- | js codegen using JQuery using default options
21−generateJQueryJS :: AjaxReq -> String
23+generateJQueryJS :: AjaxReq -> Text
22generateJQueryJS = generateJQueryJSWith defCommonGeneratorOptions
24generateJQueryJS = generateJQueryJSWith defCommonGeneratorOptions
23
25
24-- | js codegen using JQuery
26-- | js codegen using JQuery
25−generateJQueryJSWith :: CommonGeneratorOptions -> AjaxReq -> String
27+generateJQueryJSWith :: CommonGeneratorOptions -> AjaxReq -> Text
26generateJQueryJSWith opts req = "\n" <>
28generateJQueryJSWith opts req = "\n" <>
27 fname <> " = function(" <> argsStr <> ")\n"
29 fname <> " = function(" <> argsStr <> ")\n"
28 <> "{\n"
30 <> "{\n"
@@ -36,7 +38,7 @@ generateJQueryJSWith opts req = "\n" <>
36 <> " });\n"
38 <> " });\n"
37 <> "}\n"
39 <> "}\n"
38
40
39− where argsStr = intercalate ", " args
41+ where argsStr = T.intercalate ", " args
40 args = captures
42 args = captures
41 ++ map (view argName) queryparams
43 ++ map (view argName) queryparams
42 ++ body
44 ++ body
@@ -67,14 +69,14 @@ generateJQueryJSWith opts req = "\n" <>
67 reqheaders =
69 reqheaders =
68 if null hs
70 if null hs
69 then ""
71 then ""
70− else " , headers: { " ++ headersStr ++ " }\n"
72+ else " , headers: { " <> headersStr <> " }\n"
71
73
72− where headersStr = intercalate ", " $ map headerStr hs
74+ where headersStr = T.intercalate ", " $ map headerStr hs
73− headerStr header = "\"" ++
75+ headerStr header = "\"" <>
74− headerArgName header ++
76+ headerArgName header <>
75− "\": " ++ toJSHeader header
77+ "\": " <> toJSHeader header
76
78
77− namespace = if null (moduleName opts)
79+ namespace = if (moduleName opts) == ""
78 then "var "
80 then "var "
79 else (moduleName opts) <> "."
81 else (moduleName opts) <> "."
80 fname = namespace <> (functionNameBuilder opts $ req ^. funcName)
82 fname = namespace <> (functionNameBuilder opts $ req ^. funcName)
@@ -82,13 +84,13 @@ generateJQueryJSWith opts req = "\n" <>
82 method = req ^. reqMethod
84 method = req ^. reqMethod
83 url = if url' == "'" then "'/'" else url'
85 url = if url' == "'" then "'/'" else url'
84 url' = "'"
86 url' = "'"
85− ++ urlPrefix opts
87+ <> urlPrefix opts
86− ++ urlArgs
88+ <> urlArgs
87− ++ queryArgs
89+ <> queryArgs
88
90
89 urlArgs = jsSegments
91 urlArgs = jsSegments
90 $ req ^.. reqUrl.path.traverse
92 $ req ^.. reqUrl.path.traverse
91
93
92 queryArgs = if null queryparams
94 queryArgs = if null queryparams
93 then ""
95 then ""
94− else " + '?" ++ jsParams queryparams
96+ else " + '?" <> jsParams queryparams
servant-js/src/Servant/JS/Vanilla.hs+18−16
@@ -1,7 +1,9 @@
1+{-#LANGUAGE OverloadedStrings #-}
1module Servant.JS.Vanilla where
2module Servant.JS.Vanilla where
2
3
3import Control.Lens
4import Control.Lens
4−import Data.List
5+import Data.Text (Text)
6+import qualified Data.Text as T
5import Data.Monoid
7import Data.Monoid
6import Servant.Foreign
8import Servant.Foreign
7import Servant.JS.Internal
9import Servant.JS.Internal
@@ -10,20 +12,20 @@ import Servant.JS.Internal
10-- to your API, using /XMLHttpRequest/. Uses 'defCommonGeneratorOptions'
12-- to your API, using /XMLHttpRequest/. Uses 'defCommonGeneratorOptions'
11-- for the 'CommonGeneratorOptions'.
13-- for the 'CommonGeneratorOptions'.
12vanillaJS :: JavaScriptGenerator
14vanillaJS :: JavaScriptGenerator
13−vanillaJS = concatMap generateVanillaJS
15+vanillaJS = mconcat . map generateVanillaJS
14
16
15-- | Generate vanilla javascript functions to make AJAX requests
17-- | Generate vanilla javascript functions to make AJAX requests
16-- to your API, using /XMLHttpRequest/. Lets you specify your
18-- to your API, using /XMLHttpRequest/. Lets you specify your
17-- own options.
19-- own options.
18vanillaJSWith :: CommonGeneratorOptions -> JavaScriptGenerator
20vanillaJSWith :: CommonGeneratorOptions -> JavaScriptGenerator
19−vanillaJSWith opts = concatMap (generateVanillaJSWith opts)
21+vanillaJSWith opts = mconcat . map (generateVanillaJSWith opts)
20
22
21-- | js codegen using XmlHttpRequest using default generation options
23-- | js codegen using XmlHttpRequest using default generation options
22−generateVanillaJS :: AjaxReq -> String
24+generateVanillaJS :: AjaxReq -> Text
23generateVanillaJS = generateVanillaJSWith defCommonGeneratorOptions
25generateVanillaJS = generateVanillaJSWith defCommonGeneratorOptions
24
26
25-- | js codegen using XmlHttpRequest
27-- | js codegen using XmlHttpRequest
26−generateVanillaJSWith :: CommonGeneratorOptions -> AjaxReq -> String
28+generateVanillaJSWith :: CommonGeneratorOptions -> AjaxReq -> Text
27generateVanillaJSWith opts req = "\n" <>
29generateVanillaJSWith opts req = "\n" <>
28 fname <> " = function(" <> argsStr <> ")\n"
30 fname <> " = function(" <> argsStr <> ")\n"
29 <> "{\n"
31 <> "{\n"
@@ -43,7 +45,7 @@ generateVanillaJSWith opts req = "\n" <>
43 <> " xhr.send(" <> dataBody <> ");\n"
45 <> " xhr.send(" <> dataBody <> ");\n"
44 <> "}\n"
46 <> "}\n"
45
47
46− where argsStr = intercalate ", " args
48+ where argsStr = T.intercalate ", " args
47 args = captures
49 args = captures
48 ++ map (view argName) queryparams
50 ++ map (view argName) queryparams
49 ++ body
51 ++ body
@@ -74,14 +76,14 @@ generateVanillaJSWith opts req = "\n" <>
74 reqheaders =
76 reqheaders =
75 if null hs
77 if null hs
76 then ""
78 then ""
77− else headersStr ++ "\n"
79+ else headersStr <> "\n"
78
80
79− where headersStr = intercalate "\n" $ map headerStr hs
81+ where headersStr = T.intercalate "\n" $ map headerStr hs
80− headerStr header = " xhr.setRequestHeader(\"" ++
82+ headerStr header = " xhr.setRequestHeader(\"" <>
81− headerArgName header ++
83+ headerArgName header <>
82− "\", " ++ toJSHeader header ++ ");"
84+ "\", " <> toJSHeader header <> ");"
83
85
84− namespace = if null (moduleName opts)
86+ namespace = if moduleName opts == ""
85 then "var "
87 then "var "
86 else (moduleName opts) <> "."
88 else (moduleName opts) <> "."
87 fname = namespace <> (functionNameBuilder opts $ req ^. funcName)
89 fname = namespace <> (functionNameBuilder opts $ req ^. funcName)
@@ -89,13 +91,13 @@ generateVanillaJSWith opts req = "\n" <>
89 method = req ^. reqMethod
91 method = req ^. reqMethod
90 url = if url' == "'" then "'/'" else url'
92 url = if url' == "'" then "'/'" else url'
91 url' = "'"
93 url' = "'"
92− ++ urlPrefix opts
94+ <> urlPrefix opts
93− ++ urlArgs
95+ <> urlArgs
94− ++ queryArgs
96+ <> queryArgs
95
97
96 urlArgs = jsSegments
98 urlArgs = jsSegments
97 $ req ^.. reqUrl.path.traverse
99 $ req ^.. reqUrl.path.traverse
98
100
99 queryArgs = if null queryparams
101 queryArgs = if null queryparams
100 then ""
102 then ""
101− else " + '?" ++ jsParams queryparams
103+ else " + '?" <> jsParams queryparams