124 lines
3.9 KiB
Haskell
124 lines
3.9 KiB
Haskell
{-# OPTIONS_GHC -Wall #-}
|
|
{-# LANGUAGE OverloadedStrings, DeriveDataTypeable #-}
|
|
module Main where
|
|
|
|
import Control.Applicative ((<$>),(<|>))
|
|
import Control.Monad (guard)
|
|
import Control.Monad.Trans (MonadIO(liftIO))
|
|
import qualified Data.ByteString as BS
|
|
import qualified Data.ByteString.Char8 as BSC
|
|
import qualified Data.HashMap.Strict as Map
|
|
import qualified Data.Version as Version
|
|
import System.Console.CmdArgs
|
|
import System.Directory
|
|
import System.Exit
|
|
import System.FilePath
|
|
import System.Process
|
|
import System.IO (hGetContents, Handle)
|
|
|
|
import Paths_elm_server (version)
|
|
import qualified Elm.Internal.Paths as Elm
|
|
import Snap.Core
|
|
import Snap.Http.Server
|
|
import Snap.Util.FileServe
|
|
|
|
data Flags = Flags
|
|
{ port :: Int
|
|
} deriving (Data,Typeable,Show,Eq)
|
|
|
|
flags :: Flags
|
|
flags = Flags
|
|
{ port = 8000 &= help "set the port of the server"
|
|
} &= help "Quickly reload Elm projects in your browser. Just refresh to recompile.\n\
|
|
\It serves static files and freshly recompiled Elm files."
|
|
&= helpArg [explicit, name "help", name "h"]
|
|
&= versionArg [ explicit, name "version", name "v"
|
|
, summary (Version.showVersion version)
|
|
]
|
|
&= summary ("Elm Server " ++ Version.showVersion version ++
|
|
", (c) Evan Czaplicki 2011-2014")
|
|
|
|
|
|
config :: Config Snap a
|
|
config = setAccessLog ConfigNoLog (setErrorLog ConfigNoLog defaultConfig)
|
|
|
|
-- | Set up the server.
|
|
main :: IO ()
|
|
main = do
|
|
cargs <- cmdArgs flags
|
|
putStrLn $ "Elm Server " ++ Version.showVersion version ++
|
|
": Just refresh a page to recompile it!"
|
|
httpServe (setPort (port cargs) config) $
|
|
serveRuntime
|
|
<|> serveElm
|
|
<|> serveDirectoryWith directoryConfig "."
|
|
|
|
directoryConfig :: MonadSnap m => DirectoryConfig m
|
|
directoryConfig =
|
|
fancyDirectoryConfig
|
|
{ indexGenerator = defaultIndexGenerator defaultMimeTypes indexStyle
|
|
, mimeTypes = Map.insert ".elm" "text/html" defaultMimeTypes
|
|
}
|
|
|
|
indexStyle :: BS.ByteString
|
|
indexStyle =
|
|
"body { margin:0; font-family:sans-serif; background:rgb(245,245,245);\
|
|
\ font-family: calibri, verdana, helvetica, arial; }\
|
|
\div.header { padding: 40px 50px; font-size: 24px; }\
|
|
\div.content { padding: 0 40px }\
|
|
\div.footer { display:none; }\
|
|
\table { width:100%; border-collapse:collapse; }\
|
|
\td { padding: 6px 10px; }\
|
|
\tr:nth-child(odd) { background:rgb(216,221,225); }\
|
|
\td { font-family:monospace }\
|
|
\th { background:rgb(90,99,120); color:white; text-align:left;\
|
|
\ padding:10px; font-weight:normal; }"
|
|
|
|
runtime :: String
|
|
runtime = "elm-runtime.js"
|
|
|
|
serveRuntime :: Snap ()
|
|
serveRuntime =
|
|
do file <- BSC.unpack . rqPathInfo <$> getRequest
|
|
guard (file == runtime)
|
|
serveFileAs "application/javascript" Elm.runtime
|
|
|
|
serveElm :: Snap ()
|
|
serveElm =
|
|
do file <- BSC.unpack . rqPathInfo <$> getRequest
|
|
exists <- liftIO $ doesFileExist file
|
|
guard (exists && takeExtension file == ".elm")
|
|
onSuccess (compile file) (serve file)
|
|
where
|
|
compile file =
|
|
let elmArgs = [ "--make", "--runtime=" ++ runtime, file ]
|
|
in createProcess $ (proc "elm" elmArgs) { std_out = CreatePipe }
|
|
|
|
serve file =
|
|
serveFileAs "text/html; charset=UTF-8" ("build" </> replaceExtension file "html")
|
|
|
|
failure :: String -> Snap ()
|
|
failure msg =
|
|
do modifyResponse $ setResponseStatus 404 "Not found"
|
|
writeBS $ BSC.pack msg
|
|
|
|
onSuccess :: IO (t, Maybe Handle, t1, ProcessHandle) -> Snap () -> Snap ()
|
|
onSuccess action success =
|
|
do (_, stdout, _, handle) <- liftIO action
|
|
exitCode <- liftIO $ waitForProcess handle
|
|
case (exitCode, stdout) of
|
|
(ExitFailure 127, _) ->
|
|
failure "Error: elm compiler not found in your path."
|
|
|
|
(ExitFailure _, Just out) ->
|
|
failure =<< liftIO (hGetContents out)
|
|
|
|
(ExitFailure _, Nothing) ->
|
|
failure "See command line for error message."
|
|
|
|
(ExitSuccess, _) -> success
|
|
|
|
{--
|
|
pageTitle :: String -> String
|
|
pageTitle = dropExtension . takeBaseName
|
|
--}
|