1{-# LANGUAGE DataKinds #-}
2{-# LANGUAGE FlexibleContexts #-}
3{-# LANGUAGE RecordWildCards #-}
4{-# LANGUAGE TupleSections #-}
8import Control.Applicative
10import Control.Monad.Error.Class (MonadError(..))
11import Control.Monad.Writer.Strict
13import Data.List (isSuffixOf, partition)
14import Data.Version (showVersion)
15import qualified Data.Map as M
16import qualified Data.Aeson as A
17import qualified Data.ByteString.Lazy as B
18import qualified Data.ByteString.UTF8 as BU8
20import Options.Applicative as Opts
22import System.Exit (exitSuccess, exitFailure)
23import System.IO (hSetEncoding, hPutStrLn, stdout, stderr, utf8)
25import System.FilePath.Glob (glob)
27import qualified Language.PureScript as P
28import qualified Paths_purescript as Paths
30import Language.PureScript.Make
34data PSCMakeOptions = PSCMakeOptions
35 { pscmInput :: [FilePath]
36 , pscmForeignInput :: [FilePath]
37 , pscmOutputDir :: FilePath
38 , pscmOpts :: P.Options
39 , pscmUsePrefix :: Bool
40 , pscmJSONErrors :: Bool
43data InputOptions = InputOptions
44 { ioInputFiles :: [FilePath]
47-- | Argumnets: verbose, use JSON, warnings, errors
48printWarningsAndErrors :: Bool -> Bool -> P.MultipleErrors -> Either P.MultipleErrors a -> IO ()
49printWarningsAndErrors verbose False warnings errors = do
50 when (P.nonEmpty warnings) $
51 hPutStrLn stderr (P.prettyPrintMultipleWarnings verbose warnings)
54 hPutStrLn stderr (P.prettyPrintMultipleErrors verbose errs)
57printWarningsAndErrors verbose True warnings errors = do
58 hPutStrLn stderr . BU8.toString . B.toStrict . A.encode $
59 JSONResult (toJSONErrors verbose P.Warning warnings)
60 (either (toJSONErrors verbose P.Error) (const []) errors)
61 either (const exitFailure) (const (return ())) errors
63compile :: PSCMakeOptions -> IO ()
64compile PSCMakeOptions{..} = do
65 input <- globWarningOnMisses (unless pscmJSONErrors . warnFileTypeNotFound) pscmInput
66 when (null input && not pscmJSONErrors) $ do
67 hPutStrLn stderr "psc: No input files."
69 let (jsFiles, pursFiles) = partition (isSuffixOf ".js") input
70 moduleFiles <- readInput (InputOptions pursFiles)
71 inputForeign <- globWarningOnMisses (unless pscmJSONErrors . warnFileTypeNotFound) pscmForeignInput
72 foreignFiles <- forM (inputForeign ++ jsFiles) (\inFile -> (inFile,) <$> readUTF8File inFile)
73 (makeErrors, makeWarnings) <- runMake pscmOpts $ do
74 (ms, foreigns) <- parseInputs moduleFiles foreignFiles
75 let filePathMap = M.fromList $ map (\(fp, P.Module _ _ mn _ _) -> (mn, fp)) ms
76 makeActions = buildMakeActions pscmOutputDir filePathMap foreigns pscmUsePrefix
77 P.make makeActions (map snd ms)
78 printWarningsAndErrors (P.optionsVerboseErrors pscmOpts) pscmJSONErrors makeWarnings makeErrors
81warnFileTypeNotFound :: String -> IO ()
82warnFileTypeNotFound = hPutStrLn stderr . ("psc: No files found using pattern: " ++)
84globWarningOnMisses :: (String -> IO ()) -> [FilePath] -> IO [FilePath]
85globWarningOnMisses warn = concatMapM globWithWarning
87 globWithWarning pattern = do
89 when (null paths) $ warn pattern
91 concatMapM f = liftM concat . mapM f
93readInput :: InputOptions -> IO [(Either P.RebuildPolicy FilePath, String)]
94readInput InputOptions{..} = forM ioInputFiles $ \inFile -> (Right inFile, ) <$> readUTF8File inFile
96parseInputs :: (MonadError P.MultipleErrors m, MonadWriter P.MultipleErrors m)
97 => [(Either P.RebuildPolicy FilePath, String)]
98 -> [(FilePath, P.ForeignJS)]
99 -> m ([(Either P.RebuildPolicy FilePath, P.Module)], M.Map P.ModuleName FilePath)
100parseInputs modules foreigns =
101 (,) <$> P.parseModulesFromFiles (either (const "") id) modules
102 <*> P.parseForeignModulesFromFiles foreigns
104inputFile :: Parser FilePath
105inputFile = strArgument $
107 <> help "The input .purs file(s)"
109inputForeignFile :: Parser FilePath
110inputForeignFile = strOption $
113 <> help "The input .js file(s) providing foreign import implementations"
115outputDirectory :: Parser FilePath
116outputDirectory = strOption $
119 <> Opts.value "output"
121 <> help "The output directory"
123requirePath :: Parser (Maybe FilePath)
124requirePath = optional $ strOption $
126 <> long "require-path"
127 <> help "The path prefix to use for require() calls in the generated JavaScript [deprecated]"
132 <> help "Disable tail call optimizations"
134noMagicDo :: Parser Bool
137 <> help "Disable the optimization that overloads the do keyword to generate efficient code specifically for the Eff monad"
142 <> help "Skip the optimization phase"
144comments :: Parser Bool
148 <> help "Include comments in the generated code"
150verboseErrors :: Parser Bool
151verboseErrors = switch $
153 <> long "verbose-errors"
154 <> help "Display verbose error messages"
156noPrefix :: Parser Bool
160 <> help "Do not include comment header"
162jsonErrors :: Parser Bool
165 <> help "Print errors to stderr as JSON"
166sourceMaps :: Parser Bool
169 <> help "Generate source maps"
172options :: Parser P.Options
173options = P.Options <$> noTco
178 <*> (not <$> comments)
182pscMakeOptions :: Parser PSCMakeOptions
183pscMakeOptions = PSCMakeOptions <$> many inputFile
184 <*> many inputForeignFile
187 <*> (not <$> noPrefix)
192 hSetEncoding stdout utf8
193 hSetEncoding stderr utf8
194 execParser opts >>= compile
196 opts = info (version <*> helper <*> pscMakeOptions) infoModList
197 infoModList = fullDesc <> headerInfo <> footerInfo
198 headerInfo = header "psc - Compiles PureScript to Javascript"
199 footerInfo = footer $ "psc " ++ showVersion Paths.version
201 version :: Parser (a -> a)
202 version = abortOption (InfoMsg (showVersion Paths.version)) $ long "version" <> help "Show the version number" <> hidden
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list