@@ -4,14 +4,20 @@ import Prelude
5import Data.Array as Array
5import Data.Array as Array
6import Data.Either (Either(..))
6import Data.Either (Either(..))
7+import Data.Enum (fromEnum)
7import Data.Foldable (for_)
8import Data.Foldable (for_)
8import Data.Maybe (Maybe(..))
9import Data.Maybe (Maybe(..))
9import Data.Newtype (unwrap)
10import Data.Newtype (unwrap)
10import Data.String.CodeUnits as String
11import Data.String.CodeUnits as String
12+import Data.Tuple.Nested ((/\))
11import Effect (Effect)
13import Effect (Effect)
12import Effect.Console (log)
14import Effect.Console (log)
13import Node.FS.Simple as FS
15import Node.FS.Simple as FS
14import Node.CLI as Process
16import Node.CLI as Process
17+import Options.Applicative (Parser, argument, execParserPure, fullDesc, header, help, helper, info, long, many, metavar, progDesc, str, switch, (<**>))
18+import Options.Applicative.Builder (defaultPrefs)
19+import Options.Applicative.Types (CompletionResult(..), ParserFailure(ParserFailure), ParserResult(..))
20+import Options.Applicative.Help.Types (ParserHelp(ParserHelp), renderHelp)
15import Purslint.Config (defaultConfig, filterRules, parseConfig)
21import Purslint.Config (defaultConfig, filterRules, parseConfig)
16import Purslint.Fix (applyAllFixes)
22import Purslint.Fix (applyAllFixes)
17import Purslint.Rule (Rule)
23import Purslint.Rule (Rule)
@@ -99,6 +105,13 @@ import Purslint.Rules.RedundantParens (redundantParensRule)
99import Purslint.Runner (runRules)
105import Purslint.Runner (runRules)
100import Purslint.Types (LintResult(..), LintWarning(..), Severity(..), SourceCode(..), Suggestion(..))
106import Purslint.Types (LintResult(..), LintWarning(..), Severity(..), SourceCode(..), Suggestion(..))
110+ , listRules :: Boolean
111+ , initConfig :: Boolean
112+ , paths :: Array String
102-- | All available rules (81 total)
115-- | All available rules (81 total)
103allRules :: Array Rule
116allRules :: Array Rule
104allRules =
117allRules =
@@ -337,38 +350,70 @@ initConfig = do
337 FS.writeTextFile ".purslintrc" sampleConfig
350 FS.writeTextFile ".purslintrc" sampleConfig
338 log "Created .purslintrc"
351 log "Created .purslintrc"
353+parseFlags :: Array String -> Effect (Maybe CliOptions)
354+parseFlags args = case parseFlags' (Array.drop 2 args) of
355+ Success cli -> pure (Just cli)
356+ Failure (ParserFailure pf) -> case pf programName of
357+ ph@(ParserHelp _) /\ exitCode /\ i /\ _unit -> do
358+ log $ renderHelp i ph
359+ Process.exit (fromEnum exitCode)
361+ CompletionInvoked (CompletionResult { execCompletion }) -> do
362+ executedCompletion <- execCompletion programName
363+ log executedCompletion
367+ programName = "purslint"
369+parseFlags' :: Array String -> ParserResult CliOptions
370+parseFlags' = execParserPure defaultPrefs opts
373+ info (flagsParser <**> helper) $
375+ <> progDesc "Lint PureScript source files"
376+ <> header "purslint - a linter for PureScript"
378+flagsParser :: Parser CliOptions
380+ fix <- switch $ long "fix" <> help "Auto-fix issues (where possible)"
381+ listRules <- switch $ long "list" <> help "List all available rules"
382+ initConfig <- switch $ long "init" <> help "Create a sample .purslintrc"
383+ pathsList <- many $ argument str $ metavar "PATH..."
384+ let paths = Array.fromFoldable pathsList
385+ in { fix, listRules, initConfig, paths }
340main :: Effect Unit
387main :: Effect Unit
342 args <- Process.argv
389 args <- Process.argv
343− -- args[0] is node, args[1] is the script, args[2..] are the actual arguments
390+ maybeCli <- parseFlags args
344− let userArgs = Array.drop 1 args
345− case userArgs of
391+ case maybeCli of
347− ["--help"] -> showUsage
349− ["--list"] -> listRules
350− ["--init"] -> initConfig
352− -- Check for --fix flag
353− let hasFix = Array.elem "--fix" userArgs
354− let paths = Array.filter (\a -> a /= "--fix") userArgs
356− -- Load config and filter rules
357− config <- loadConfig
358− let enabledRules = filterRules allRules config
362− _ <- Array.foldM (\acc path -> do
363− count <- fixPath enabledRules path
364− pure (acc + count)) 0 paths
365− pure unit
392+ Nothing -> pure unit
394+ if cli.listRules then
396+ else if cli.initConfig then
398+ else if Array.null cli.paths then
401+ -- Load config and filter rules
402+ config <- loadConfig
403+ let enabledRules = filterRules allRules config
407+ _ <- Array.foldM (\acc path -> do
408+ count <- fixPath enabledRules path
409+ pure (acc + count)) 0 cli.paths
367− -- Lint mode
412+ -- Lint mode
368− totalWarnings <- Array.foldM (\acc path -> do
413+ totalWarnings <- Array.foldM (\acc path -> do
369− count <- lintPathWithConfig enabledRules path
414+ count <- lintPathWithConfig enabledRules path
370− pure (acc + count)) 0 paths
415+ pure (acc + count)) 0 cli.paths
371− when (totalWarnings > 0) do
416+ when (totalWarnings > 0) do
373− log $ "Total: " <> show totalWarnings <> " warning" <> if totalWarnings == 1 then "" else "s"
418+ log $ "Total: " <> show totalWarnings <> " warning" <> if totalWarnings == 1 then "" else "s"
374− Process.exit 1
419+ Process.exit 1