yarn-lock 0.3.1 → 0.3.2
raw patch · 5 files changed
+75/−30 lines, 5 files
Files
- CHANGELOG.md +5/−0
- src/Data/MultiKeyedMap.hs +10/−0
- src/Yarn/Lock/Parse.hs +36/−24
- tests/TestParse.hs +23/−5
- yarn-lock.cabal +1/−1
CHANGELOG.md view
@@ -5,6 +5,11 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). +## [0.3.2] - 2017-10-02++### Fixed+- Support parsing packages with `@` in the package name + ## [0.3.1] - 2017-08-16 ### Added
src/Data/MultiKeyedMap.hs view
@@ -23,6 +23,7 @@ , mkMKMap, fromList, toList , insert , flattenKeys, keys, values+, mapKeys ) where import qualified Data.Map.Strict as M@@ -149,7 +150,15 @@ ins = newVal [k] v m upd ik = MKMap { keyMap, highestIk, valMap = M.insert ik v valMap } +-- | Map function over all keys. See 'M.mapKeys'.+--+-- TODO: Might leak space if a k is mapped to an+-- already existing k', because k’s value might not be+-- referenced anymore.+mapKeys :: (Ord k') => (k -> k') -> MKMap k v -> MKMap k' v+mapKeys f MKMap{..} = MKMap { keyMap = M.mapKeys f keyMap, .. } + -- | Helper, assumes there is no such value already. -- Will leak space otherwise! --@@ -163,3 +172,4 @@ , highestIk = next , valMap = M.insert next v valMap } where next = succ highestIk+
src/Yarn/Lock/Parse.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE NoImplicitPrelude, GeneralizedNewtypeDeriving #-}+{-# LANGUAGE NoImplicitPrelude, GeneralizedNewtypeDeriving, OverloadedStrings #-} {-| Module : Yarn.Lock.Parse Description : Parser for yarn.lock files@@ -15,14 +15,17 @@ -- ** Parsers , packageList , packageEntry--- = internal parsers-, field, packageKeys, packageKey+-- * Internal Parsers+, field, nestedField, simpleField+, packageKeys, packageKey ) where import Protolude hiding (try) import qualified Data.Char as Ch-import qualified Data.Text as Text+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@@ -30,7 +33,7 @@ -- import qualified Data.MultiKeyedMap as MKM -- import Data.Proxy (Proxy(..)) -import qualified Yarn.Lock.Types as T+import qualified Yarn.Lock.Types as YLT -- | The @yarn.lock@ format doesn’t specifically include a fixed scheme,@@ -44,7 +47,7 @@ -- | A parsed 'Package' AST has one or more keys, a position in the original files -- and a collection of fields.-type Package = T.Keyed (SourcePos, PackageFields)+type Package = YLT.Keyed (SourcePos, PackageFields) -- | Parse a complete yarn.lock into an abstract syntax tree,@@ -67,8 +70,9 @@ -- source-map "^0.4.4" -- optionalDependencies: -- uglify-js "^2.6"+-- " -- @-packageEntry :: Parser (T.Keyed (SourcePos, PackageFields))+packageEntry :: Parser (YLT.Keyed (SourcePos, PackageFields)) packageEntry = label "package entry" $ do pos <- getPosition -- A package entry is a non-indented@@ -76,14 +80,14 @@ -- block that has a header of package keys -- and an indented part that contains fields $ indentedFieldsWithHeader packageKeys- pure $ T.Keyed keys (pos, pkgs)+ pure $ YLT.Keyed keys (pos, pkgs) -- | The list of PackageKeys that index the same Package -- -- @ -- align-text@^0.1.1, align-text@^0.1.3:\\n -- @-packageKeys :: Parser [T.PackageKey]+packageKeys :: Parser [YLT.PackageKey] packageKeys = label "package keys" $ do firstEls <- many (try $ lexeme $ packageKey ":," <* char ',') lastEl <- packageKey ":" <* char ':'@@ -92,21 +96,26 @@ -- | A packageKey is @\<package-name\>\@\<semver\>@; -- -- If the semver contains spaces, it is also quoted with @"@.-packageKey :: [Char] -> Parser T.PackageKey+packageKey :: [Char] -> Parser YLT.PackageKey packageKey separators = inString (pkgKey "\"") -- if no string delimiters is used we need to check for the separators -- this file format is shit :< <|> pkgKey separators <?> "package key" where- pkgKey valueChars = do- pkgName <- someTextOf (noneOf "@") <?> "package name part of package key"- _ <- char '@'- semver <- (someTextOf (noneOf valueChars))- <|> (pure Text.empty <?> "an empty semver")- <?> "semver part of package key"- pure $ T.PackageKey pkgName semver-+ pkgKey :: [Char] -> Parser YLT.PackageKey+ pkgKey valueChars = label "package key" $ do+ key <- someTextOf (noneOf valueChars)+ -- okay, here’s the rub:+ -- `@` is used for separation, but package names containing `@`+ -- are totally a thing. As first character, as well.+ -- As are package keys without any semver whatsoever, thus ending+ -- with `@`.+ -- Did I mention this file format is big chunks of elephant shit?+ case (\(n, v) -> (T.dropEnd 1 n, v)) $ T.breakOnEnd "@" key of+ ("", _) -> fail "packageKey: package name can not be empty"+ (n, "") -> pure $ YLT.PackageKey n ""+ (n, v) -> pure $ YLT.PackageKey n v -- | Either a simple or a nested field. field :: Parser (Text, Either Text PackageFields)@@ -115,20 +124,22 @@ simple = fmap Left <$> simpleField nested = fmap Right <$> nestedField --- | A key-value pair, separated by space. The value is enclosed in "".+-- | A key-value pair, separated by space.+-- Key any value may be enclosed in "". -- Returns key and value. simpleField :: Parser (Text, Text)-simpleField = (,) <$> lexeme symbolChars+simpleField = (,) <$> lexeme (strSymbolChars <|> symbolChars) -- valueChars may be in Strings or maybe not >: -- this file format is absolute garbage <*> (strValueChars <|> valueChars) <?> "simple field" where valueChars, strValueChars :: Parser Text- valueChars = someTextOf (noneOf "\n\r\"")+ valueChars = someTextOf (noneOf ("\n\r\"" :: [Char]))+ strSymbolChars = inString $ symbolChars strValueChars = inString $ valueChars -- as with packageKey semvers, this can be empty- <|> (pure Text.empty <?> "an empty value field")+ <|> (pure T.empty <?> "an empty value field") -- | Similar to a @simpleField@, but instead of a string -- we get another block with deeper indentation.@@ -162,13 +173,14 @@ symbolChars :: Parser Text symbolChars = label "key symbol" $ someTextOf $ satisfy (\c -> Ch.isAscii c &&- (Ch.isLower c || Ch.isUpper c || Ch.isNumber c || c `elem` "-_."))+ (Ch.isLower c || Ch.isUpper c || Ch.isNumber c || c `elem` special))+ where special = "-_.@/" :: [Char] -- text versions of parsers & helpers someTextOf :: Parser Char -> Parser Text-someTextOf c = Text.pack <$> some c+someTextOf c = T.pack <$> some c -- | parse everything as inside a string inString :: Parser a -> Parser a
tests/TestParse.hs view
@@ -46,10 +46,23 @@ dependencies: core-util-is "~1.0.0" is.array ""- string_decoder "~0.10.x"+ "@types/string_decoder" "~0.10.x" johnny-dep 2.3.4 |] +nestedFieldExample :: Text+nestedFieldExample = [text|+ dependencies:+ core-util-is "~1.0.0"+ is.array ""+ "@types/string_decoder" "~0.10.x"+ johnny-dep 2.3.4+ |]++case_nestedField :: Assertion+case_nestedField = do+ void $ parseSuccess nestedField nestedFieldExample+ case_NestedPackage :: Assertion case_NestedPackage = do assertBool "there is unicode" (all Ch.isAscii (toS nestedPackage :: [Char]))@@ -62,7 +75,9 @@ (Just (Right (PackageFields nested))) -> do assertEqual "nested keys" 4 $ length nested assertEqual "dep exists" (Just (Left "2.3.4"))- $ Map.lookup "johnny-dep" nested + $ Map.lookup "johnny-dep" nested+ assertEqual "there can be @" (Just (Left "~0.10.x"))+ $ Map.lookup "@types/string_decoder" nested case_PackageField :: IO () case_PackageField = do@@ -81,13 +96,16 @@ case_PackageKey :: Assertion case_PackageKey = do- let key = "foo@^1.3.4, bar@blafoo234, xnu@:\n"+ let key = "foo@^1.3.4, bar@blafoo234, xnu@, @types/foo@@:\n" parseSuccess packageKeys key >>= \keys -> do- keys @=? [ PackageKey "foo" "^1.3.4"+ keys @?= [ PackageKey "foo" "^1.3.4" , PackageKey "bar" "blafoo234" -- yes, the version can be empty …- , PackageKey "xnu" ""]+ , PackageKey "xnu" ""+ -- and yes, package names can contain `@`+ , PackageKey "@types/foo@" ""+ ] parseSuccess :: Parser a -> Text -> IO a parseSuccess parser string = do
yarn-lock.cabal view
@@ -3,7 +3,7 @@ -- see: https://github.com/sol/hpack name: yarn-lock-version: 0.3.1+version: 0.3.2 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