git.iko.soy/rowtype-yoga/purescript-native
on 4e7cfee help sign in
147 lines 5.4 kB history raw
1{-# LANGUAGE FlexibleContexts #-}
2{-# LANGUAGE ScopedTypeVariables #-}
3{-# LANGUAGE TupleSections #-}
4{-# LANGUAGE RecordWildCards #-}
6-- | Bundles compiled PureScript modules for the browser.
7module Main (main) where
9import Data.Traversable (for)
10import Data.Version (showVersion)
11import Data.Monoid ((<>))
12import Data.Aeson (encode)
13import Data.Maybe (isNothing)
15import Control.Applicative
16import Control.Monad
17import Control.Monad.Error.Class
18import Control.Monad.Trans.Except
19import Control.Monad.IO.Class
21import System.FilePath (takeDirectory, (</>), (<.>), takeFileName)
22import System.FilePath.Glob (glob)
23import System.Exit (exitFailure)
24import System.IO (stderr, stdout, hPutStrLn, hSetEncoding, utf8)
25import System.IO.UTF8 (readUTF8File, writeUTF8File)
26import System.Directory (createDirectoryIfMissing, getCurrentDirectory)
28import qualified Data.ByteString.Lazy as B
29import qualified Data.ByteString.UTF8 as BU8
31import Language.PureScript.Bundle
33import Options.Applicative (Parser, ParseError (..))
34import qualified Options.Applicative as Opts
36import qualified Paths_purescript as Paths
38import SourceMap
39import SourceMap.Types
41-- | Command line options.
42data Options = Options
43 { optionsInputFiles :: [FilePath]
44 , optionsOutputFile :: Maybe FilePath
45 , optionsEntryPoints :: [String]
46 , optionsMainModule :: Maybe String
47 , optionsNamespace :: String
48 , optionsSourceMaps :: Bool
49 } deriving Show
51-- | The main application function.
52-- This function parses the input files, performs dead code elimination, filters empty modules
53-- and generates and prints the final Javascript bundle.
54app :: (MonadError ErrorMessage m, MonadIO m) => Options -> m (Maybe SourceMapping, String)
55app Options{..} = do
56 inputFiles <- concat <$> mapM (liftIO . glob) optionsInputFiles
57 when (null inputFiles) . liftIO $ do
58 hPutStrLn stderr "psc-bundle: No input files."
59 exitFailure
60 when (isNothing optionsOutputFile && optionsSourceMaps == True) . liftIO $ do
61 hPutStrLn stderr "psc-bundle: Source maps only supported when output file specified."
62 exitFailure
64 input <- for inputFiles $ \filename -> do
65 js <- liftIO (readUTF8File filename)
66 mid <- guessModuleIdentifier filename
67 length js `seq` return (mid, Just filename, js) -- evaluate readFile till EOF before returning, not to exhaust file handles
69 let entryIds = map (`ModuleIdentifier` Regular) optionsEntryPoints
71 currentDir <- liftIO getCurrentDirectory
72 let outFile = if optionsSourceMaps then fmap (currentDir </>) optionsOutputFile else Nothing
73 bundleSM input entryIds optionsMainModule optionsNamespace outFile
75-- | Command line options parser.
76options :: Parser Options
77options = Options <$> some inputFile
78 <*> optional outputFile
79 <*> many entryPoint
80 <*> optional mainModule
81 <*> namespace
82 <*> sourceMaps
83 where
84 inputFile :: Parser FilePath
85 inputFile = Opts.strArgument $
86 Opts.metavar "FILE"
87 <> Opts.help "The input .js file(s)"
89 outputFile :: Parser FilePath
90 outputFile = Opts.strOption $
91 Opts.short 'o'
92 <> Opts.long "output"
93 <> Opts.help "The output .js file"
95 entryPoint :: Parser String
96 entryPoint = Opts.strOption $
97 Opts.short 'm'
98 <> Opts.long "module"
99 <> Opts.help "Entry point module name(s). All code which is not a transitive dependency of an entry point module will be removed."
101 mainModule :: Parser String
102 mainModule = Opts.strOption $
103 Opts.long "main"
104 <> Opts.help "Generate code to run the main method in the specified module."
106 namespace :: Parser String
107 namespace = Opts.strOption $
108 Opts.short 'n'
109 <> Opts.long "namespace"
110 <> Opts.value "PS"
111 <> Opts.showDefault
112 <> Opts.help "Specify the namespace that PureScript modules will be exported to when running in the browser."
114 sourceMaps :: Parser Bool
115 sourceMaps = Opts.switch $
116 Opts.long "source-maps"
117 <> Opts.help "Whether to generate source maps for the bundle (requires --output)."
119-- | Make it go.
120main :: IO ()
121main = do
122 hSetEncoding stdout utf8
123 hSetEncoding stderr utf8
124 opts <- Opts.execParser (Opts.info (version <*> Opts.helper <*> options) infoModList)
125 output <- runExceptT (app opts)
126 case output of
127 Left err -> do
128 hPutStrLn stderr (unlines (printErrorMessage err))
129 exitFailure
130 Right (sourcemap, js) ->
131 case optionsOutputFile opts of
132 Just outputFile -> do
133 createDirectoryIfMissing True (takeDirectory outputFile)
134 case sourcemap of
135 Just sm -> do
136 writeUTF8File outputFile $ js ++ "\n//# sourceMappingURL=" ++ (takeFileName outputFile <.> "map") ++ "\n"
137 writeUTF8File (outputFile <.> "map") $ BU8.toString . B.toStrict . encode $ generate sm
138 Nothing -> writeUTF8File outputFile js
139 Nothing -> putStrLn js
140 where
141 infoModList = Opts.fullDesc <> headerInfo <> footerInfo
142 headerInfo = Opts.header "psc-bundle - Bundles compiled PureScript modules for the browser"
143 footerInfo = Opts.footer $ "psc-bundle " ++ showVersion Paths.version
145 version :: Parser (a -> a)
146 version = Opts.abortOption (InfoMsg (showVersion Paths.version)) $
147 Opts.long "version" <> Opts.help "Show the version number" <> Opts.hidden