yarn-lock (empty) → 0.1.0
raw patch · 4 files changed
+256/−0 lines, 4 filesdep +basedep +containersdep +megaparsecsetup-changed
Dependencies added: base, containers, megaparsec, protolude, text
Files
- LICENSE +20/−0
- Setup.hs +2/−0
- src/Yarn/Lock.hs +199/−0
- yarn-lock.cabal +35/−0
+ LICENSE view
@@ -0,0 +1,20 @@+Copyright (c) 2017 Profpatsch <mail@profpatsch.de>+MIT LICENSE++Permission is hereby granted, free of charge, to any person obtaining a copy+of this software and associated documentation files (the "Software"), to deal+in the Software without restriction, including without limitation the rights+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+copies of the Software, and to permit persons to whom the Software is+furnished to do so, subject to the following conditions:++The above copyright notice and this permission notice shall be included in+all copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+SOFTWARE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ src/Yarn/Lock.hs view
@@ -0,0 +1,199 @@+{-# LANGUAGE NoImplicitPrelude #-}+{-|+Module : Yarn.Lock+Description : Parser & Types for yarn.lock files+Maintainer : Profpatsch+Stability : experimental++The <https://yarnpkg.com/ Yarn package manager> improves on npm+in that it writes @yarn.lock@ files that contain a complete+version resolution of all dependencies. This way a deterministic+deployment can be guaranteed.++This module provides a parser for @yarn.lock@ files.+-}+module Yarn.Lock+( Lockfile, PackageKey(..), Package(..), RemoteFile(..)+, PackageEntry, PackageList+, Yarn.Lock.parse+-- | = Parsers+, lockfile+, packageListToLockfile, packageList+, packageEntry, packageKeys, packageKey, package+) where++import Protolude hiding (try)+import Data.String (String)+import Text.Megaparsec as MP+import Text.Megaparsec.Text+import qualified Data.Text as T++import qualified Data.Map.Strict as M++-- | Yarn lockfile.+type Lockfile = M.Map PackageKey Package++-- | Key that indexes package for a specific version.+data PackageKey = PackageKey+ { name :: Text -- ^ package name+ , npmSemver :: Text -- ^ semver string+ } deriving (Show, Eq, Ord)++-- | The actual package with dependencies and download link.+data Package = Package+ { version :: Text -- ^ resolved, specific version+ , resolved :: RemoteFile -- ^ download link w/ hash+ , dependencies :: [PackageKey] -- ^ list of dependencies+ , optionalDependencies :: [PackageKey] -- ^ list of optional dependencies+ } deriving (Eq, Show)++-- | A package download link.+data RemoteFile = RemoteFile+ { url :: Text+ , sha1sum :: Text }+ deriving (Eq, Show)++-- | A entry as it appears in the yarn.lock representation.+type PackageEntry = ([PackageKey], Package)+-- | Convenience alias.+type PackageList = [PackageEntry]++-- | Convenience function that converts errors to Text.+--+-- The actual parsers are below.+parse :: Text -- ^ name of source file+ -> Text -- ^ input for parser+ -> Either Text Lockfile+parse src inp = first (T.pack . parseErrorPretty)+ $ MP.parse lockfile (T.unpack src) inp++-- TODO: actually use somehow (apart from manual testing)+-- The yarn.lock file should resolve each packageKey exactly once.+--+-- No pkgname/semver combination should appear twice. That means+-- the lengths of the converted map and the list lists need to match.+-- prop_LockfileSameAmountOfKeys :: PackageList -> Bool+-- prop_LockfileSameAmountOfKeys pl = length (packageListToLockfile pl)+-- == length (concatMap fst pl)+++-- HALP, I don’t know how to parser.+-- It appears to be a more general format which somewhat resembles yaml.+-- The code below conflates the format & the semantics of yarn.lock files.+-- It should be separated sometime, to make parsing easier.++-- | Convenience function that applies @packageListToLockfile@.+lockfile :: Parser Lockfile+lockfile = packageListToLockfile <$> packageList++-- | The yarn.lock file is basically a hashmap with multi-keyed entries.+--+-- This should press it into our Lockfile Map.+packageListToLockfile :: PackageList -> Lockfile+packageListToLockfile = foldl' go mempty+ where go lf (keys, pkg) = foldl' (\lf' key' -> M.insert key' pkg lf') lf keys++-- | Parse a complete yarn.lock into exaclty the same representation.+--+-- You can apply @packageListToLockfile@ to make it usable.+packageList :: Parser PackageList+packageList = many $ (skipMany (comment <|> eol)) *> packageEntry+ where comment = char '#' *> manyTill anyChar eol++-- | A single PackageEntry.+--+-- @+-- handlebars@^4.0.4:+-- version "4.0.6"+-- resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.0.6.tgz#2ce4484850537f9c97a8026d5399b935c4ed4ed7"+-- dependencies:+-- async "^1.4.0"+-- optimist "^0.6.1"+-- source-map "^0.4.4"+-- optionalDependencies:+-- uglify-js "^2.6"+-- @+packageEntry :: Parser PackageEntry+packageEntry = (,) <$> packageKeys <*> package <?> "package entry"++-- | The list of PackageKeys that index the same Package+--+-- @+-- align-text@^0.1.1, align-text@^0.1.3:\\n+-- @+packageKeys :: Parser [PackageKey]+packageKeys = sepBy1 packageKey (string ", ") <* (char ':') <* eol <?> "package keys"++-- | A packageKey is @\<package-name\>\@\<semver\>@;+--+-- If the semver contains spaces, it is also quoted with @"@.+packageKey :: Parser PackageKey+packageKey = label "package key" $ inString pkgKey <|> pkgKey+ where+ pkgKey = PackageKey+ -- everything until the version, sep is @+ <$> (someTextUntilSep '@' <?> "package name part of package key")+ -- a version is anything but the , (used for seperating package keys)+ -- or : (used to close the packageKeys line)+ -- could be more specific (version is semver), but I’m lazy+ <*> (someText (noneOf "\",:") <?> "semver part of package key")++-- | Parses the content fields of a package.+package :: Parser Package+package = Package+-- TODO: order shouldn’t matter, horrible indentation scheme+ <$> (indent 2 $ key "version" stringText)+ <*> (indent 2 $ key "resolved" remoteFile)+ <*> (maybe [] identity <$> optional (indent 2 $ dependencyEntries "dependencies"))+ <*> (maybe [] identity <$> optional (indent 2 $ dependencyEntries "optionalDependencies"))+++-- internal parsers++-- | the “resolved”-field contains the link and the hash+remoteFile :: Parser RemoteFile+remoteFile = label "file link with hash" $ RemoteFile+ <$> someTextUntilSep '#'+ <*> stringText++-- | dependency field of a package+dependencyEntries :: String -> Parser [PackageKey]+dependencyEntries key' = label (key' <>" field") $ do+ _ <- string (key' <>":") <* eol+ -- TODO: cool indentation handling+ some (indent 4 dep)+ where+ -- It’s a bit like a key below, but the value of the key is not known.+ -- Here’s where the format should get its own AST, but I’m too lazy right now.+ dep = PackageKey <$> someTextUntilSep ' ' <*> inString stringText <* eol <?> "a dependency entry"++-- | A key-value pair, separated by space. The value is enclosed in "".+--+-- The given parser is used to parse the value and should not parse ".+key :: String -> Parser a -> Parser a+key name' val = label ("key " <> name') $+ string name' *> char ' ' *> inString val <* eol+++-- text versions of parsers & helpers++someText :: Parser Char -> Parser Text+someText c = T.pack <$> some c++-- | parse everything as inside a string+-- TODO: this breaks the 'between' abstraction, can it be avoided somehow?+inString :: Parser a -> Parser a+inString = between (char '"') (char '"')++-- | function to annotate text inside strings (which should never parse ")+-- symptom of the broken 'between' abstraction+stringText :: Parser Text+stringText = someText (noneOf "\"") <?> "non-empty text without \""++-- | parse some text until seperator is reached+someTextUntilSep :: Char -> Parser Text+someTextUntilSep sep = T.pack <$> someTill anyChar (char sep)++-- | intend by @i@ spaces+indent :: Int -> Parser a -> Parser a+indent i p = try $ count i (char ' ') *> p
+ yarn-lock.cabal view
@@ -0,0 +1,35 @@+-- This file has been generated from package.yaml by hpack version 0.17.0.+--+-- see: https://github.com/sol/hpack++name: yarn-lock+version: 0.1.0+synopsis: Represent and parse yarn.lock files+description: Types and parser for the lock file format of the npm successor yarn.+category: Data+homepage: https://github.com/Profpatsch/yaml-lock#readme+bug-reports: https://github.com/Profpatsch/yaml-lock/issues+author: Profpatsch+maintainer: mail@profpatsch.de+license: MIT+license-file: LICENSE+build-type: Simple+cabal-version: >= 1.10++source-repository head+ type: git+ location: https://github.com/Profpatsch/yaml-lock++library+ hs-source-dirs:+ src+ ghc-options: -Wall+ build-depends:+ base == 4.*+ , containers+ , text+ , megaparsec == 5.*+ , protolude >= 0.1+ exposed-modules:+ Yarn.Lock+ default-language: Haskell2010