yarn-lock 0.4.1 → 0.5.0
raw patch · 6 files changed
+110/−29 lines, 6 files
Files
- CHANGELOG.md +13/−1
- src/Yarn/Lock/File.hs +7/−1
- src/Yarn/Lock/Parse.hs +34/−10
- src/Yarn/Lock/Types.hs +21/−4
- tests/TestParse.hs +33/−11
- yarn-lock.cabal +2/−2
CHANGELOG.md view
@@ -5,7 +5,19 @@ 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+## [0.5.0] - 2018-06-12++### Changed++- `PackageKey`s now correctly parse scoped npm names+ - This means `Text` is now a sum of `SimplePackageKey`/`ScopedPackageKey`++### Fixed++- `PackageKey`s with versions containing `@` parse correctly+ - Like for example `git+ssh:git@github.com` links for some git packages++## [0.4.1] - 2018-05-15 ### Changed
src/Yarn/Lock/File.hs view
@@ -95,6 +95,11 @@ text = FieldParser { parseField = either Just (const Nothing) , parserName = "text" } + packageKey :: FieldParser T.PackageKeyName+ packageKey = FieldParser+ { parseField = parseField text >=> T.parsePackageKeyName+ , parserName = "package key" }+ -- | Parse a field nested one level to a list of 'PackageKey's. keylist :: FieldParser [T.PackageKey] keylist = FieldParser@@ -102,8 +107,9 @@ , parseField = either (const Nothing) (\(Parse.PackageFields inner) -> for (M.toList inner) $ \(k, v) -> do+ name <- parseField packageKey (Left k) npmVersionSpec <- parseField text v- pure $ T.PackageKey { T.name = k, ..}) }+ pure $ T.PackageKey { name, npmVersionSpec }) } -- | Appling heuristics to the field contents to find the -- correct remote type.
src/Yarn/Lock/Parse.hs view
@@ -36,8 +36,9 @@ import qualified Yarn.Lock.Types as YLT -type Parser = Parsec Void Text +-- | We use a simple (pure) @Megaparsec@ parser.+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.@@ -112,16 +113,37 @@ pkgKey valueChars = label "package key" $ do 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.- -- 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+ -- `@` is used for separation, but package names can also+ -- start with the `@` character (so-called “scoped packages”).+ -- Furthermore, versions can contain `@` as well.+ -- This file format is a pile of elephant shit.+ case breakDrop '@' key of+ ("", rest) -> case breakDrop '@' rest of+ -- scoped key with empty name+ ("", _) -> emptyKeyErr key+ -- scoped key ("@scope/package")+ (scopedName, ver) -> YLT.PackageKey+ <$> scoped (T.cons '@' scopedName) <*> pure ver+ -- just a simple key+ (name, ver) -> pure $ YLT.PackageKey (YLT.SimplePackageKey name) ver + emptyKeyErr :: Text -> Parser a+ emptyKeyErr key = fail+ ("packagekey: package name can not be empty (is: "+ <> toS key <> ")")++ -- | Like 'T.breakOn', but drops the separator char.+ breakDrop :: Char -> Text -> (Text, Text)+ breakDrop c str = case T.breakOn (T.singleton c) str of+ (s, "") -> (s, "")+ (s, s') -> (s, T.drop 1 s')++ -- | Parses a (scoped) package key and throws an error if misformatted.+ scoped n = maybe+ (fail $ "packageKey: scoped variable must be of form @scope/package"+ <> " (is: " <> toS n <> ")")+ pure $ YLT.parsePackageKeyName n+ -- | Either a simple or a nested field. field :: Parser (Text, Either Text PackageFields) field = try nested <|> simple <?> "field"@@ -175,6 +197,8 @@ -- -- Update: npm doesn’t specify the package name format, at all. -- Apart from the length.+-- Update: According to https://docs.npmjs.com/misc/scope+-- the package name format is “URL-safe characters, no leading dots or underscores” TODO symbolChars :: Parser Text symbolChars = label "key symbol" $ someTextOf $ MP.satisfy (\c -> Ch.isAscii c &&
src/Yarn/Lock/Types.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE DeriveFunctor #-}+{-# LANGUAGE DeriveFunctor, OverloadedStrings #-} {-| Module : Yarn.Lock.Types Description : Types for yarn.lock files@@ -8,6 +8,7 @@ module Yarn.Lock.Types where import Protolude hiding (try)+import qualified Data.Text as T import qualified Data.MultiKeyedMap as MKM import qualified Data.List.NonEmpty as NE @@ -18,8 +19,7 @@ -- -- Attention: Might be changed to a newtype in a future release. type Lockfile = MKM.MKMap PackageKey Package--- TODO--- newtype Lockfile = Lockfile (MKM.MKMap PackageKey Package)+-- TODO newtype Lockfile = Lockfile (MKM.MKMap PackageKey Package) -- | Proxy type for our MKMap intermediate key lockfileIkProxy :: Proxy Int@@ -27,11 +27,28 @@ -- | Key that indexes package for a specific version. data PackageKey = PackageKey- { name :: Text -- ^ package name+ { name :: PackageKeyName -- ^ package name , npmVersionSpec :: Text -- ^ tring that specifies the version of a package; -- sometimes a npm semver, sometimes an arbitrary string } deriving (Show, Eq, Ord)++-- | The name of a package. They can be scoped, see+-- | <https://docs.npmjs.com/misc/scope> for an explanation.+data PackageKeyName+ = SimplePackageKey Text+ -- ^ just a package name+ | ScopedPackageKey Text Text+ -- ^ a scope and a package name (e.g. @types/foobar)+ deriving (Show, Eq, Ord)++-- | Try to parse a string into a package key name (scoped or not).+parsePackageKeyName :: Text -> Maybe PackageKeyName+parsePackageKeyName n = case T.stripPrefix "@" n of+ Nothing -> Just $ SimplePackageKey n+ Just sc -> case T.breakOn "/" sc of+ (_, "") -> Nothing+ (scope, pkg) -> Just $ ScopedPackageKey scope (T.drop 1 pkg) -- | Something with a list of 'PackageKey's pointing to it. data Keyed a = Keyed (NE.NonEmpty PackageKey) a
tests/TestParse.hs view
@@ -27,7 +27,7 @@ parseSuccess packageList startComment >>= \((Keyed keys _) : _) -> do assertBool "only foo"- (keys == pure (PackageKey "dummy-package" "foo"))+ (keys == pure (PackageKey (SimplePackageKey "dummy-package") "foo")) -- registryPackage :: Text -- registryPackage = [text|@@ -56,8 +56,8 @@ assertBool "field2 member" (Map.member "field2" fields) Map.lookup "field1" fields @=? (Just (Left "°§ℓ»«UAIERNT")) -nestedPackage :: Text-nestedPackage = [text|+nestedPackageExample :: Text+nestedPackageExample = [text| readable-stream@1.0, "readable-stream@>=1.0.33-1 <1.1.0-0": dependencies: core-util-is "~1.0.0"@@ -81,8 +81,8 @@ case_NestedPackage :: Assertion case_NestedPackage = do- assertBool "there is unicode" (all Ch.isAscii (toS nestedPackage :: [Char]))- parseSuccess packageEntry nestedPackage+ assertBool "there is unicode" (all Ch.isAscii (toS nestedPackageExample :: [Char]))+ parseSuccess packageEntry nestedPackageExample >>= \(Keyed _ (_, PackageFields fields)) -> do case Map.lookup "dependencies" fields of (Nothing) -> assertFailure "where’s the key"@@ -92,7 +92,7 @@ assertEqual "nested keys" 4 $ length nested assertEqual "dep exists" (Just (Left "2.3.4")) $ Map.lookup "johnny-dep" nested- assertEqual "there can be @" (Just (Left "~0.10.x"))+ assertEqual "scoped packages start with @" (Just (Left "~0.10.x")) $ Map.lookup "@types/string_decoder" nested case_PackageField :: IO ()@@ -112,17 +112,39 @@ case_PackageKey :: Assertion case_PackageKey = do- let key = "foo@^1.3.4, bar@blafoo234, xnu@, @types/foo@@:\n"+ let key = "foo@^1.3.4, bar@blafoo234, xnu@, @types/foo@:\n" parseSuccess packageKeys key >>= \keys -> do keys @?= NE.fromList- [ PackageKey "foo" "^1.3.4"- , PackageKey "bar" "blafoo234"+ [ PackageKey (SimplePackageKey "foo") "^1.3.4"+ , PackageKey (SimplePackageKey "bar") "blafoo234" -- yes, the version can be empty …- , PackageKey "xnu" ""+ , PackageKey (SimplePackageKey "xnu") "" -- and yes, package names can contain `@`- , PackageKey "@types/foo@" ""+ , PackageKey (ScopedPackageKey "types" "foo") "" ]+++-- | PackageKeys can contain arbitrary stuff apparently+case_complexKey :: Assertion+case_complexKey = do+ parseSuccess packageKeys+ "\"mango-components@git+ssh://git@github.com:stuff/#fe234\":"+ >>= \((PackageKey name version) NE.:| []) -> do+ assertEqual "complexKey name"+ (SimplePackageKey "mango-components") name+ assertEqual "complexKey version"+ "git+ssh://git@github.com:stuff/#fe234"+ version+ parseSuccess packageKeys+ "\"@types/mango-components@git@github\":"+ >>= \((PackageKey name version) NE.:| []) -> do+ assertEqual "complexKeyScoped name"+ (ScopedPackageKey "types" "mango-components") name+ assertEqual "complexKeyScoped version" "git@github" version+++-- HELPERS parseSuccess :: Parser a -> Text -> IO a parseSuccess parser string = do
yarn-lock.cabal view
@@ -2,10 +2,10 @@ -- -- see: https://github.com/sol/hpack ----- hash: bfe08f55682f41944db8ec965c30c279fd7d244188df301b7d3d22a65d0c9df7+-- hash: 9195f2d20c7a3cd78e9ef32e7e18b231b383f572887111142cd2df1d76891703 name: yarn-lock-version: 0.4.1+version: 0.5.0 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