shakespeare-js 1.1.1 → 1.1.2
raw patch · 7 files changed
+231/−43 lines, 7 filesdep −shakespeare-jsdep ~shakespearePVP ok
version bump matches the API change (PVP)
Dependencies removed: shakespeare-js
Dependency ranges changed: shakespeare
API changes (from Hackage documentation)
+ Text.TypeScript: tsc :: QuasiQuoter
+ Text.TypeScript: typeScriptFile :: FilePath -> Q Exp
+ Text.TypeScript: typeScriptFileReload :: FilePath -> Q Exp
Files
- Text/Coffee.hs +40/−14
- Text/Julius.hs +1/−2
- Text/Roy.hs +17/−14
- Text/TypeScript.hs +117/−0
- shakespeare-js.cabal +22/−9
- test/Quoter.hs +10/−0
- test/ShakespeareJsTest.hs +24/−4
Text/Coffee.hs view
@@ -1,4 +1,3 @@-{-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE CPP #-}@@ -14,6 +13,34 @@ -- CoffeeScript already uses that sequence for string interpolation. Therefore, -- Shakespearean interpolation is introduced with @%{...}@. --+-- If you interpolate variables,+-- the template is first wrapped with a function containing javascript variables representing shakespeare variables,+-- then compiled with @coffee@,+-- and then the value of the variables are applied to the function.+-- This means that in production the template can be compiled+-- once at compile time and there will be no dependency in your production+-- system on @coffee@. +--+-- Your code:+--+-- > b = 1+-- > console.log(#{a} + b)+--+-- Function wrapper added to your coffeescript code:+--+-- > ((shakespeare_var_a) =>+-- > b = 1+-- > console.log(shakespeare_var_a + b)+-- > )+--+-- This is then compiled down to javascript, and the variables are applied:+--+-- > ;(function(shakespeare_var_a){+-- > var b = 1;+-- > console.log(shakespeare_var_a + b);+-- > })(#{a});+--+-- -- Further reading: -- -- 1. Shakespearean templates: <http://www.yesodweb.com/book/templates>@@ -39,24 +66,23 @@ import Text.Shakespeare import Text.Julius --- | The Coffeescript language compiles down to Javascript.--- We do this compilation once at compile time to avoid needing to do it during the request.--- We call this a preConversion because other shakespeare modules like Lucius use Haskell to compile during the request rather than a system call.--- During the pre-conversion we first modify all Haskell insertions--- so that they will be ignored by the Coffeescript compiler (backticks).--- So %{var} is change to `%{var}` using the preEscapeBegin and preEscapeEnd.--- preEscapeIgnore is used to not insert backtacks for variable already inside strings or backticks.--- coffeescript will happily ignore the interpolations, and backticks would not be treated as escaping in that context. coffeeSettings :: Q ShakespeareSettings coffeeSettings = do jsettings <- javascriptSettings return $ jsettings { varChar = '%' , preConversion = Just PreConvert {- preConvert = ReadProcess "coffee" ["-sp"]- , preEscapeBegin = "`"- , preEscapeEnd = "`"- , preEscapeIgnoreBalanced = "'\"`"- , preEscapeIgnoreLine = "#"+ preConvert = ReadProcess "coffee" ["-spb"]+ , preEscapeIgnoreBalanced = "'\"`" -- don't insert backtacks for variable already inside strings or backticks.+ , preEscapeIgnoreLine = "#" -- ignore commented lines+ , wrapInsertion = Just WrapInsertion { + wrapInsertionIndent = Just " "+ , wrapInsertionStartBegin = "(("+ , wrapInsertionSeparator = ", "+ , wrapInsertionStartClose = ") =>"+ , wrapInsertionEnd = ")"+ , wrapInsertionApplyBegin = "("+ , wrapInsertionApplyClose = ")\n"+ } } }
Text/Julius.hs view
@@ -5,9 +5,8 @@ {-# OPTIONS_GHC -fno-warn-missing-fields #-} -- | A Shakespearean module for Javascript templates, introducing type-safe, -- compile-time variable and url interpolation.----- To use this module, @coffee@ must be installed on your system. ----- You might consider trying 'Text.Coffee', which compiles down to Javascript.+-- You might consider trying 'Text.Typescript' or 'Text.Coffee' which compile down to Javascript. -- -- Further reading: <http://www.yesodweb.com/book/templates> module Text.Julius
Text/Roy.hs view
@@ -1,4 +1,3 @@-{-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE CPP #-}@@ -10,15 +9,9 @@ -- -- To use this module, @roy@ must be installed on your system. ----- @#{...}@ is the Shakespearean standard for variable interpolation, but--- CoffeeScript already uses that sequence for string interpolation.--- Therefore, it seems more future-proof to use @%{...}@ for interpolation------ Integration with Roy is a bit rough right now.--- You can only perorm a shakespeare insertion inside a Roy string.--- This should work well for urls and strings.--- Otherwise you should stick your Haskell into Julius as a window variable,--- and then retrieve it in your Roy code.+-- Unfortunately variable interpolation in Roy does not currently work,+-- but it can with a small change to Roy:+-- <https://github.com/pufuwozu/roy/issues/165> -- -- Further reading: --@@ -50,13 +43,23 @@ roySettings :: Q ShakespeareSettings roySettings = do jsettings <- javascriptSettings- return $ jsettings { varChar = '%'+ return $ jsettings { varChar = '#' , preConversion = Just PreConvert { preConvert = ReadProcess "roy" ["--stdio"]- , preEscapeBegin = "`"- , preEscapeEnd = "`"- , preEscapeIgnoreBalanced = "'\"`"+ , preEscapeIgnoreBalanced = "'\"" , preEscapeIgnoreLine = "//"+ , wrapInsertion = Nothing+ {-+ Just WrapInsertion { + wrapInsertionIndent = Just " "+ , wrapInsertionStartBegin = "(\\"+ , wrapInsertionSeparator = " "+ , wrapInsertionStartClose = " ->\n"+ , wrapInsertionEnd = ")"+ , wrapInsertionApplyBegin = " "+ , wrapInsertionApplyClose = ")\n"+ }+ -} } }
+ Text/TypeScript.hs view
@@ -0,0 +1,117 @@+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE CPP #-}+{-# OPTIONS_GHC -fno-warn-missing-fields #-}+-- | A Shakespearean module for TypeScript, introducing type-safe,+-- compile-time variable and url interpolation. It is exactly the same as+-- "Text.Julius", except that the template is first compiled to Javascript with+-- the system tool @tsc@.+--+-- To use this module, @tsc@ must be installed on your system.+--+-- If you interpolate variables,+-- the template is first wrapped with a function containing javascript variables representing shakespeare variables,+-- then compiled with @tsc@,+-- and then the value of the variables are applied to the function.+-- This means that in production the template can be compiled+-- once at compile time and there will be no dependency in your production+-- system on @tsc@. +--+-- Your code:+--+-- > var b = 1+-- > console.log(#{a} + b)+--+-- Final Result:+--+-- > ;(function(shakespeare_var_a){+-- > var b = 1;+-- > console.log(shakespeare_var_a + b);+-- > })(#{a});+--+--+-- Important Warnings! This integration is not ideal.+--+-- Due to the function wrapper, all type declarations must be in separate .d.ts files.+-- However, if you don't interpolate variables, no function wrapper will be+-- created, and you can make type declarations in the same file.+--+-- This does not work cross-platform!+--+-- Unfortunately tsc does not support stdin and stdout.+-- So a hack of writing to temporary files using the mktemp+-- command is used. This works on my version of Linux, but not for windows+-- unless perhaps you install a mktemp utility, which I have not tested.+-- Please vote up this bug: <http://typescript.codeplex.com/workitem/600>+--+-- Making this work on Windows would not be very difficult, it will just require a new+-- package with a dependency on a package like temporary.+--+-- Further reading:+--+-- 1. Shakespearean templates: <http://www.yesodweb.com/book/templates>+--+-- 2. TypeScript: <http://typescript.codeplex.com/>+module Text.TypeScript+ ( -- * Functions+ -- ** Template-Reading Functions+ -- | These QuasiQuoter and Template Haskell methods return values of+ -- type @'JavascriptUrl' url@. See the Yesod book for details.+ tsc+ , typeScriptFile+ , typeScriptFileReload++#ifdef TEST_EXPORT+ , typeScriptSettings+#endif+ ) where++import Language.Haskell.TH.Quote (QuasiQuoter (..))+import Language.Haskell.TH.Syntax+import Text.Shakespeare+import Text.Julius++-- | The TypeScript language compiles down to Javascript.+-- We do this compilation once at compile time to avoid needing to do it during the request.+-- We call this a preConversion because other shakespeare modules like Lucius use Haskell to compile during the request instead rather than a system call.+typeScriptSettings :: Q ShakespeareSettings+typeScriptSettings = do+ jsettings <- javascriptSettings+ return $ jsettings { varChar = '#'+ , preConversion = Just PreConvert {+ preConvert = ReadProcess "sh" ["-c", "TMP_IN=$(mktemp XXXXXXXXXX.ts); TMP_OUT=$(mktemp XXXXXXXXXX.js); cat /dev/stdin > ${TMP_IN} && tsc --out ${TMP_OUT} ${TMP_IN} && cat ${TMP_OUT}; rm ${TMP_IN} && rm ${TMP_OUT}"]+ , preEscapeIgnoreBalanced = "'\""+ , preEscapeIgnoreLine = "//"+ , wrapInsertion = Just WrapInsertion { + wrapInsertionIndent = Nothing+ , wrapInsertionStartBegin = ";(function("+ , wrapInsertionSeparator = ", "+ , wrapInsertionStartClose = "){"+ , wrapInsertionEnd = "})"+ , wrapInsertionApplyBegin = "("+ , wrapInsertionApplyClose = ");\n"+ }+ }+ }++-- | Read inline, quasiquoted TypeScript+tsc :: QuasiQuoter+tsc = QuasiQuoter { quoteExp = \s -> do+ rs <- typeScriptSettings+ quoteExp (shakespeare rs) s+ }++-- | Read in a Roy template file. This function reads the file once, at+-- compile time.+typeScriptFile :: FilePath -> Q Exp+typeScriptFile fp = do+ rs <- typeScriptSettings+ shakespeareFile rs fp++-- | Read in a Roy template file. This impure function uses+-- unsafePerformIO to re-read the file on every call, allowing for rapid+-- iteration.+typeScriptFileReload :: FilePath -> Q Exp+typeScriptFileReload fp = do+ rs <- typeScriptSettings+ shakespeareFileReload rs fp
shakespeare-js.cabal view
@@ -1,5 +1,5 @@ name: shakespeare-js-version: 1.1.1+version: 1.1.2 license: MIT license-file: LICENSE author: Michael Snoyman <michael@snoyman.com>@@ -9,11 +9,13 @@ Shakespeare is a template family for type-safe, efficient templates with simple variable interpolation . Shakespeare templates can be used inline with a quasi-quoter or in an external file. Shakespeare interpolates variables according to the type being inserted. In this case, the variable type needs a ToJavascript instance. .- There is also shakespeare-coffeescript for coffeescript templates. Coffescript is a language that compiles down to javascript. It expects a coffeescript compiler in your path, and variable should be a ToCoffee instance. And we even have a Roy template for the adventorous FP addicts.+ shakespeare-javascript is alson known as Julius, and passes through plain javascript .+ There is also a shakespeare version for CoffeeScript, TypeScript, and Roy, all languages that compile down to Javascript. They all expect you to have the appropriate compiler in your path.+ .+ shakespeare originated from the hamlet template package. Please see http://www.yesodweb.com/book/shakespearean-templates for a more thorough description and examples .- shakespeare-js was originally called julius, and shakespeare originated from the hamlet template package. extra-source-files: test/juliuses/external1.coffee test/juliuses/external1.julius@@ -38,6 +40,7 @@ Text.Julius Text.Coffee Text.Roy+ Text.TypeScript ghc-options: -Wall if impl(ghc >= 7.4) cpp-options: -DGHC_7_4@@ -45,6 +48,9 @@ if flag(test_coffee) cpp-options: -DTEST_COFFEE + if flag(test_roy)+ cpp-options: -DTEST_ROY+ if flag(test_export) cpp-options: -DTEST_EXPORT @@ -56,15 +62,20 @@ -- cabal configure --enable-tests -ftest_coffee && cabal build && dist/build/test/test default: False +flag test_roy+ description: render tests through coffeescript render function+ -- cabal configure --enable-tests -ftest_coffee && cabal build && dist/build/test/test+ default: False+ test-suite test- hs-source-dirs: test+ hs-source-dirs: test ./ main-is: ../test.hs other-modules: Quoter type: exitcode-stdio-1.0 ghc-options: -Wall- build-depends: shakespeare-js- , shakespeare+ build-depends:+ shakespeare , base , HUnit , hspec >= 1.3@@ -72,9 +83,11 @@ , template-haskell , aeson --- cabal bug--- if flag(test_coffee)--- cpp-options: -DTEST_COFFEE+ cpp-options: -DTEST_EXPORT+ if flag(test_coffee)+ cpp-options: -DTEST_COFFEE+ if flag(test_roy)+ cpp-options: -DTEST_ROY source-repository head
test/Quoter.hs view
@@ -10,7 +10,11 @@ import Text.Coffee (coffeeSettings) import Text.Shakespeare (shakespeare) #else+# ifdef TEST_ROY+import Text.Roy+# else import Text.Julius+# endif #endif quote :: QuasiQuoter@@ -29,7 +33,13 @@ quoteFile = coffeeFile quoteFileReload = coffeeFileReload #else+# ifdef TEST_ROY+quote = roy+quoteFile = royFile+quoteFileReload = royFileReload+# else quote = julius quoteFile = juliusFile quoteFileReload = juliusFileReload+# endif #endif
test/ShakespeareJsTest.hs view
@@ -29,6 +29,7 @@ specs :: Spec specs = describe "shakespeare-js" $ do +#if !(defined TEST_COFFEE || defined TEST_ROY) it "julius" $ do let var = "x=2" let urlp = (Home, [(pack "p", pack "q")]) @@ -63,6 +64,7 @@ , "url?p=q" , "f(2)" ] ++ "\n" +#endif {- TODO it "juliusFileDebugChange" $ do @@ -80,16 +82,20 @@ let foo = "foo" double = 3.14 :: Double int = -5 :: Int - in jelper "[oof, oof, 3.14, -5]" #ifdef TEST_COFFEE - [quote|[%{Data.List.reverse foo}, %{L.reverse foo}, %{show double}, %{show int}]|] + in jelper "var _this = this;\n\n(function(shakespeare_var_rawJSDataListreversefoo, shakespeare_var_rawJSLreversefoo, shakespeare_var_rawJSshowdouble, shakespeare_var_rawJSshowint) {\n return [shakespeare_var_rawJSDataListreversefoo, shakespeare_var_rawJSLreversefoo, shakespeare_var_rawJSshowdouble, shakespeare_var_rawJSshowint];\n})(oof, oof, 3.14, -5)" #else - [quote|[#{rawJS $ Data.List.reverse foo}, #{rawJS $ L.reverse foo}, #{rawJS $ show double}, #{rawJS $ show int}]|] +# ifdef TEST_ROY + in jelper "(function(shakespeare_var_rawJSDataListreversefoo, shakespeare_var_rawJSLreversefoo, shakespeare_var_rawJSshowdouble, shakespeare_var_rawJSshowint) {\n return [shakespeare_var_rawJSDataListreversefoo, shakespeare_var_rawJSLreversefoo, shakespeare_var_rawJSshowdouble, shakespeare_var_rawJSshowint];\n})(oof, oof, 3.14, -5)" +# else + in jelper "[oof, oof, 3.14, -5]" +# endif #endif + [quote|[#{rawJS $ Data.List.reverse foo}, #{rawJS $ L.reverse foo}, #{rawJS $ show double}, #{rawJS $ show int}]|] -- not valid coffeescript -#ifndef TEST_COFFEE +#if !(defined TEST_COFFEE || defined TEST_ROY) it "single dollar at and caret" $ do jelper "$@^" [quote|$@^|] jelper "#{@{^{" [quote|#\{@\{^\{|] @@ -97,8 +103,13 @@ it "dollar operator" $ do let val = (1 :: Int, (2 :: Int, 3 :: Int)) +#if !(defined TEST_COFFEE || defined TEST_ROY) jelper "2" [quote|#{ rawJS $ show $ fst $ snd val }|] jelper "2" [quote|#{ rawJS $ show $ fst $ snd $ val}|] +#else + jelper "var _this = this;\n\n(function(shakespeare_var_rawJSshowfstsndval) {\n return shakespeare_var_rawJSshowfstsndval;\n})(2)" [quote|#{ rawJS $ show $ fst $ snd val }|] + jelper "var _this = this;\n\n(function(shakespeare_var_rawJSshowfstsndval) {\n return shakespeare_var_rawJSshowfstsndval;\n})(2)" [quote|#{ rawJS $ show $ fst $ snd val }|] +#endif it "empty file" $ jelper "" [quote||] @@ -108,8 +119,15 @@ "true false true false true false" [julius|#{True} #{False} #{toJSON True} #{toJSON False} #{rawJS True} #{rawJS False}|] + it "^\\ should not be escaped" $ jelper + "var re = /[^\\r]/;" + [julius|var re = /[^\r]/;|] + it "^\\{ should be escaped" $ jelper + "var re = /[^{]/;" + [julius|var re = /[^\{]/;|] + data Url = Home | Sub SubUrl data SubUrl = SubUrl render :: Url -> [(Text, Text)] -> Text @@ -150,8 +168,10 @@ +#ifndef TEST_ROY jmixin :: JavascriptUrl u jmixin = [quote|f(2)|] +#endif jelper :: String -> JavascriptUrl Url -> Assertion jelper res h = do