{-# 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: <https://www.yesodweb.com/book/shakespearean-templates>
--
-- 2. TypeScript: <https://www.typescriptlang.org/>
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
    , tscJSX
    , typeScriptFile
    , typeScriptJSXFile
    , typeScriptFileReload
    , typeScriptJSXFileReload

#ifdef TEST_EXPORT
    , typeScriptSettings
    , typeScriptJSXSettings
#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 :: Q ShakespeareSettings
typeScriptSettings = do
  ShakespeareSettings
jsettings <- Q ShakespeareSettings
javascriptSettings
  forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ ShakespeareSettings
jsettings { varChar :: Char
varChar = Char
'#'
  , preConversion :: Maybe PreConvert
preConversion = forall a. a -> Maybe a
Just PreConvert {
      preConvert :: PreConversion
preConvert = String -> [String] -> PreConversion
ReadProcess String
"sh" [String
"-c", String
"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 :: String
preEscapeIgnoreBalanced = String
"'\""
    , preEscapeIgnoreLine :: String
preEscapeIgnoreLine = String
"//"
    , wrapInsertion :: Maybe WrapInsertion
wrapInsertion = forall a. a -> Maybe a
Just WrapInsertion { 
        wrapInsertionIndent :: Maybe String
wrapInsertionIndent = forall a. Maybe a
Nothing
      , wrapInsertionStartBegin :: String
wrapInsertionStartBegin = String
";(function("
      , wrapInsertionSeparator :: String
wrapInsertionSeparator = String
", "
      , wrapInsertionStartClose :: String
wrapInsertionStartClose = String
"){"
      , wrapInsertionEnd :: String
wrapInsertionEnd = String
"})"
      , wrapInsertionAddParens :: Bool
wrapInsertionAddParens = Bool
False
      }
    }
  }


-- | Identical to 'typeScriptSettings' but uses jsx when compiling TypeScript
typeScriptJSXSettings :: Q ShakespeareSettings
typeScriptJSXSettings :: Q ShakespeareSettings
typeScriptJSXSettings = do
    ShakespeareSettings
tsSettings <- Q ShakespeareSettings
typeScriptSettings
    let rp :: PreConversion
rp = String -> [String] -> PreConversion
ReadProcess String
"sh" [String
"-c", String
"TMP_IN=$(mktemp XXXXXXXXXX.tsx); TMP_OUT=$(mktemp XXXXXXXXXX.js); cat /dev/stdin > ${TMP_IN} && tsc --module amd --jsx react --out ${TMP_OUT} ${TMP_IN} && cat ${TMP_OUT}; rm ${TMP_IN} && rm ${TMP_OUT}"]
    forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ ShakespeareSettings
tsSettings {
        preConversion :: Maybe PreConvert
preConversion = forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (\PreConvert
pc -> PreConvert
pc { preConvert :: PreConversion
preConvert = PreConversion
rp }) (ShakespeareSettings -> Maybe PreConvert
preConversion ShakespeareSettings
tsSettings)
    }


-- | Read inline, quasiquoted TypeScript
tsc :: QuasiQuoter
tsc :: QuasiQuoter
tsc = QuasiQuoter { quoteExp :: String -> Q Exp
quoteExp = \String
s -> do
    ShakespeareSettings
rs <- Q ShakespeareSettings
typeScriptSettings
    QuasiQuoter -> String -> Q Exp
quoteExp (ShakespeareSettings -> QuasiQuoter
shakespeare ShakespeareSettings
rs) String
s
    }

-- | Read inline, quasiquoted TypeScript with jsx
tscJSX :: QuasiQuoter
tscJSX :: QuasiQuoter
tscJSX = QuasiQuoter { quoteExp :: String -> Q Exp
quoteExp = \String
s -> do
    ShakespeareSettings
rs <- Q ShakespeareSettings
typeScriptJSXSettings
    QuasiQuoter -> String -> Q Exp
quoteExp (ShakespeareSettings -> QuasiQuoter
shakespeare ShakespeareSettings
rs) String
s
    }

-- | Read in a TypeScript template file. This function reads the file once, at
-- compile time.
typeScriptFile :: FilePath -> Q Exp
typeScriptFile :: String -> Q Exp
typeScriptFile String
fp = do
    ShakespeareSettings
rs <- Q ShakespeareSettings
typeScriptSettings
    ShakespeareSettings -> String -> Q Exp
shakespeareFile ShakespeareSettings
rs String
fp

-- | Read in a TypeScript template file with jsx. This function reads the file
-- once, at compile time.
typeScriptJSXFile :: FilePath -> Q Exp
typeScriptJSXFile :: String -> Q Exp
typeScriptJSXFile String
fp = do
    ShakespeareSettings
rs <- Q ShakespeareSettings
typeScriptJSXSettings
    ShakespeareSettings -> String -> Q Exp
shakespeareFile ShakespeareSettings
rs String
fp



-- | Read in a TypeScript template file. This impure function uses
-- unsafePerformIO to re-read the file on every call, allowing for rapid
-- iteration.
typeScriptFileReload :: FilePath -> Q Exp
typeScriptFileReload :: String -> Q Exp
typeScriptFileReload String
fp = do
    ShakespeareSettings
rs <- Q ShakespeareSettings
typeScriptSettings
    ShakespeareSettings -> String -> Q Exp
shakespeareFileReload ShakespeareSettings
rs String
fp


-- | Read in a TypeScript with jsx template file. This impure function uses
-- unsafePerformIO to re-read the file on every call, allowing for rapid
-- iteration.
typeScriptJSXFileReload :: FilePath -> Q Exp
typeScriptJSXFileReload :: String -> Q Exp
typeScriptJSXFileReload String
fp = do
    ShakespeareSettings
rs <- Q ShakespeareSettings
typeScriptJSXSettings
    ShakespeareSettings -> String -> Q Exp
shakespeareFileReload ShakespeareSettings
rs String
fp