on abe4d52 help sign in
elk/elm/src/Compiler.hs
114 lines 4.7 kB history raw
1{-# LANGUAGE DeriveDataTypeable #-}
2module Main where
4import Data.Either (lefts, rights)
5import Data.List (intersect, intercalate)
6import Data.Maybe (fromMaybe)
7import Data.Version (showVersion)
8import System.Console.CmdArgs
9import System.FilePath
10import Text.Blaze.Html.Renderer.String (renderHtml)
12import qualified Text.Jasmine as JS
13import qualified Data.ByteString.Lazy.Char8 as BS
15import Ast
16import Initialize
17import CompileToJS
18import GenerateHtml
19import Paths_Elm
21import System.IO.Unsafe
23data ELM =
24 ELM { make :: Bool
25 , files :: [FilePath]
26 , runtime :: Maybe FilePath
27 , separate_js :: Bool
28 , only_js :: Bool
29 , import_js :: [FilePath]
30 , generate_noscript :: Bool
31 , minify :: Bool
32 , output_directory :: Maybe FilePath
33 }
34 deriving (Data,Typeable,Show,Eq)
36elm = ELM { make = False &= help "automatically compile dependencies."
37 , files = def &= args &= typ "FILES"
38 , runtime = Nothing &= typFile &=
39 help "Specify a custom location for Elm's runtime system."
40 , separate_js = False &= help "Compile to separate HTML and JS files."
41 , only_js = False &= help "Compile only to JavaScript."
42 , import_js = [] &= typFile &= help "Include a JavaScript file before the body of the Elm program. Can be used many times. Files will be included in the given order."
43 , generate_noscript = True &= help "Add generated <noscript> tag to HTML output."
44 , minify = False &= help "Minify generated JavaScript"
45 , output_directory = Nothing &= typFile &= help "Output files to directory specified. Defaults to the location of original file."
46 } &=
47 help "Compile Elm programs to HTML, CSS, and JavaScript." &=
48 summary ("The Elm Compiler " ++ showVersion version ++ ", (c) Evan Czaplicki")
50main = do
51 args <- cmdArgs elm
52 mini <- getDataFileName ("elm-runtime-" ++ showVersion version ++ ".js")
53 compileArgs mini args
55compileArgs mini (ELM _ [] _ _ _ _ _ _ _) =
56 putStrLn "Usage: elm [OPTIONS] [FILES]\nFor more help: elm --help"
57compileArgs mini (ELM make files rtLoc split only js nscrpt isMini outputDir) =
58 mapM_ (fileTo isMini get what js nscrpt outputDir $ fromMaybe mini rtLoc) files
59 where get = if make then getModules [] else getModule
60 what = if only then JS else
61 if split then Split else HTML
63data What = JS | HTML | Split
65fileTo isMini get what jsFiles noscript outputDir rtLoc file = do
66 let jsStyle = if isMini then Minified else Readable
67 let formatJS = if isMini then BS.unpack . JS.minify . BS.pack else id
68 ems <- get file
69 jss <- concat `fmap` mapM readFile jsFiles
70 case ems of
71 Left err -> putStrLn $ "Error while compiling " ++ file ++ ":\n" ++ err
72 Right ms ->
73 let path = fromMaybe "" outputDir </> file
74 js = replaceExtension path ".js"
75 html = replaceExtension path ".html"
76 in case unsafePerformIO (print path) `seq` what of
77 JS -> writeFile js . formatJS $ jss ++ concatMap jsModule ms
78 HTML -> writeFile html . renderHtml $ modulesToHtml jsStyle "" rtLoc jss noscript ms
79 Split -> do
80 writeFile html . renderHtml $ linkedHtml rtLoc js ms
81 writeFile js . formatJS $ jss ++ concatMap jsModule ms
83getModules :: [String] -> FilePath -> IO (Either String [([String],Module)])
84getModules uses file = do
85 code <- readFile file
86 case initialize code of
87 Left err -> return . Left $ "Error in " ++ file ++ ":\n" ++ err
88 Right (escs, modul@(Module _ _ imports _)) ->
89 let imps = filter (`notElem` builtInModules) $ map fst imports in
90 case intersect uses imps of
91 x:_ -> return . Left $ "Error: Cyclic dependency. Module " ++
92 x ++ " depends on itself."
93 [] -> do
94 ems <- mapM (getModules (uses ++ imps) . toFilePath) imps
95 return $ case lefts ems of
96 [] -> Right $ concat (rights ems) ++ [(escs,modul)]
97 errs -> Left $ intercalate "\n" errs
99getModule :: FilePath -> IO (Either String [([String],Module)])
100getModule file = do
101 code <- readFile file
102 return . fmap (:[]) $ initialize code
104toFilePath :: String -> FilePath
105toFilePath modul = map (\c -> if c == '.' then '/' else c) modul ++ ".elm"
107builtInModules =
108 concat [ [ "List", "Char", "Maybe", "Dict", "Set", "Automaton", "Date" ]
109 , [ "Signal", "Mouse", "Keyboard.Raw"
110 , "Window", "Time", "HTTP", "Input", "Random" ]
111 , [ "Graphics", "Text", "Color" ]
112 , map ("JavaScript"++) [ "", ".Experimental" ]
113 , [ "Prelude", "JSON" ]
114 ]