@@ -0,0 +1,238 @@
1+{-# LANGUAGE OverloadedStrings, QuasiQuotes #-}
8+import Control.Applicative ((<|>))
9+import Control.Concurrent (readMVar)
10+import Control.Monad (liftM2, msum)
11+import qualified Data.ByteString as B
12+import qualified Data.ByteString.Builder as B
13+import qualified Data.ByteString.Lazy as LBS
14+import qualified Data.HashSet as HashSet
15+import qualified Data.Map as Map
16+import qualified Data.OneOrMore as OneOrMore
17+import Network.URI (parseURI)
19+import Snap.Http.Server
21+import Snap.Util.FileServe (serveFile)
22+import Snap.Util.FileUploads
23+import qualified System.Directory as Dir
24+import qualified System.IO.Streams as Stream
25+import Text.RawString.QQ (r)
27+import qualified AST.Source as Src
28+import qualified AST.Canonical as Can
29+import qualified AST.Optimized as Opt
30+import qualified Compile
31+import qualified Elm.Details as Details
32+import qualified Elm.Interface as I
33+import qualified Elm.ModuleName as ModuleName
34+import qualified Elm.Package as Pkg
35+import qualified Generate.Html as Html
36+import qualified Generate.JavaScript as Generate
37+import qualified Generate.Mode as Mode
38+import qualified Json.Encode as Encode
39+import qualified Parse.Module as Parse
40+import qualified Reporting
41+import qualified Reporting.Error as Error
42+import qualified Reporting.Render.Code as Code
51+ do artifacts <- loadArtifacts
52+ httpServe config $ msum $
53+ [ path "compile" $ compile artifacts
54+ , path "compile/error.js" $ serveFile "error.js"
59+config :: Config Snap a
64+ # setAccessLog ConfigNoLog
65+ # setErrorLog ConfigNoLog
68+(#) :: a -> (a -> b) -> b
79+ do modifyResponse $ setResponseStatus 404 "Not Found"
80+ modifyResponse $ setContentType "text/html; charset=utf-8"
88+compile :: Artifacts -> Snap ()
89+compile (Artifacts ifaces objs) =
90+ applyCORS corsOptions $ method POST $
91+ do parts <- handleMultipart defaultUploadPolicy handlePart
94+ case compileByteString ifaces source of
96+ writeBuilder $ errorToHtmlBuilder source err
98+ Right (home, locals, main) ->
100+ mode = Mode.Dev Nothing
101+ name = ModuleName._module home
102+ mains = Map.singleton home main
103+ graph = Opt.addLocalGraph locals objs
105+ writeBuilder $ Html.sandwich name $ Generate.generate mode graph mains
111+handlePart :: PartInfo -> Stream.InputStream B.ByteString -> IO (Maybe B.ByteString)
112+handlePart info stream =
113+ if partFieldName info == "code" && partDisposition info == DispositionFormData
114+ then Just . LBS.toStrict <$> storeAsLazyByteString stream
118+compileByteString :: Map.Map ModuleName.Raw I.Interface -> B.ByteString -> Either Error.Error (ModuleName.Canonical, Opt.LocalGraph, Opt.Main)
119+compileByteString ifaces source =
120+ case Parse.fromByteString Pkg.dummyName source of
122+ Left (Error.BadSyntax err)
125+ case Compile.compile Pkg.dummyName ifaces modul of
129+ Right artifacts@(Compile.Artifacts modul _ locals@(Opt.LocalGraph maybeMain _ _)) ->
131+ Just main -> Right (Can._name modul, locals, main)
132+ Nothing -> Left (error "TODO no main")
135+errorToHtmlBuilder :: B.ByteString -> Error.Error -> B.Builder
136+errorToHtmlBuilder source err =
138+ json = Encode.encodeUgly (Error.errorToJson (Code.toSource source) err)
143+ <meta charset="UTF-8">
144+ <style>body { padding: 0; margin: 0; }</style>
145+ <script src="https://worker.elm-lang.org/compile/error.js"></script>
146+ <script>Elm.Error.init({flags:|] <> json <> [r|})</script>
158+ { _ifaces :: Map.Map ModuleName.Raw I.Interface
159+ , _graph :: Opt.GlobalGraph
163+loadArtifacts :: IO Artifacts
165+ do style <- Reporting.terminal
166+ root <- Dir.getCurrentDirectory
167+ result <- Details.load style root
170+ error "Ran into some problem loading elm.json details"
173+ do omvar <- Details.loadObjects root details
174+ imvar <- Details.loadInterfaces root details
175+ mdeps <- readMVar imvar
176+ mobjs <- readMVar omvar
177+ case liftM2 (,) mdeps mobjs of
179+ error "Ran into some weird problem loading elm.json details"
181+ Just (deps, objs) ->
182+ return $ Artifacts (toInterfaces deps) objs
185+toInterfaces :: Map.Map ModuleName.Canonical I.DependencyInterface -> Map.Map ModuleName.Raw I.Interface
187+ Map.mapMaybe toUnique $ Map.fromListWith OneOrMore.more $
188+ Map.elems (Map.mapMaybeWithKey getPublic deps)
191+getPublic :: ModuleName.Canonical -> I.DependencyInterface -> Maybe (ModuleName.Raw, OneOrMore.OneOrMore I.Interface)
192+getPublic (ModuleName.Canonical _ name) dep =
194+ I.Public iface -> Just (name, OneOrMore.one iface)
195+ I.Private _ _ _ -> Nothing
198+toUnique :: OneOrMore.OneOrMore a -> Maybe a
201+ OneOrMore.One value -> Just value
202+ OneOrMore.More _ _ -> Nothing
209+corsOptions :: (Monad m) => CORSOptions m
212+ { corsAllowOrigin = return allowedOrigins
213+ , corsAllowCredentials = return True
214+ , corsExposeHeaders = return HashSet.empty
215+ , corsAllowedMethods = return allowedMethods
216+ , corsAllowedHeaders = return
220+{-# NOINLINE allowedMethods #-}
221+allowedMethods :: HashSet.HashSet HashableMethod
223+ HashSet.singleton (HashableMethod POST)
226+{-# NOINLINE allowedOrigins #-}
227+allowedOrigins :: OriginList
231+ [ "https://elm-lang.org"
232+ , "https://package.elm-lang.org"
235+ Origins $ mkOriginSet $
236+ case traverse parseURI origins of
238+ Nothing -> error "Invalid entry in allowedOrigins list"