purescript-0.10.4: psc-bundle/Main.hs
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE RecordWildCards #-}
-- | Bundles compiled PureScript modules for the browser.
module Main (main) where
import Data.Traversable (for)
import Data.Version (showVersion)
import Data.Monoid ((<>))
import Data.Aeson (encode)
import Data.Maybe (isNothing)
import Control.Applicative
import Control.Monad
import Control.Monad.Error.Class
import Control.Monad.Trans.Except
import Control.Monad.IO.Class
import System.FilePath (takeDirectory, (</>), (<.>), takeFileName)
import System.FilePath.Glob (glob)
import System.Exit (exitFailure)
import System.IO (stderr, stdout, hPutStrLn, hSetEncoding, utf8)
import System.IO.UTF8 (readUTF8File, writeUTF8File)
import System.Directory (createDirectoryIfMissing, getCurrentDirectory)
import qualified Data.ByteString.Lazy as B
import qualified Data.ByteString.UTF8 as BU8
import Language.PureScript.Bundle
import Options.Applicative (Parser, ParseError (..))
import qualified Options.Applicative as Opts
import qualified Paths_purescript as Paths
import SourceMap
import SourceMap.Types
-- | Command line options.
data Options = Options
{ optionsInputFiles :: [FilePath]
, optionsOutputFile :: Maybe FilePath
, optionsEntryPoints :: [String]
, optionsMainModule :: Maybe String
, optionsNamespace :: String
, optionsSourceMaps :: Bool
} deriving Show
-- | The main application function.
-- This function parses the input files, performs dead code elimination, filters empty modules
-- and generates and prints the final Javascript bundle.
app :: (MonadError ErrorMessage m, MonadIO m) => Options -> m (Maybe SourceMapping, String)
app Options{..} = do
inputFiles <- concat <$> mapM (liftIO . glob) optionsInputFiles
when (null inputFiles) . liftIO $ do
hPutStrLn stderr "psc-bundle: No input files."
exitFailure
when (isNothing optionsOutputFile && optionsSourceMaps == True) . liftIO $ do
hPutStrLn stderr "psc-bundle: Source maps only supported when output file specified."
exitFailure
input <- for inputFiles $ \filename -> do
js <- liftIO (readUTF8File filename)
mid <- guessModuleIdentifier filename
length js `seq` return (mid, Just filename, js) -- evaluate readFile till EOF before returning, not to exhaust file handles
let entryIds = map (`ModuleIdentifier` Regular) optionsEntryPoints
currentDir <- liftIO getCurrentDirectory
let outFile = if optionsSourceMaps then fmap (currentDir </>) optionsOutputFile else Nothing
bundleSM input entryIds optionsMainModule optionsNamespace outFile
-- | Command line options parser.
options :: Parser Options
options = Options <$> some inputFile
<*> optional outputFile
<*> many entryPoint
<*> optional mainModule
<*> namespace
<*> sourceMaps
where
inputFile :: Parser FilePath
inputFile = Opts.strArgument $
Opts.metavar "FILE"
<> Opts.help "The input .js file(s)"
outputFile :: Parser FilePath
outputFile = Opts.strOption $
Opts.short 'o'
<> Opts.long "output"
<> Opts.help "The output .js file"
entryPoint :: Parser String
entryPoint = Opts.strOption $
Opts.short 'm'
<> Opts.long "module"
<> Opts.help "Entry point module name(s). All code which is not a transitive dependency of an entry point module will be removed."
mainModule :: Parser String
mainModule = Opts.strOption $
Opts.long "main"
<> Opts.help "Generate code to run the main method in the specified module."
namespace :: Parser String
namespace = Opts.strOption $
Opts.short 'n'
<> Opts.long "namespace"
<> Opts.value "PS"
<> Opts.showDefault
<> Opts.help "Specify the namespace that PureScript modules will be exported to when running in the browser."
sourceMaps :: Parser Bool
sourceMaps = Opts.switch $
Opts.long "source-maps"
<> Opts.help "Whether to generate source maps for the bundle (requires --output)."
-- | Make it go.
main :: IO ()
main = do
hSetEncoding stdout utf8
hSetEncoding stderr utf8
opts <- Opts.execParser (Opts.info (version <*> Opts.helper <*> options) infoModList)
output <- runExceptT (app opts)
case output of
Left err -> do
hPutStrLn stderr (unlines (printErrorMessage err))
exitFailure
Right (sourcemap, js) ->
case optionsOutputFile opts of
Just outputFile -> do
createDirectoryIfMissing True (takeDirectory outputFile)
case sourcemap of
Just sm -> do
writeUTF8File outputFile $ js ++ "\n//# sourceMappingURL=" ++ (takeFileName outputFile <.> "map") ++ "\n"
writeUTF8File (outputFile <.> "map") $ BU8.toString . B.toStrict . encode $ generate sm
Nothing -> writeUTF8File outputFile js
Nothing -> putStrLn js
where
infoModList = Opts.fullDesc <> headerInfo <> footerInfo
headerInfo = Opts.header "psc-bundle - Bundles compiled PureScript modules for the browser"
footerInfo = Opts.footer $ "psc-bundle " ++ showVersion Paths.version
version :: Parser (a -> a)
version = Opts.abortOption (InfoMsg (showVersion Paths.version)) $
Opts.long "version" <> Opts.help "Show the version number" <> Opts.hidden