-- | `zephyr` command line option parser -- module Command.Options ( Options (..) , parseOptions ) where import Data.List (intercalate) import qualified Data.Map as M import qualified Data.Set as S import qualified Data.Text as T import Data.Traversable (for) import qualified Language.PureScript as P import Language.PureScript.DCE.Errors (EntryPoint (..)) import qualified Options.Applicative as Opts -- | @zephyr@ options -- data Options = Options { optEntryPoints :: [EntryPoint] -- ^ List of entry points. , optInputDir :: FilePath -- ^ Input directory, default: @outout@. , optOutputDir :: FilePath -- ^ Output directory, default: @dce-output@. , optVerbose :: Bool -- ^ Verbose output. , optForeign :: Bool -- ^ Dead code eliminate foreign javascript module. , optPureScriptOptions :: P.Options -- ^ PureScription options , optUsePrefix :: Bool , optJsonErrors :: Bool -- ^ Print errors in `JSON` format; default 'False'. , optEvaluate :: Bool -- ^ Rewirite using an evaluation; it can reduce literal expressions; default -- 'False'. } inputDirectoryOpt :: Opts.Parser FilePath inputDirectoryOpt = Opts.strOption $ Opts.short 'i' <> Opts.long "input-directory" <> Opts.value "output" <> Opts.showDefault <> Opts.help "Input directory (purs output directory)." outputDirectoryOpt :: Opts.Parser FilePath outputDirectoryOpt = Opts.strOption $ Opts.short 'o' <> Opts.long "dce-output" <> Opts.value "dce-output" <> Opts.showDefault <> Opts.help "Output directory." entryPointOpt :: Opts.Parser EntryPoint entryPointOpt = Opts.argument (Opts.auto >>= checkIfQualified) $ Opts.metavar "entry-point" <> Opts.help "Qualified identifier or a module name (it may be prefixed with `ident:` or `module:`). All code which is not a transitive dependency of an entry point (or any exported identifier from a give module) will be removed. You can pass multiple entry points." where checkIfQualified (EntryPoint q@(P.Qualified Nothing _)) = fail $ "not a qualified indentifier: '" ++ T.unpack (P.showQualified P.runIdent q) ++ "'" checkIfQualified e = return e verboseOutputOpt :: Opts.Parser Bool verboseOutputOpt = Opts.switch $ Opts.short 'v' <> Opts.long "verbose" <> Opts.showDefault <> Opts.help "Verbose CoreFn parser errors." dceForeignOpt :: Opts.Parser Bool dceForeignOpt = Opts.switch $ Opts.short 'f' <> Opts.long "dce-foreign" <> Opts.showDefault <> Opts.help "dce foriegn modules" comments :: Opts.Parser Bool comments = Opts.switch $ Opts.short 'c' <> Opts.long "comments" <> Opts.help "Include comments in the generated code" verboseErrors :: Opts.Parser Bool verboseErrors = Opts.switch $ Opts.short 'v' <> Opts.long "verbose-errors" <> Opts.help "Display verbose error messages" codegenTargets :: Opts.Parser [P.CodegenTarget] codegenTargets = Opts.option targetParser $ Opts.short 'g' <> Opts.long "codegen" <> Opts.value [P.JS] <> Opts.help ( "Specifies comma-separated codegen targets to include. " <> targetsMessage <> " The default target is 'js', but if this option is used only the targets specified will be used." ) dceEvalOpt :: Opts.Parser Bool dceEvalOpt = Opts.switch $ Opts.short 'e' <> Opts.long "evaluate" <> Opts.showDefault <> Opts.help "rewrite using simple evaluation" targets :: M.Map String P.CodegenTarget targets = M.fromList [ ("js", P.JS) , ("sourcemaps", P.JSSourceMap) , ("corefn", P.CoreFn) ] targetsMessage :: String targetsMessage = "Accepted codegen targets are '" <> intercalate "', '" (M.keys targets) <> "'." targetParser :: Opts.ReadM [P.CodegenTarget] targetParser = Opts.str >>= \s -> for (T.split (== ',') s) $ maybe (Opts.readerError targetsMessage) pure . flip M.lookup targets . T.unpack . T.strip noPrefix :: Opts.Parser Bool noPrefix = Opts.switch $ Opts.short 'p' <> Opts.long "no-prefix" <> Opts.help "Do not include comment header" jsonErrors :: Opts.Parser Bool jsonErrors = Opts.switch $ Opts.long "json-errors" <> Opts.help "Print errors to stderr as JSON" pureScriptOptions :: Opts.Parser P.Options pureScriptOptions = P.Options <$> verboseErrors <*> (not <$> comments) <*> (handleTargets <$> codegenTargets) where -- Ensure that the JS target is included if sourcemaps are handleTargets :: [P.CodegenTarget] -> S.Set P.CodegenTarget handleTargets ts = S.fromList (if P.JSSourceMap `elem` ts then P.JS : ts else ts) parseOptions :: Opts.Parser Options parseOptions = Options <$> Opts.many entryPointOpt <*> inputDirectoryOpt <*> outputDirectoryOpt <*> verboseOutputOpt <*> dceForeignOpt <*> pureScriptOptions <*> (not <$> noPrefix) <*> jsonErrors <*> dceEvalOpt