diff --git a/app/Command/Compile.hs b/app/Command/Compile.hs
--- a/app/Command/Compile.hs
+++ b/app/Command/Compile.hs
@@ -7,7 +7,7 @@
 import Data.Aeson qualified as A
 import Data.Bool (bool)
 import Data.ByteString.Lazy.UTF8 qualified as LBU8
-import Data.List (intercalate, (\\))
+import Data.List (intercalate)
 import Data.Map qualified as M
 import Data.Set qualified as S
 import Data.Text qualified as T
@@ -15,17 +15,19 @@
 import Language.PureScript qualified as P
 import Language.PureScript.CST qualified as CST
 import Language.PureScript.Errors.JSON (JSONResult(..), toJSONErrors)
+import Language.PureScript.Glob (toInputGlobs, PSCGlobs(..), warnFileTypeNotFound)
 import Language.PureScript.Make (buildMakeActions, inferForeignModules, runMake)
 import Options.Applicative qualified as Opts
+import SharedCLI qualified
 import System.Console.ANSI qualified as ANSI
 import System.Exit (exitSuccess, exitFailure)
 import System.Directory (getCurrentDirectory)
-import System.FilePath.Glob (glob)
-import System.IO (hPutStr, hPutStrLn, stderr, stdout)
+import System.IO (hPutStr, stderr, stdout)
 import System.IO.UTF8 (readUTF8FilesT)
 
 data PSCMakeOptions = PSCMakeOptions
   { pscmInput        :: [FilePath]
+  , pscmInputFromFile :: Maybe FilePath
   , pscmExclude      :: [FilePath]
   , pscmOutputDir    :: FilePath
   , pscmOpts         :: P.Options
@@ -54,9 +56,12 @@
 
 compile :: PSCMakeOptions -> IO ()
 compile PSCMakeOptions{..} = do
-  included <- globWarningOnMisses warnFileTypeNotFound pscmInput
-  excluded <- globWarningOnMisses warnFileTypeNotFound pscmExclude
-  let input = included \\ excluded
+  input <- toInputGlobs $ PSCGlobs
+    { pscInputGlobs = pscmInput
+    , pscInputGlobsFromFile = pscmInputFromFile
+    , pscExcludeGlobs = pscmExclude
+    , pscWarnFileTypeNotFound = warnFileTypeNotFound "compile"
+    }
   when (null input) $ do
     hPutStr stderr $ unlines [ "purs compile: No input files."
                              , "Usage: For basic information, try the `--help' option."
@@ -72,29 +77,6 @@
   printWarningsAndErrors (P.optionsVerboseErrors pscmOpts) pscmJSONErrors moduleFiles makeWarnings makeErrors
   exitSuccess
 
-warnFileTypeNotFound :: String -> IO ()
-warnFileTypeNotFound = hPutStrLn stderr . ("purs compile: No files found using pattern: " ++)
-
-globWarningOnMisses :: (String -> IO ()) -> [FilePath] -> IO [FilePath]
-globWarningOnMisses warn = concatMapM globWithWarning
-  where
-  globWithWarning pattern' = do
-    paths <- glob pattern'
-    when (null paths) $ warn pattern'
-    return paths
-  concatMapM f = fmap concat . mapM f
-
-inputFile :: Opts.Parser FilePath
-inputFile = Opts.strArgument $
-     Opts.metavar "FILE"
-  <> Opts.help "The input .purs file(s)."
-
-excludedFiles :: Opts.Parser FilePath
-excludedFiles = Opts.strOption $
-     Opts.short 'x'
-  <> Opts.long "exclude-files"
-  <> Opts.help "Glob of .purs files to exclude from the supplied files."
-
 outputDirectory :: Opts.Parser FilePath
 outputDirectory = Opts.strOption $
      Opts.short 'o'
@@ -161,8 +143,9 @@
     handleTargets ts = S.fromList (if P.JSSourceMap `elem` ts then P.JS : ts else ts)
 
 pscMakeOptions :: Opts.Parser PSCMakeOptions
-pscMakeOptions = PSCMakeOptions <$> many inputFile
-                                <*> many excludedFiles
+pscMakeOptions = PSCMakeOptions <$> many SharedCLI.inputFile
+                                <*> SharedCLI.globInputFile
+                                <*> many SharedCLI.excludeFiles
                                 <*> outputDirectory
                                 <*> options
                                 <*> (not <$> noPrefix)
diff --git a/app/Command/Docs.hs b/app/Command/Docs.hs
--- a/app/Command/Docs.hs
+++ b/app/Command/Docs.hs
@@ -13,12 +13,14 @@
 import Language.PureScript qualified as P
 import Language.PureScript.Docs qualified as D
 import Language.PureScript.Docs.Tags (dumpCtags, dumpEtags)
+import Language.PureScript.Glob (PSCGlobs(..), toInputGlobs, warnFileTypeNotFound)
 import Options.Applicative qualified as Opts
 import Text.PrettyPrint.ANSI.Leijen qualified as PP
+import SharedCLI qualified
 import System.Directory (getCurrentDirectory, createDirectoryIfMissing, removeFile)
 import System.Exit (exitFailure)
 import System.FilePath ((</>))
-import System.FilePath.Glob (compile, glob, globDir1)
+import System.FilePath.Glob (compile, globDir1)
 import System.IO (hPutStrLn, stderr)
 import System.IO.UTF8 (writeUTF8FileT)
 
@@ -35,12 +37,19 @@
   , _pscdOutput :: Maybe FilePath
   , _pscdCompileOutputDir :: FilePath
   , _pscdInputFiles  :: [FilePath]
+  , _pscdInputFromFile :: Maybe FilePath
+  , _pscdExcludeFiles :: [FilePath]
   }
   deriving (Show)
 
 docgen :: PSCDocsOptions -> IO ()
-docgen (PSCDocsOptions fmt moutput compileOutput inputGlob) = do
-  input <- concat <$> mapM glob inputGlob
+docgen (PSCDocsOptions fmt moutput compileOutput inputGlob inputGlobFromFile excludeGlob) = do
+  input <- toInputGlobs $ PSCGlobs
+    { pscInputGlobs = inputGlob
+    , pscInputGlobsFromFile = inputGlobFromFile
+    , pscExcludeGlobs = excludeGlob
+    , pscWarnFileTypeNotFound = warnFileTypeNotFound "docs"
+    }
   when (null input) $ do
     hPutStrLn stderr "purs docs: no input files."
     exitFailure
@@ -104,7 +113,13 @@
     Ctags -> "tags"
 
 pscDocsOptions :: Opts.Parser PSCDocsOptions
-pscDocsOptions = PSCDocsOptions <$> format <*> output <*> compileOutputDir <*> many inputFile
+pscDocsOptions = 
+  PSCDocsOptions <$> format 
+                 <*> output 
+                 <*> compileOutputDir 
+                 <*> many SharedCLI.inputFile
+                 <*> SharedCLI.globInputFile
+                 <*> many SharedCLI.excludeFiles
   where
   format :: Opts.Parser Format
   format = Opts.option Opts.auto $
@@ -127,11 +142,6 @@
     <> Opts.long "compile-output"
     <> Opts.metavar "DIR"
     <> Opts.help "Compiler output directory"
-
-  inputFile :: Opts.Parser FilePath
-  inputFile = Opts.strArgument $
-       Opts.metavar "FILE"
-    <> Opts.help "The input .purs file(s)"
 
 command :: Opts.Parser (IO ())
 command = docgen <$> (Opts.helper <*> pscDocsOptions)
diff --git a/app/Command/Graph.hs b/app/Command/Graph.hs
--- a/app/Command/Graph.hs
+++ b/app/Command/Graph.hs
@@ -10,21 +10,30 @@
 import Data.ByteString.Lazy.UTF8 qualified as LBU8
 import Language.PureScript qualified as P
 import Language.PureScript.Errors.JSON (JSONResult(..), toJSONErrors)
+import Language.PureScript.Glob (PSCGlobs(..), toInputGlobs, warnFileTypeNotFound)
 import Options.Applicative qualified as Opts
+import SharedCLI qualified
 import System.Console.ANSI qualified as ANSI
 import System.Exit (exitFailure)
 import System.Directory (getCurrentDirectory)
-import System.FilePath.Glob (glob)
 import System.IO (hPutStr, hPutStrLn, stderr)
 
 data GraphOptions = GraphOptions
   { graphInput      :: [FilePath]
+  , graphInputFromFile :: Maybe FilePath
+  , graphExclude       :: [FilePath]
   , graphJSONErrors :: Bool
   }
 
 graph :: GraphOptions -> IO ()
 graph GraphOptions{..} = do
-  input <- globWarningOnMisses (unless graphJSONErrors . warnFileTypeNotFound) graphInput
+  input <- toInputGlobs $ PSCGlobs
+    { pscInputGlobs = graphInput
+    , pscInputGlobsFromFile = graphInputFromFile
+    , pscExcludeGlobs = graphExclude
+    , pscWarnFileTypeNotFound = unless graphJSONErrors . warnFileTypeNotFound "graph"
+    }
+
   when (null input && not graphJSONErrors) $ do
     hPutStr stderr $ unlines
       [ "purs graph: No input files."
@@ -37,26 +46,16 @@
   printWarningsAndErrors graphJSONErrors makeWarnings makeResult
     >>= (LB.putStr . Json.encode)
 
-  where
-  warnFileTypeNotFound :: String -> IO ()
-  warnFileTypeNotFound =
-    hPutStrLn stderr . ("purs graph: No files found using pattern: " <>)
-
-
 command :: Opts.Parser (IO ())
 command = graph <$> (Opts.helper <*> graphOptions)
   where
   graphOptions :: Opts.Parser GraphOptions
   graphOptions =
-    GraphOptions <$> many inputFile
+    GraphOptions <$> many SharedCLI.inputFile
+                 <*> SharedCLI.globInputFile
+                 <*> many SharedCLI.excludeFiles
                  <*> jsonErrors
 
-  inputFile :: Opts.Parser FilePath
-  inputFile =
-    Opts.strArgument $
-      Opts.metavar "FILE" <>
-      Opts.help "The input .purs file(s)."
-
   jsonErrors :: Opts.Parser Bool
   jsonErrors =
     Opts.switch $
@@ -84,16 +83,3 @@
   case errors of
     Left _errs -> exitFailure
     Right res -> pure res
-
-
-globWarningOnMisses :: (String -> IO ()) -> [FilePath] -> IO [FilePath]
-globWarningOnMisses warn = concatMapM globWithWarning
-  where
-  globWithWarning :: String -> IO [FilePath]
-  globWithWarning pattern' = do
-    paths <- glob pattern'
-    when (null paths) $ warn pattern'
-    return paths
-
-  concatMapM :: (a -> IO [b]) -> [a] -> IO [b]
-  concatMapM f = fmap concat . mapM f
diff --git a/app/Command/Ide.hs b/app/Command/Ide.hs
--- a/app/Command/Ide.hs
+++ b/app/Command/Ide.hs
@@ -35,6 +35,7 @@
 import Language.PureScript.Ide.Types (Ide, IdeConfiguration(..), IdeEnvironment(..), IdeLogLevel(..), emptyIdeState)
 import Network.Socket qualified as Network
 import Options.Applicative qualified as Opts
+import SharedCLI qualified
 import System.Directory (doesDirectoryExist, getCurrentDirectory, setCurrentDirectory)
 import System.FilePath ((</>))
 import System.IO (BufferMode(..), hClose, hFlush, hSetBuffering, hSetEncoding, utf8)
@@ -59,6 +60,8 @@
 data ServerOptions = ServerOptions
   { _serverDirectory  :: Maybe FilePath
   , _serverGlobs      :: [FilePath]
+  , _serverGlobsFromFile :: Maybe FilePath
+  , _serverGlobsExcluded :: [FilePath]
   , _serverOutputPath :: FilePath
   , _serverPort       :: Network.PortNumber
   , _serverLoglevel   :: IdeLogLevel
@@ -110,7 +113,7 @@
     Opts.option Opts.auto (Opts.long "port" `mappend` Opts.short 'p' `mappend` Opts.value (4242 :: Integer))
 
   server :: ServerOptions -> IO ()
-  server opts'@(ServerOptions dir globs outputPath port logLevel editorMode polling noWatch) = do
+  server opts'@(ServerOptions dir globs globsFromFile globsExcluded outputPath port logLevel editorMode polling noWatch) = do
     when (logLevel == LogDebug || logLevel == LogAll)
       (putText "Parsed Options:" *> print opts')
     maybe (pure ()) setCurrentDirectory dir
@@ -136,6 +139,8 @@
         { confLogLevel = logLevel
         , confOutputPath = outputPath
         , confGlobs = globs
+        , confGlobsFromFile = globsFromFile
+        , confGlobsExclude = globsExcluded
         }
     ts <- newIORef Nothing
     let
@@ -150,7 +155,9 @@
   serverOptions =
     ServerOptions
       <$> optional (Opts.strOption (Opts.long "directory" `mappend` Opts.short 'd'))
-      <*> many (Opts.argument Opts.str (Opts.metavar "Source GLOBS..."))
+      <*> many SharedCLI.inputFile
+      <*> SharedCLI.globInputFile
+      <*> many SharedCLI.excludeFiles
       <*> Opts.strOption (Opts.long "output-directory" `mappend` Opts.value "output/")
       <*> (fromIntegral <$>
            Opts.option Opts.auto (Opts.long "port" `mappend` Opts.short 'p' `mappend` Opts.value (4242 :: Integer)))
diff --git a/app/Command/REPL.hs b/app/Command/REPL.hs
--- a/app/Command/REPL.hs
+++ b/app/Command/REPL.hs
@@ -15,27 +15,25 @@
 import Data.Foldable (for_)
 import Language.PureScript qualified as P
 import Language.PureScript.CST qualified as CST
+import Language.PureScript.Glob (PSCGlobs(..), toInputGlobs, warnFileTypeNotFound)
 import Language.PureScript.Interactive
 import Options.Applicative qualified as Opts
+import SharedCLI qualified
 import System.Console.Haskeline (InputT, Settings(..), defaultSettings, getInputLine, handleInterrupt, outputStrLn, runInputT, setComplete, withInterrupt)
 import System.IO.UTF8 (readUTF8File)
 import System.Exit (ExitCode(..), exitFailure)
 import System.Directory (doesFileExist, getCurrentDirectory)
 import System.FilePath ((</>))
-import System.FilePath.Glob qualified as Glob
 import System.IO (hPutStrLn, stderr)
 
 -- | Command line options
 data PSCiOptions = PSCiOptions
   { psciInputGlob         :: [String]
+  , psciInputFromFile     :: Maybe String
+  , psciExclude           :: [String]
   , psciBackend           :: Backend
   }
 
-inputFile :: Opts.Parser FilePath
-inputFile = Opts.strArgument $
-     Opts.metavar "FILES"
-  <> Opts.help "Optional .purs files to load on start"
-
 nodePathOption :: Opts.Parser (Maybe FilePath)
 nodePathOption = Opts.optional . Opts.strOption $
      Opts.metavar "FILE"
@@ -63,7 +61,9 @@
   <|> (nodeBackend <$> nodePathOption <*> nodeFlagsOption)
 
 psciOptions :: Opts.Parser PSCiOptions
-psciOptions = PSCiOptions <$> many inputFile
+psciOptions = PSCiOptions <$> many SharedCLI.inputFile
+                          <*> SharedCLI.globInputFile
+                          <*> many SharedCLI.excludeFiles
                           <*> backend
 
 -- | Parses the input and returns either a command, or an error as a 'String'.
@@ -132,7 +132,12 @@
   where
     loop :: PSCiOptions -> IO ()
     loop PSCiOptions{..} = do
-        inputFiles <- concat <$> traverse Glob.glob psciInputGlob
+        inputFiles <- toInputGlobs $ PSCGlobs
+          { pscInputGlobs = psciInputGlob
+          , pscInputGlobsFromFile = psciInputFromFile
+          , pscExcludeGlobs = psciExclude
+          , pscWarnFileTypeNotFound = warnFileTypeNotFound "repl"
+          }
         e <- runExceptT $ do
           modules <- ExceptT (loadAllModules inputFiles)
           when (null modules) . liftIO $ do
diff --git a/app/SharedCLI.hs b/app/SharedCLI.hs
new file mode 100644
--- /dev/null
+++ b/app/SharedCLI.hs
@@ -0,0 +1,24 @@
+module SharedCLI where
+
+import Prelude
+
+import Options.Applicative qualified as Opts
+
+inputFile :: Opts.Parser FilePath
+inputFile = Opts.strArgument $
+     Opts.metavar "GLOB"
+  <> Opts.help "A glob for input .purs file(s)."
+
+globInputFile :: Opts.Parser (Maybe FilePath)
+globInputFile = Opts.optional $ Opts.strOption $
+     Opts.long "source-globs-file"
+  <> Opts.metavar "FILE"
+  <> Opts.help "A file containing a line-separated list of input .purs globs."
+
+excludeFiles :: Opts.Parser FilePath
+excludeFiles = Opts.strOption $
+     Opts.short 'x'
+  <> Opts.long "exclude-files"
+  <> Opts.metavar "GLOB"
+  <> Opts.help "A glob of .purs files to exclude from the input .purs files."
+
diff --git a/purescript.cabal b/purescript.cabal
--- a/purescript.cabal
+++ b/purescript.cabal
@@ -1,6 +1,6 @@
 cabal-version:      2.4
 name:               purescript
-version:            0.15.14
+version:            0.15.15
 license:            BSD-3-Clause
 license-file:       LICENSE
 copyright:
@@ -137,6 +137,7 @@
         Language.PureScript.Errors
         Language.PureScript.Errors.JSON
         Language.PureScript.Externs
+        Language.PureScript.Glob
         Language.PureScript.Graph
         Language.PureScript.Hierarchy
         Language.PureScript.Ide
@@ -335,6 +336,7 @@
         Command.Ide
         Command.Publish
         Command.REPL
+        SharedCLI
         Version
         Paths_purescript
 
diff --git a/src/Language/PureScript/Glob.hs b/src/Language/PureScript/Glob.hs
new file mode 100644
--- /dev/null
+++ b/src/Language/PureScript/Glob.hs
@@ -0,0 +1,44 @@
+module Language.PureScript.Glob where
+
+import Prelude
+
+import Control.Monad (when)
+import Data.List (nub, (\\))
+import Data.Text qualified as T
+import System.FilePath.Glob (glob)
+import System.IO (hPutStrLn, stderr)
+import System.IO.UTF8 (readUTF8FileT)
+
+data PSCGlobs = PSCGlobs
+  { pscInputGlobs :: [FilePath]
+  , pscInputGlobsFromFile :: Maybe FilePath
+  , pscExcludeGlobs :: [FilePath]
+  , pscWarnFileTypeNotFound :: FilePath -> IO ()
+  }
+
+toInputGlobs :: PSCGlobs -> IO [FilePath]
+toInputGlobs (PSCGlobs {..}) = do
+  globsFromFile <- inputGlobsFromFile pscInputGlobsFromFile
+  included <- globWarningOnMisses pscWarnFileTypeNotFound $ nub $ pscInputGlobs <> globsFromFile
+  excluded <- globWarningOnMisses pscWarnFileTypeNotFound pscExcludeGlobs
+  pure $ included \\ excluded
+
+inputGlobsFromFile :: Maybe FilePath -> IO [FilePath]
+inputGlobsFromFile globsFromFile = do
+  mbInputsFromFile <- traverse readUTF8FileT globsFromFile
+  let
+    excludeBlankLines = not . T.null . T.strip
+    excludeComments = not . T.isPrefixOf "#"
+    toInputs = map (T.unpack . T.strip) . filter (\x -> excludeBlankLines x && excludeComments x) . T.lines
+  pure $ foldMap toInputs mbInputsFromFile
+
+globWarningOnMisses :: (String -> IO ()) -> [FilePath] -> IO [FilePath]
+globWarningOnMisses warn = foldMap globWithWarning
+  where
+  globWithWarning pattern' = do
+    paths <- glob pattern'
+    when (null paths) $ warn pattern'
+    return paths
+
+warnFileTypeNotFound :: String -> String -> IO ()
+warnFileTypeNotFound pursCmd = hPutStrLn stderr . ("purs " <> pursCmd <> ": No files found using pattern: " ++)
diff --git a/src/Language/PureScript/Ide.hs b/src/Language/PureScript/Ide.hs
--- a/src/Language/PureScript/Ide.hs
+++ b/src/Language/PureScript/Ide.hs
@@ -24,6 +24,7 @@
 import Data.Map qualified as Map
 import Data.Text qualified as T
 import Language.PureScript qualified as P
+import Language.PureScript.Glob (toInputGlobs, PSCGlobs(..))
 import Language.PureScript.Ide.CaseSplit qualified as CS
 import Language.PureScript.Ide.Command (Command(..), ImportCommand(..), ListType(..))
 import Language.PureScript.Ide.Completion (CompletionOptions, completionFromMatch, getCompletions, getExactCompletions, simpleExport)
@@ -42,7 +43,6 @@
 import Language.PureScript.Ide.Usage (findUsages)
 import System.Directory (getCurrentDirectory, getDirectoryContents, doesDirectoryExist, doesFileExist)
 import System.FilePath ((</>), normalise)
-import System.FilePath.Glob (glob)
 
 -- | Accepts a Command and runs it against psc-ide's State. This is the main
 -- entry point for the server.
@@ -179,8 +179,14 @@
 -- | Finds all matches for the globs specified at the commandline
 findAllSourceFiles :: Ide m => m [FilePath]
 findAllSourceFiles = do
-  globs <- confGlobs . ideConfiguration <$> ask
-  liftIO (concatMapM glob globs)
+  IdeConfiguration{..} <- ideConfiguration <$> ask
+  liftIO $ toInputGlobs $ PSCGlobs
+    { pscInputGlobs = confGlobs
+    , pscInputGlobsFromFile = confGlobsFromFile
+    , pscExcludeGlobs = confGlobsExclude
+    , pscWarnFileTypeNotFound = const $ pure ()
+    }
+
 
 -- | Looks up the ExternsFiles for the given Modulenames and loads them into the
 -- server state. Then proceeds to parse all the specified sourcefiles and
diff --git a/src/Language/PureScript/Ide/Types.hs b/src/Language/PureScript/Ide/Types.hs
--- a/src/Language/PureScript/Ide/Types.hs
+++ b/src/Language/PureScript/Ide/Types.hs
@@ -162,6 +162,8 @@
   { confOutputPath :: FilePath
   , confLogLevel :: IdeLogLevel
   , confGlobs :: [FilePath]
+  , confGlobsFromFile :: Maybe FilePath
+  , confGlobsExclude :: [FilePath]
   }
 
 data IdeEnvironment =
diff --git a/tests/Language/PureScript/Ide/Test.hs b/tests/Language/PureScript/Ide/Test.hs
--- a/tests/Language/PureScript/Ide/Test.hs
+++ b/tests/Language/PureScript/Ide/Test.hs
@@ -22,6 +22,8 @@
     { confLogLevel = LogNone
     , confOutputPath = "output/"
     , confGlobs = ["src/**/*.purs"]
+    , confGlobsFromFile = Nothing
+    , confGlobsExclude = []
     }
 
 runIde' :: IdeConfiguration -> IdeState -> [Command] -> IO ([Either IdeError Success], IdeState)
