hpack 0.15.0 → 0.16.0
raw patch · 9 files changed
+355/−45 lines, 9 filesdep +bytestringPVP ok
version bump matches the API change (PVP)
Dependencies added: bytestring
API changes (from Hackage documentation)
Files
- README.md +192/−0
- hpack.cabal +9/−3
- src/Hpack.hs +6/−5
- src/Hpack/Config.hs +30/−11
- src/Hpack/Run.hs +11/−5
- src/Hpack/Util.hs +10/−6
- test/Hpack/ConfigSpec.hs +76/−2
- test/Hpack/RunSpec.hs +8/−0
- test/Hpack/UtilSpec.hs +13/−13
+ README.md view
@@ -0,0 +1,192 @@+# hpack: An alternative format for Haskell packages++## Examples++ * Given this [package.yaml](https://github.com/sol/hpack/blob/master/package.yaml) running `hpack` will generate [hpack.cabal](https://github.com/sol/hpack/blob/master/hpack.cabal)+ * Given this [package.yaml](https://github.com/zalora/getopt-generics/blob/master/package.yaml) running `hpack` will generate [getopt-generics.cabal](https://github.com/zalora/getopt-generics/blob/master/getopt-generics.cabal)+ * Given this [package.yaml](https://github.com/hspec/sensei/blob/master/package.yaml) running `hpack` will generate [sensei.cabal](https://github.com/hspec/sensei/blob/master/sensei.cabal)+ * Given this [package.yaml](https://github.com/haskell-compat/base-orphans/blob/master/package.yaml) running `hpack` will generate [base-orphans.cabal](https://github.com/haskell-compat/base-orphans/blob/master/base-orphans.cabal)++## Documentation++### Getting started++One easy way of getting started is to use+[hpack-convert](http://hackage.haskell.org/package/hpack-convert) to convert an+existing cabal file into a `package.yaml`.++### Quick-reference++#### Top-level fields++| Hpack | Cabal | Default | Notes | Example |+| --- | --- | --- | --- | --- |+| `name` | `name` | | | |+| `version` | `version` | `0.0.0` | | |+| `synopsis` | `synopsis` | | | |+| `description` | `description` | | | |+| `category` | `category` | | | |+| `stability` | `stability` | | | |+| `homepage` | `homepage` | If `github` given, `<repo>#readme` | | |+| `bug-reports` | `bug-reports` | If `github` given, `<repo>/issues` | | |+| `author` | `author` | | May be a list | |+| `maintainer` | `maintainer` | | May be a list | |+| `copyright` | `copyright` | | May be a list |+| `license` | `license` | | | |+| `license-file` | `license-file` | `LICENSE` if file exists | | |+| `tested-with` | `tested-with` | | | |+| `build-type` | `build-type` | `Simple` | Must be `Simple`, `Configure`, `Make`, or `Custom` | |+| | `cabal-version` | `>= 1.10` or `>= 1.21` | `>= 1.21` if library component has `reexported-modules` field | |+| `extra-source-files` | `extra-source-files` | | Accepts [glob patterns](#file-globbing) | |+| `data-files` | `data-files` | | Accepts [glob patterns](#file-globbing) | |+| `github` | `source-repository head` | | Accepts `user/repo` or `user/repo/subdir` | `github: foo/bar`+| `git` | `source-repository head` | | No effect if `github` given | `git: https://my.repo.com/foo` |+| `flags` | `flag <name>` | | Map from flag name to flag (see [Flags](#flags)) | |+| `library` | `library` | | See [Library fields](#library-fields) | |+| `executables` | `executable <name>` | | Map from executable name to executable (see [Executable fields](#executable-fields)) | |+| `tests` | `test-suite <name>` | | Map from test name to test (see [Test fields](#test-fields)) | |+| `benchmarks` | `benchmark <name>` | | Map from benchmark name to benchmark (see [Benchmark fields](#benchmark-fields)) | |++#### Global top-level fields++These fields are merged with all library, executable, test, and benchmark components.++| Hpack | Cabal | Default | Notes |+| --- | --- | --- | --- |+| `source-dirs` | `hs-source-dirs` | | |+| `default-extensions` | `default-extensions` | | |+| `other-extension` | `other-extensions` | | |+| `ghc-options` | `ghc-options` | | |+| `ghc-prof-options` | `ghc-prof-options` | | |+| `cpp-options` | `cpp-options` | | |+| `cc-options` | `cc-options` | | |+| `c-sources` | `c-sources` | | |+| `extra-lib-dirs` | `extra-lib-dirs` | | |+| `extra-libraries` | `extra-libraries` | | |+| `include-dirs` | `include-dirs` | | |+| `install-includes` | `install-includes` | | |+| `ld-options` | `ld-options` | | |+| `buildable` | `buildable` | | May be overridden by later stanza |+| `dependencies` | `build-depends` | | |+| `build-tools` | `build-tools` | | |+| `when` | | | Accepts a list of conditionals (see [Conditionals](#conditionals)) |++#### <a name="library-fields"></a>Library fields++| Hpack | Cabal | Default | Notes |+| --- | --- | --- | --- |+| `exposed` | `exposed` | | |+| `exposed-modules` | `exposed-modules` | All modules in `source-dirs` less `other-modules` | |+| `other-modules` | `other-modules` | All modules in `source-dirs` less `exposed-modules` | |+| `reexported-modules` | `reexported-modules` | | |+| | `default-language` | `Haskell2010` | |++#### <a name="executable-fields"></a>Executable fields++| Hpack | Cabal | Default | Notes |+| --- | --- | --- | --- |+| `main` | `main-is` | | |+| `other-modules` | `other-modules` | | |+| | `default-language` | `Haskell2010` | |++#### <a name="test-fields"></a>Test fields++| Hpack | Cabal | Default | Notes |+| --- | --- | --- | --- |+| | `type` | `exitcode-stdio-1.0` | |+| `main` | `main-is` | | |+| `other-modules` | `other-modules` | | |+| | `default-language` | `Haskell2010` | |++#### <a name="benchmark-fields"></a>Benchmark fields++| Hpack | Cabal | Default | Notes |+| --- | --- | --- | --- |+| | `type` | `exitcode-stdio-1.0` | |+| `main` | `main-is` | | |+| `other-modules` | `other-modules` | | |+| | `default-language` | `Haskell2010` | |++#### <a name="flags"></a>Flags++| Hpack | Cabal | Default | Notes |+| --- | --- | --- | --- |+| `description` | `description` | | Optional |+| `manual` | `manual` | | Required (unlike Cabal) |+| `default` | `default` | | Required (unlike Cabal) |++#### <a name="conditionals"></a> Conditionals++Conditionals with no else branch:++- Must have a `condition` field+- May have any number of other fields++For example,++ when:+ - condition: os(darwin)+ extra-lib-dirs: lib/darwin++becomes++ if os(darwin)+ extra-lib-dirs:+ lib/darwin++Conditionals with an else branch:++- Must have a `condition` field+- Must have a `then` field, itself an object containing any number of other fields+- Must have a `else` field, itself an object containing any number of other fields+- All other top-level fields are ignored++For example,++ when:+ - condition: flag(fast)+ then:+ ghc-options: -O2+ else:+ ghc-options: -O0++becomes++ if flag(fast)+ ghc-options: -O2+ else+ ghc-options: -O0+++### <a name="file-globbing"></a>File globbing++At place where you can specify a list of files you can also use glob patterns.+Glob patters and ordinary file names can be freely mixed, e.g.:++```yaml+extra-source-files:+ - static/*.js+ - static/site.css+```++Glob patterns are expanded according to the following rules:++ - `?` and `*` are expanded according to POSIX (they match arbitrary+ characters, except for directory separators)+ - `**` is expanded in a `zsh`-like fashion (matching across directory+ separators)+ - `?`, `*` and `**` do not match a `.` at the beginning of a file/directory++### Slides++ - Slides from my talk about `hpack` at the Singapore Haskell meetup:+ http://typeful.net/talks/hpack++## Vim integration++To run `hpack` automatically on modifications to `package.yaml` add the+following to your `~/.vimrc`:++```vim+autocmd BufWritePost package.yaml silent !hpack --silent+```
hpack.cabal view
@@ -1,9 +1,9 @@--- This file has been generated from package.yaml by hpack version 0.14.1.+-- This file has been generated from package.yaml by hpack version 0.15.0. -- -- see: https://github.com/sol/hpack name: hpack-version: 0.15.0+version: 0.16.0 synopsis: An alternative format for Haskell packages category: Development homepage: https://github.com/sol/hpack#readme@@ -14,6 +14,9 @@ build-type: Simple cabal-version: >= 1.10 +extra-source-files:+ README.md+ source-repository head type: git location: https://github.com/sol/hpack@@ -25,6 +28,7 @@ build-depends: base >= 4.7 && < 5 , base-compat >= 0.8+ , bytestring , deepseq , directory , filepath@@ -56,6 +60,7 @@ build-depends: base >= 4.7 && < 5 , base-compat >= 0.8+ , bytestring , deepseq , directory , filepath@@ -73,12 +78,13 @@ main-is: Spec.hs hs-source-dirs: test- , src+ src ghc-options: -Wall cpp-options: -DTEST build-depends: base >= 4.7 && < 5 , base-compat >= 0.8+ , bytestring , deepseq , directory , filepath
src/Hpack.hs view
@@ -17,22 +17,23 @@ import Prelude () import Prelude.Compat -import Control.DeepSeq-import Control.Exception import Control.Monad.Compat+import qualified Data.ByteString as B import Data.List.Compat import Data.Maybe+import qualified Data.Text as T+import Data.Text.Encoding (encodeUtf8) import Data.Version (Version) import qualified Data.Version as Version import System.Environment import System.Exit import System.IO-import System.IO.Error import Text.ParserCombinators.ReadP import Paths_hpack (version) import Hpack.Config import Hpack.Run+import Hpack.Util programVersion :: Version -> String programVersion v = "hpack version " ++ Version.showVersion v@@ -113,14 +114,14 @@ hpackWithVersionResult :: Version -> FilePath -> IO Result hpackWithVersionResult v dir = do (warnings, cabalFile, new) <- run dir- old <- either (const Nothing) (Just . splitHeader) <$> tryJust (guard . isDoesNotExistError) (readFile cabalFile >>= (return $!!))+ old <- fmap splitHeader <$> tryReadFile cabalFile let oldVersion = fmap fst old >>= extractVersion status <- if (oldVersion <= Just v) then if (fmap snd old == Just (lines new)) then return OutputUnchanged else do- writeFile cabalFile $ header v ++ new+ B.writeFile cabalFile $ encodeUtf8 $ T.pack $ header v ++ new return Generated else return AlreadyGeneratedByNewerHpack
src/Hpack/Config.hs view
@@ -462,13 +462,19 @@ mkPackage :: FilePath -> (CaptureUnknownFields (Section PackageConfig)) -> IO ([String], Package) mkPackage dir (CaptureUnknownFields unknownFields globalOptions@Section{sectionData = PackageConfig{..}}) = do- let name = fromMaybe (takeBaseName dir) packageConfigName+ let+ (nameWarnings, name) = maybe (["Package name not specified, inferred " ++ show inferredName], inferredName) ((,) []) packageConfigName+ where inferredName = takeBaseName dir - mLibrary <- mapM (toLibrary dir name globalOptions) mLibrarySection- executables <- toExecutables dir globalOptions (map (fmap captureUnknownFieldsValue) executableSections)- tests <- toExecutables dir globalOptions (map (fmap captureUnknownFieldsValue) testsSections)- benchmarks <- toExecutables dir globalOptions (map (fmap captureUnknownFieldsValue) benchmarkSections)+ libraryResult <- mapM (toLibrary dir name globalOptions) mLibrarySection+ let+ mLibrary = fmap snd libraryResult+ libraryWarnings = maybe [] fst libraryResult + (executablesWarnings, executables) <- toExecutables dir globalOptions (map (fmap captureUnknownFieldsValue) executableSections)+ (testsWarnings, tests) <- toExecutables dir globalOptions (map (fmap captureUnknownFieldsValue) testsSections)+ (benchmarksWarnings, benchmarks) <- toExecutables dir globalOptions (map (fmap captureUnknownFieldsValue) benchmarkSections)+ licenseFileExists <- doesFileExist (dir </> "LICENSE") missingSourceDirs <- nub . sort <$> filterM (fmap not <$> doesDirectoryExist . (dir </>)) (@@ -479,10 +485,10 @@ ) (extraSourceFilesWarnings, extraSourceFiles) <-- expandGlobs dir (fromMaybeList packageConfigExtraSourceFiles)+ expandGlobs "extra-source-files" dir (fromMaybeList packageConfigExtraSourceFiles) (dataFilesWarnings, dataFiles) <-- expandGlobs dir (fromMaybeList packageConfigDataFiles)+ expandGlobs "data-files" dir (fromMaybeList packageConfigDataFiles) let pkg = Package { packageName = name@@ -512,11 +518,16 @@ warnings = formatUnknownFields "package description" unknownFields+ ++ nameWarnings ++ flagWarnings ++ maybe [] (formatUnknownFields "library section") (captureUnknownFieldsFields <$> packageConfigLibrary) ++ formatUnknownSectionFields "executable" executableSections ++ formatUnknownSectionFields "test" testsSections ++ formatMissingSourceDirs missingSourceDirs+ ++ libraryWarnings+ ++ executablesWarnings+ ++ testsWarnings+ ++ benchmarksWarnings ++ extraSourceFilesWarnings ++ dataFilesWarnings @@ -587,8 +598,13 @@ where fromGithub = (++ "/issues") . sourceRepositoryUrl <$> github -toLibrary :: FilePath -> String -> Section global -> Section LibrarySection -> IO (Section Library)-toLibrary dir name globalOptions library = traverse fromLibrarySection sect+expandCSources :: FilePath -> Section a -> IO ([String], Section a)+expandCSources dir sect@Section{..} = do+ (warnings, files) <- expandGlobs "c-sources" dir sectionCSources+ return (warnings, sect {sectionCSources = files})++toLibrary :: FilePath -> String -> Section global -> Section LibrarySection -> IO ([String], Section Library)+toLibrary dir name globalOptions library = traverse fromLibrarySection sect >>= expandCSources dir where sect :: Section LibrarySection sect = mergeSections globalOptions library@@ -603,8 +619,11 @@ reexportedModules = fromMaybeList librarySectionReexportedModules return (Library librarySectionExposed exposedModules otherModules reexportedModules) -toExecutables :: FilePath -> Section global -> [(String, Section ExecutableSection)] -> IO [Section Executable]-toExecutables dir globalOptions executables = mapM toExecutable sections+toExecutables :: FilePath -> Section global -> [(String, Section ExecutableSection)] -> IO ([String], [Section Executable])+toExecutables dir globalOptions executables = do+ result <- mapM toExecutable sections >>= mapM (expandCSources dir)+ let (warnings, xs) = unzip result+ return (concat warnings, xs) where sections :: [(String, Section ExecutableSection)] sections = map (fmap $ mergeSections globalOptions) executables
src/Hpack/Run.hs view
@@ -14,6 +14,7 @@ , renderConditional , renderFlag , renderSourceRepository+, renderDirectories , formatDescription #endif ) where@@ -203,17 +204,17 @@ renderSection :: Section a -> [Element] renderSection Section{..} = [- renderSourceDirs sectionSourceDirs+ renderDirectories "hs-source-dirs" sectionSourceDirs , renderDefaultExtensions sectionDefaultExtensions , renderOtherExtensions sectionOtherExtensions , renderGhcOptions sectionGhcOptions , renderGhcProfOptions sectionGhcProfOptions , renderCppOptions sectionCppOptions , renderCcOptions sectionCcOptions- , Field "include-dirs" (LineSeparatedList sectionIncludeDirs)+ , renderDirectories "include-dirs" sectionIncludeDirs , Field "install-includes" (LineSeparatedList sectionInstallIncludes) , Field "c-sources" (LineSeparatedList sectionCSources)- , Field "extra-lib-dirs" (LineSeparatedList sectionExtraLibDirs)+ , renderDirectories "extra-lib-dirs" sectionExtraLibDirs , Field "extra-libraries" (LineSeparatedList sectionExtraLibraries) , renderLdOptions sectionLdOptions , renderDependencies sectionDependencies@@ -232,8 +233,13 @@ defaultLanguage :: Element defaultLanguage = Field "default-language" "Haskell2010" -renderSourceDirs :: [String] -> Element-renderSourceDirs = Field "hs-source-dirs" . CommaSeparatedList+renderDirectories :: String -> [String] -> Element+renderDirectories name = Field name . LineSeparatedList . replaceDots+ where+ replaceDots = map replaceDot+ replaceDot xs = case xs of+ "." -> "./."+ _ -> xs renderExposedModules :: [String] -> Element renderExposedModules = Field "exposed-modules" . LineSeparatedList
src/Hpack/Util.hs view
@@ -19,14 +19,18 @@ import Prelude.Compat import Control.Applicative-import Control.DeepSeq import Control.Exception import Control.Monad.Compat import Data.Aeson.Types+import qualified Data.ByteString as B import Data.Char import Data.Data import Data.List.Compat hiding (sort) import Data.Ord+import qualified Data.Text as T+import Data.Text.Encoding (decodeUtf8With)+import Data.Text.Encoding.Error (lenientDecode)+import System.IO.Error import System.Directory import System.FilePath import qualified System.FilePath.Posix as Posix@@ -95,20 +99,20 @@ tryReadFile :: FilePath -> IO (Maybe String) tryReadFile file = do- r <- try (readFile file) :: IO (Either IOException String)- return $!! either (const Nothing) Just r+ r <- tryJust (guard . isDoesNotExistError) (B.readFile file)+ return $ either (const Nothing) (Just . T.unpack . decodeUtf8With lenientDecode) r toPosixFilePath :: FilePath -> FilePath toPosixFilePath = Posix.joinPath . splitDirectories -expandGlobs :: FilePath -> [String] -> IO ([String], [FilePath])-expandGlobs dir patterns = do+expandGlobs :: String -> FilePath -> [String] -> IO ([String], [FilePath])+expandGlobs name dir patterns = do files <- (fst <$> globDir compiledPatterns dir) >>= mapM removeDirectories let warnings = [warn pattern | ([], pattern) <- zip files patterns] return (warnings, combineResults files) where combineResults = nub . sort . map (toPosixFilePath . makeRelative dir) . concat- warn pattern = "Specified pattern " ++ show pattern ++ " for extra-source-files does not match any files"+ warn pattern = "Specified pattern " ++ show pattern ++ " for " ++ name ++ " does not match any files" compiledPatterns = map (compileWith options) patterns removeDirectories = filterM doesFileExist options = CompOptions {
test/Hpack/ConfigSpec.hs view
@@ -112,10 +112,10 @@ let input = [i| c-sources: - foo.c- - bar.c+ - bar/*.c |] captureUnknownFieldsValue <$> decodeEither input- `shouldBe` Right (section Empty){sectionCSources = ["foo.c", "bar.c"]}+ `shouldBe` Right (section Empty){sectionCSources = ["foo.c", "bar/*.c"]} it "accepts extra-lib-dirs" $ do let input = [i|@@ -306,6 +306,7 @@ describe "readPackageConfig" $ do it "warns on unknown fields" $ do withPackageWarnings_ [i|+ name: foo bar: 23 baz: 42 |]@@ -317,6 +318,7 @@ it "warns on unknown fields in when block, list" $ do withPackageWarnings_ [i|+ name: foo when: - condition: impl(ghc) bar: 23@@ -330,6 +332,7 @@ it "warns on unknown fields in when block, single" $ do withPackageWarnings_ [i|+ name: foo when: condition: impl(ghc) github: foo/bar@@ -342,6 +345,21 @@ ] ) + it "warns on missing name" $ do+ withPackageWarnings_ [i|+ {}+ |]+ (`shouldBe` [+ "Package name not specified, inferred \"foo\""+ ]+ )++ it "infers name" $ do+ withPackageConfig_ [i|+ {}+ |]+ (packageName >>> (`shouldBe` "foo"))+ it "accepts name" $ do withPackageConfig_ [i| name: bar@@ -496,6 +514,7 @@ it "warns on unknown fields in flag sections" $ do withPackageWarnings_ [i|+ name: foo flags: integration-tests: description: Run the integration test suite@@ -626,6 +645,7 @@ context "when reading library section" $ do it "warns on unknown fields" $ do withPackageWarnings_ [i|+ name: foo library: bar: 23 baz: 42@@ -690,6 +710,30 @@ |] (packageLibrary >>> (`shouldBe` Just (section library) {sectionBuildTools = ["alex", "happy"]})) + it "accepts c-sources" $ do+ withPackageConfig [i|+ library:+ c-sources:+ - cbits/*.c+ |]+ (do+ touch "cbits/foo.c"+ touch "cbits/bar.c"+ )+ (packageLibrary >>> (`shouldBe` Just (section library) {sectionCSources = ["cbits/bar.c", "cbits/foo.c"]}))++ it "accepts global c-sources" $ do+ withPackageConfig [i|+ c-sources:+ - cbits/*.c+ library: {}+ |]+ (do+ touch "cbits/foo.c"+ touch "cbits/bar.c"+ )+ (packageLibrary >>> (`shouldBe` Just (section library) {sectionCSources = ["cbits/bar.c", "cbits/foo.c"]}))+ it "allows to specify exposed" $ do withPackageConfig_ [i| library:@@ -755,6 +799,7 @@ context "when reading executable section" $ do it "warns on unknown fields" $ do withPackageWarnings_ [i|+ name: foo executables: foo: main: Main.hs@@ -915,10 +960,38 @@ |] (`shouldBe` package {packageExecutables = [(section $ executable "foo" "driver/Main.hs") {sectionGhcProfOptions = ["-fprof-auto"]}]}) + it "accepts c-sources" $ do+ withPackageConfig [i|+ executables:+ foo:+ main: driver/Main.hs+ c-sources:+ - cbits/*.c+ |]+ (do+ touch "cbits/foo.c"+ touch "cbits/bar.c"+ )+ (`shouldBe` package {packageExecutables = [(section $ executable "foo" "driver/Main.hs") {sectionCSources = ["cbits/bar.c", "cbits/foo.c"]}]}) + it "accepts global c-sources" $ do+ withPackageConfig [i|+ c-sources:+ - cbits/*.c+ executables:+ foo:+ main: driver/Main.hs+ |]+ (do+ touch "cbits/foo.c"+ touch "cbits/bar.c"+ )+ (`shouldBe` package {packageExecutables = [(section $ executable "foo" "driver/Main.hs") {sectionCSources = ["cbits/bar.c", "cbits/foo.c"]}]})+ context "when reading test section" $ do it "warns on unknown fields" $ do withPackageWarnings_ [i|+ name: foo tests: foo: main: Main.hs@@ -975,6 +1048,7 @@ context "when a specified source directory does not exist" $ do it "warns" $ do withPackageWarnings [i|+ name: foo source-dirs: - some-dir - some-existing-dir
test/Hpack/RunSpec.hs view
@@ -323,3 +323,11 @@ , " location: https://github.com/hspec/hspec" , " subdir: hspec-core" ]++ describe "renderDirectories" $ do+ it "replaces . with ./. (for compatibility with cabal syntax)" $ do+ (render defaultRenderSettings 0 $ renderDirectories "name" ["."])+ `shouldBe` [+ "name:"+ , " ./."+ ]
test/Hpack/UtilSpec.hs view
@@ -97,22 +97,22 @@ describe "expandGlobs" $ around withTempDirectory $ do it "accepts simple files" $ \dir -> do touch (dir </> "foo.js")- expandGlobs dir ["foo.js"] `shouldReturn` ([], ["foo.js"])+ expandGlobs "field-name" dir ["foo.js"] `shouldReturn` ([], ["foo.js"]) it "removes duplicates" $ \dir -> do touch (dir </> "foo.js")- expandGlobs dir ["foo.js", "*.js"] `shouldReturn` ([], ["foo.js"])+ expandGlobs "field-name" dir ["foo.js", "*.js"] `shouldReturn` ([], ["foo.js"]) it "rejects directories" $ \dir -> do touch (dir </> "foo") createDirectory (dir </> "bar")- expandGlobs dir ["*"] `shouldReturn` ([], ["foo"])+ expandGlobs "field-name" dir ["*"] `shouldReturn` ([], ["foo"]) it "rejects character ranges" $ \dir -> do touch (dir </> "foo1") touch (dir </> "foo2") touch (dir </> "foo[1,2]")- expandGlobs dir ["foo[1,2]"] `shouldReturn` ([], ["foo[1,2]"])+ expandGlobs "field-name" dir ["foo[1,2]"] `shouldReturn` ([], ["foo[1,2]"]) context "when expanding *" $ do it "expands by extension" $ \dir -> do@@ -122,36 +122,36 @@ , "files/baz.js"] mapM_ (touch . (dir </>)) files touch (dir </> "files/foo.hs")- expandGlobs dir ["files/*.js"] `shouldReturn` ([], sort files)+ expandGlobs "field-name" dir ["files/*.js"] `shouldReturn` ([], sort files) it "rejects dot-files" $ \dir -> do touch (dir </> "foo/bar") touch (dir </> "foo/.baz")- expandGlobs dir ["foo/*"] `shouldReturn` ([], ["foo/bar"])+ expandGlobs "field-name" dir ["foo/*"] `shouldReturn` ([], ["foo/bar"]) it "accepts dot-files when explicitly asked to" $ \dir -> do touch (dir </> "foo/bar") touch (dir </> "foo/.baz")- expandGlobs dir ["foo/.*"] `shouldReturn` ([], ["foo/.baz"])+ expandGlobs "field-name" dir ["foo/.*"] `shouldReturn` ([], ["foo/.baz"]) it "matches at most one directory component" $ \dir -> do touch (dir </> "foo/bar/baz.js") touch (dir </> "foo/bar.js")- expandGlobs dir ["*/*.js"] `shouldReturn` ([], ["foo/bar.js"])+ expandGlobs "field-name" dir ["*/*.js"] `shouldReturn` ([], ["foo/bar.js"]) context "when expanding **" $ do it "matches arbitrary many directory components" $ \dir -> do let file = "foo/bar/baz.js" touch (dir </> file)- expandGlobs dir ["**/*.js"] `shouldReturn` ([], [file])+ expandGlobs "field-name" dir ["**/*.js"] `shouldReturn` ([], [file]) context "when a pattern does not match anything" $ do it "warns" $ \dir -> do- expandGlobs dir ["foo"] `shouldReturn`- (["Specified pattern \"foo\" for extra-source-files does not match any files"], [])+ expandGlobs "field-name" dir ["foo"] `shouldReturn`+ (["Specified pattern \"foo\" for field-name does not match any files"], []) context "when a pattern only matches a directory" $ do it "warns" $ \dir -> do createDirectory (dir </> "foo")- expandGlobs dir ["foo"] `shouldReturn`- (["Specified pattern \"foo\" for extra-source-files does not match any files"], [])+ expandGlobs "field-name" dir ["foo"] `shouldReturn`+ (["Specified pattern \"foo\" for field-name does not match any files"], [])