yarn-lock 0.4.0 → 0.4.1
raw patch · 9 files changed
+90/−55 lines, 9 filesdep +quickcheck-instancesdep ~basedep ~eitherdep ~megaparsec
Dependencies added: quickcheck-instances
Dependency ranges changed: base, either, megaparsec, protolude
Files
- CHANGELOG.md +11/−0
- src/Data/MultiKeyedMap.hs +3/−2
- src/Yarn/Lock/File.hs +1/−1
- src/Yarn/Lock/Helpers.hs +6/−1
- src/Yarn/Lock/Parse.hs +21/−17
- tests/TestFile.hs +1/−6
- tests/TestMultiKeyedMap.hs +1/−0
- tests/TestParse.hs +16/−1
- yarn-lock.cabal +30/−27
CHANGELOG.md view
@@ -5,6 +5,17 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). +## [0.5.0] - 2018-05-15++### Changed++- Update the parser to `megaparsec 6.*`+- Raise `protolude` minimal version to `0.2.*`++### Fixed++- Import missing quickcheck `NonEmpty` instances from `quickcheck-orphans`+ ## [0.4.0] - 2017-10-07 ### Changed
src/Data/MultiKeyedMap.hs view
@@ -32,6 +32,7 @@ import qualified Data.List.NonEmpty as NE import Data.Proxy (Proxy(..)) import qualified Data.Tuple as Tuple+import GHC.Stack (HasCallStack) import qualified Text.Show as Show -- TODO: add time behaviour of functions to docstrings@@ -87,10 +88,10 @@ {-# INLINE traverse #-} -- | Find value at key. Partial. See 'M.!'.-at :: (Ord k) => MKMap k v -> k -> v+at :: (HasCallStack, Ord k) => MKMap k v -> k -> v at MKMap{keyMap, valMap} k = valMap M.! (keyMap M.! k) -- | Operator alias of 'at'.-(!) :: (Ord k) => MKMap k v -> k -> v+(!) :: (HasCallStack, Ord k) => MKMap k v -> k -> v (!) = at {-# INLINABLE (!) #-} {-# INLINABLE at #-}
src/Yarn/Lock/File.hs view
@@ -20,7 +20,7 @@ , ConversionError(..) ) where -import Protolude+import Protolude hiding (hash, getField) import qualified Data.List.NonEmpty as NE import qualified Data.Map.Strict as M import qualified Data.Text as Text
src/Yarn/Lock/Helpers.hs view
@@ -15,6 +15,7 @@ import Protolude import qualified Data.List as L+import GHC.Stack (HasCallStack) import qualified Data.MultiKeyedMap as MKM @@ -26,7 +27,11 @@ -- Node packages often contain those and the yarn lockfile -- does not yet eliminate them, which may lead to infinite -- recursions.-decycle :: Lockfile -> Lockfile+--+-- Invariant: Every dependency entry in each package in the+-- 'Lockfile' *must* point to an existing key, otherwise+-- the function crashes.+decycle :: HasCallStack => Lockfile -> Lockfile decycle lf = goFold [] lf (MKM.keys lf) -- TODO: probably rewrite with State where
src/Yarn/Lock/Parse.hs view
@@ -17,26 +17,28 @@ , packageEntry -- * Internal Parsers , field, nestedField, simpleField-, packageKeys, packageKey+, packageKeys ) where -import Protolude hiding (try)+import Protolude hiding (try, some, many) import qualified Data.Char as Ch import qualified Data.List.NonEmpty as NE import qualified Data.Text as T import qualified Data.Map.Strict as M import Control.Monad (fail) -import Text.Megaparsec as MP hiding (space)-import Text.Megaparsec.Text-import qualified Text.Megaparsec.Lexer as MPL+import Text.Megaparsec as MP+import qualified Text.Megaparsec.Char as MP+import qualified Text.Megaparsec.Char.Lexer as MPL -- import qualified Data.MultiKeyedMap as MKM -- import Data.Proxy (Proxy(..)) import qualified Yarn.Lock.Types as YLT +type Parser = Parsec Void Text + -- | The @yarn.lock@ format doesn’t specifically include a fixed scheme, -- it’s just an unnecessary custom version of a list of fields. --@@ -54,8 +56,10 @@ -- | Parse a complete yarn.lock into an abstract syntax tree, -- keeping the source positions of each package entry. packageList :: Parser [Package]-packageList = many $ (skipMany (comment <|> eol)) *> packageEntry- where comment = char '#' *> manyTill anyChar eol+packageList = MP.many $ (skipMany (comment <|> MP.string "\n")) *> packageEntry+ where+ comment :: Parser (Tokens Text)+ comment = MP.char '#' *> takeWhileP Nothing (/= '\n') -- | A single Package. --@@ -90,8 +94,8 @@ -- @ packageKeys :: Parser (NE.NonEmpty YLT.PackageKey) packageKeys = label "package keys" $ do- firstEls <- many (try $ lexeme $ packageKey ":," <* char ',')- lastEl <- packageKey ":" <* char ':'+ firstEls <- many (try $ lexeme $ packageKey ":," <* MP.char ',')+ lastEl <- packageKey ":" <* MP.char ':' pure $ NE.fromList $ firstEls <> [lastEl] -- | A packageKey is @\<package-name\>\@\<semver\>@;@@ -106,7 +110,7 @@ where pkgKey :: [Char] -> Parser YLT.PackageKey pkgKey valueChars = label "package key" $ do- key <- someTextOf (noneOf valueChars)+ key <- someTextOf (MP.noneOf valueChars) -- okay, here’s the rub: -- `@` is used for separation, but package names containing `@` -- are totally a thing. As first character, as well.@@ -136,7 +140,7 @@ <?> "simple field" where valueChars, strValueChars :: Parser Text- valueChars = someTextOf (noneOf ("\n\r\"" :: [Char]))+ valueChars = someTextOf (MP.noneOf ("\n\r\"" :: [Char])) strSymbolChars = inString $ symbolChars strValueChars = inString $ valueChars -- as with packageKey semvers, this can be empty@@ -146,7 +150,7 @@ -- we get another block with deeper indentation. nestedField :: Parser (Text, PackageFields) nestedField = label "nested field" $- indentedFieldsWithHeader (symbolChars <* char ':')+ indentedFieldsWithHeader (symbolChars <* MP.char ':') -- internal parsers@@ -172,7 +176,7 @@ -- Update: npm doesn’t specify the package name format, at all. -- Apart from the length. symbolChars :: Parser Text-symbolChars = label "key symbol" $ someTextOf $ satisfy+symbolChars = label "key symbol" $ someTextOf $ MP.satisfy (\c -> Ch.isAscii c && (Ch.isLower c || Ch.isUpper c || Ch.isNumber c || c `elem` special)) where special = "-_.@/" :: [Char]@@ -185,7 +189,7 @@ -- | parse everything as inside a string inString :: Parser a -> Parser a-inString = between (char '"') (char '"')+inString = between (MP.char '"') (MP.char '"') -- lexers @@ -193,7 +197,7 @@ space :: Parser () space = MPL.space (void MP.spaceChar) (MPL.skipLineComment "# ")- (void $ satisfy (const False))+ (void $ MP.satisfy (const False)) -- | Parse a lexeme. lexeme :: Parser a -> Parser a@@ -202,6 +206,6 @@ -- | Ensure parser is not indented. nonIndented :: Parser a -> Parser a nonIndented = MPL.nonIndented space-indentBlock :: ParsecT Dec Text Identity (MPL.IndentOpt (ParsecT Dec Text Identity) a b)- -> ParsecT Dec Text Identity a+indentBlock :: Parser (MPL.IndentOpt Parser a b)+ -> Parser a indentBlock = MPL.indentBlock space
tests/TestFile.hs view
@@ -64,15 +64,10 @@ (File.MissingField "version" NE.:| [File.UnknownRemoteType]) $ emptyAst [] --- will be in protolude soon-infixl 4 <&>-(<&>) :: Functor f => f a -> (a -> b) -> f b-(<&>) = flip fmap- astToPackageSuccess :: Parse.PackageFields -> IO T.Package astToPackageSuccess ast = case File.astToPackage ast of (Left errs) -> do- assertFailure ("should have succeded, but:\n" <> show errs)+ _ <- assertFailure ("should have succeded, but:\n" <> show errs) panic "not reached" (Right pkg) -> pure pkg
tests/TestMultiKeyedMap.hs view
@@ -7,6 +7,7 @@ import Test.Tasty (TestTree) import Test.Tasty.TH import Test.Tasty.QuickCheck+import Test.QuickCheck.Instances () -- orphans! import qualified Data.MultiKeyedMap as MKM
tests/TestParse.hs view
@@ -14,6 +14,21 @@ import Yarn.Lock.Types import Yarn.Lock.Parse +startComment :: Text+startComment = [text|+ # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.+ # yarn lockfile v1+ dummy-package@foo:+ version: foo+ |]++case_startCommentEmptyPackageList :: Assertion+case_startCommentEmptyPackageList = do+ parseSuccess packageList startComment+ >>= \((Keyed keys _) : _) -> do+ assertBool "only foo"+ (keys == pure (PackageKey "dummy-package" "foo"))+ -- registryPackage :: Text -- registryPackage = [text| -- accepts@1.3.3, accepts@~1.3.3:@@ -114,7 +129,7 @@ case MP.parse parser "" string of (Right a) -> pure a (Left err) -> do- assertFailure ("parse should succeed, but: \n"+ _ <- assertFailure ("parse should succeed, but: \n" <> MP.parseErrorPretty err <> "for input\n" <> toS string <> "\n\"") panic "not reached"
yarn-lock.cabal view
@@ -1,9 +1,11 @@--- This file has been generated from package.yaml by hpack version 0.18.1.+-- This file has been generated from package.yaml by hpack version 0.27.0. -- -- see: https://github.com/sol/hpack+--+-- hash: bfe08f55682f41944db8ec965c30c279fd7d244188df301b7d3d22a65d0c9df7 name: yarn-lock-version: 0.4.0+version: 0.4.1 synopsis: Represent and parse yarn.lock files description: Types and parser for the lock file format of the npm successor yarn. All modules should be imported qualified. category: Data@@ -24,16 +26,6 @@ location: https://github.com/Profpatsch/yarn-lock library- hs-source-dirs:- src- ghc-options: -Wall- build-depends:- base == 4.9.*- , containers- , text- , megaparsec == 5.*- , protolude == 0.1.* || == 0.2.*- , either == 4.* exposed-modules: Data.MultiKeyedMap Yarn.Lock@@ -43,31 +35,42 @@ Yarn.Lock.Types other-modules: Paths_yarn_lock+ hs-source-dirs:+ src+ ghc-options: -Wall+ build-depends:+ base ==4.*+ , containers+ , either >=4 && <6+ , megaparsec ==6.*+ , protolude ==0.2.*+ , text default-language: Haskell2010 test-suite yarn-lock-tests type: exitcode-stdio-1.0 main-is: Test.hs+ other-modules:+ TestFile+ TestMultiKeyedMap+ TestParse+ Paths_yarn_lock hs-source-dirs: tests ghc-options: -Wall build-depends:- base == 4.9.*+ ansi-wl-pprint >=0.6+ , base ==4.* , containers+ , either >=4 && <6+ , megaparsec ==6.*+ , neat-interpolation >=0.3+ , protolude+ , quickcheck-instances ==0.3.*+ , tasty >=0.11+ , tasty-hunit >=0.9+ , tasty-quickcheck >=0.8+ , tasty-th >=0.1.7 , text- , megaparsec == 5.*- , protolude == 0.1.* || == 0.2.*- , either == 4.* , yarn-lock- , ansi-wl-pprint >= 0.6- , tasty >= 0.11- , tasty-th >= 0.1.7- , tasty-hunit >= 0.9- , tasty-quickcheck >= 0.8- , protolude- , neat-interpolation >= 0.3- other-modules:- TestFile- TestMultiKeyedMap- TestParse default-language: Haskell2010