yarn2nix 0.5.0 → 0.7.0
raw patch · 19 files changed
+706/−411 lines, 19 filesdep +transformersdep −eitherdep ~data-fixdep ~hnixdep ~optparse-applicativenew-component:exe:node-package-toolPVP ok
version bump matches the API change (PVP)
Dependencies added: transformers
Dependencies removed: either
Dependency ranges changed: data-fix, hnix, optparse-applicative, process, protolude, tasty, tasty-hunit, tasty-quickcheck, yarn-lock
API changes (from Hackage documentation)
+ Distribution.Nixpkgs.Nodejs.Utils: packageKeyNameToSymbol :: PackageKeyName -> Text
+ Nix.Expr.Additions: inheritStatic :: [Text] -> Binding e
- Distribution.Nodejs.Package: WrongType :: Text -> Text -> Warning
+ Distribution.Nodejs.Package: WrongType :: Text -> Maybe Text -> Warning
- Distribution.Nodejs.Package: [wrongTypeDefault] :: Warning -> Text
+ Distribution.Nodejs.Package: [wrongTypeDefault] :: Warning -> Maybe Text
Files
- NodePackageTool.hs +170/−0
- README.md +40/−1
- SetupNodePackagePaths.hs +0/−116
- default.nix +24/−9
- nix-lib/buildNodePackage.nix +21/−6
- nix-lib/default.nix +81/−30
- nix-lib/old-version-dependencies.nix +20/−0
- nix-lib/yarn-lock.nix +0/−19
- shell.nix +35/−36
- src/Distribution/Nixpkgs/Nodejs/Cli.hs +1/−1
- src/Distribution/Nixpkgs/Nodejs/FromPackage.hs +24/−8
- src/Distribution/Nixpkgs/Nodejs/OptimizedNixOutput.hs +48/−32
- src/Distribution/Nixpkgs/Nodejs/ResolveLockfile.hs +10/−7
- src/Distribution/Nixpkgs/Nodejs/Utils.hs +12/−2
- src/Distribution/Nodejs/Package.hs +37/−14
- src/Nix/Expr/Additions.hs +9/−5
- tests/TestNpmjsPackage.hs +68/−25
- yarn2nix.cabal +105/−99
- yarn2nix.nix +1/−1
+ NodePackageTool.hs view
@@ -0,0 +1,170 @@+{-# LANGUAGE RecordWildCards, NoImplicitPrelude, LambdaCase, FlexibleContexts, NamedFieldPuns, OverloadedStrings #-}+import Protolude+import qualified Data.Text.IO as TIO+import qualified Control.Monad.Except as ExcT+import qualified Control.Exception as Exc+import qualified System.IO.Error as IOErr+import qualified Data.ByteString.Lazy as BL+import qualified Data.HashMap.Lazy as HML+import qualified System.FilePath as FP+import qualified System.Directory as Dir+import qualified System.Posix.Files as PosixFiles+import Options.Applicative++import qualified Distribution.Nodejs.Package as NP++data Args+ = Args+ { argsPackageDir :: FilePath+ , argsMode :: Mode }++data Mode+ = LinkBin+ { linkBinTargetDir :: FilePath }+ | SetBinExecFlag++args :: Parser Args+args = subparser+ ( command "link-bin"+ (info (modeCommands linkBinSubcommands)+ (progDesc "link package dependecies’ bin files"))+ <> command "set-bin-exec-flag"+ (info (modeCommands setBinExecFlagSubcommands)+ (progDesc "make all bin scripts executable")) )+ where+ modeCommands modeSubcommands = Args+ <$> strOption+ ( long "package"+ <> metavar "FOLDER"+ <> help "folder with the node package" )+ <*> modeSubcommands+ linkBinSubcommands = LinkBin+ <$> strOption+ ( long "to"+ <> metavar "LINK_TARGET"+ <> help "folder to link to (absolute or relative from package folder)" )+ setBinExecFlagSubcommands = pure SetBinExecFlag+ ++type ErrorLogger = ExceptT [Char] IO++-- | Print a warning to stdout.+warn :: [Text] -> ErrorLogger ()+warn ws = for_ ws $ \w -> liftIO $ TIO.hPutStrLn stderr ("Warning: " <> w)++-- | On Exception rethrow with annotation.+tryIOMsg :: ([Char] -> [Char]) -> IO a -> ErrorLogger a+tryIOMsg errAnn = ExcT.withExceptT (errAnn . Exc.displayException) . tryIO+++main :: IO ()+main = execParser (info (args <**> helper)+ (progDesc "Tool for various node package maintenance tasks"))+ >>= realMain++realMain :: Args -> IO ()+realMain Args{..} = do+ -- basic sanity checks+ let packageJsonPath = argsPackageDir FP.</> "package.json"+ unlessM (Dir.doesFileExist packageJsonPath)+ $ die $ toS $ packageJsonPath <> " does not exist."+ case argsMode of+ LinkBin{..} -> do+ unlessM (Dir.doesDirectoryExist linkBinTargetDir)+ $ die $ toS $ linkBinTargetDir <> " is not a directory."+ _ -> pass++ -- parse & decode & run logic+ runExceptT+ (tryRead packageJsonPath >>= tryDecode packageJsonPath >>= go)+ >>= \case+ (Left err) -> die $ toS $ "ERROR: " <> err+ (Right _) -> pass++ where+ tryRead :: FilePath -> ErrorLogger BL.ByteString+ tryRead fp = tryIOMsg exc $ BL.readFile fp+ where exc e = fp <> " cannot be read:\n" <> e+ tryDecode :: FilePath -> BL.ByteString -> ExceptT [Char] IO NP.Package+ tryDecode fp fileBs = do+ (pkg, warnings) <- NP.unLoggingPackage+ <$> ExcT.ExceptT (pure $ first exc $ NP.decode fileBs)+ warn $ fmap ((\w -> toS fp <> ": " <> w) . NP.formatWarning) warnings+ pure pkg+ where exc e = fp <> " cannot be decoded\n" <> toS e++ tryAccess :: IO a -> IO (Maybe a)+ tryAccess io =+ hush <$> tryJust+ (\e -> guard (IOErr.isDoesNotExistError e ||+ IOErr.isPermissionError e))+ io++ qte s = "\"" <> s <> "\""++ go :: NP.Package -> ErrorLogger ()+ go NP.Package{bin} = do+ binFiles <- readBinFiles bin+ for_ binFiles $ case argsMode of+ -- Link all dependency binaries to their target folder+ LinkBin{..} -> linkBin linkBinTargetDir+ -- Set the executable flag of all package binaries+ SetBinExecFlag -> setBinExecFlag . snd++ -- | Read the binary files and return their names & paths.+ readBinFiles :: NP.Bin -> ErrorLogger [(Text, FilePath)]+ readBinFiles bin = case bin of+ -- files with names how they should be linked+ (NP.BinFiles bs) -> pure $ HML.toList bs+ -- a whole folder where everything should be linked+ (NP.BinFolder bf) -> do+ dirM <- liftIO $ tryAccess (Dir.listDirectory bf)+ case dirM of+ Nothing -> do+ warn ["Binary folder " <> toS (qte bf) <> " could not be accessed."]+ pure []+ (Just dir) ->+ pure $ fmap (\f -> (toS f, bf FP.</> f)) dir++ -- | Canonicalize the path.+ canon :: FilePath -> ErrorLogger FilePath+ canon fp = tryIOMsg+ (\e -> "Couldn’t canonicalize path " <> qte fp <> ": " <> e)+ (Dir.canonicalizePath fp)++ -- | Canonicalize relative to our package+ -- and ensure that the relative path is not outside.+ canonPkg :: FilePath -> ErrorLogger FilePath+ canonPkg relPath = do+ pkgDir <- canon argsPackageDir+ resPath <- canon $ argsPackageDir FP.</> relPath+ when (not $ pkgDir `isPrefixOf` resPath)+ $ throwError $ mconcat+ [ "The link to executable file "+ , qte relPath+ , " lies outside of the package folder!\n"+ , "That’s a security risk, aborting." ]+ pure resPath++ -- | Link a binary file to @targetDir/name@.+ -- @relBinPath@ is relative from the package dir.+ linkBin :: FilePath -> (Text, FilePath) -> ErrorLogger ()+ linkBin targetDir_ (name, relBinPath) = do+ binPath <- canonPkg relBinPath+ targetDir <- canon targetDir_+ tryIOMsg+ (\e -> "Symlink could not be created: " <> e)+ (PosixFiles.createSymbolicLink binPath $ targetDir FP.</> toS name)++ -- | Set executable flag of the file.+ setBinExecFlag :: FilePath -> ErrorLogger ()+ setBinExecFlag file_ = do+ file <- canonPkg file_+ res <- liftIO $ tryAccess $ do+ perm <- Dir.getPermissions file+ Dir.setPermissions file+ $ Dir.setOwnerExecutable True perm+ case res of+ Nothing ->+ warn ["Cannot set executable bit on " <> toS file]+ Just () -> pass
README.md view
@@ -19,6 +19,7 @@ - Extremely fast. - Nice code that can be easily extended, new repositories introduced, adapt to new versions of the `yarn.lock` format. - Comes with a [nix library][nix-lib] that uses the power of overlays to make overriding dependencies possible.+- POWERED BY [HNIX](https://github.com/haskell-nix/hnix)™ since before it was cool. Probably a few more. @@ -55,12 +56,50 @@ [nix-lib]: ./nix-lib/default.nix -## Building+## Building `yarn2nix` ``` $ nix-build $ result/bin/yarn2nix ```++## Using the generated nix files to build a project++**Note:** This is a temporary interface. Ideally, the library will be in nixpkgs+and yarn2nix will be callable from inside the build (so the resulting nix files+don’t have to be checked in).++Once you have the `yarn2nix` binary, use it to generate nix files for the+`yarn.lock` file and the `package.json`:++```shell+$ yarn2nix ./jsprotect/yarn.lock > npm-deps.nix+$ yarn2nix --template ./jsproject/package.json > npm-package.nix+```++Then use the library to assemble the generated files in a `default.nix`:++```nix+let+ nixpkgsPath = <nixpkgs>;+ pkgs = import nixpkgsPath {};+ nixLib = pkgs.callPackage /path/to/yarn2nix/nix-lib {+ # WARNING (TODO): for now you need to use this checked out yarn2nix+ # because the upstream package (in haskellPackages) might have+ # broken dependencies (yarn-lock and yarn2nix are not in stackage)+ yarn2nix = import /path/to/yarn2nix { inherit nixpkgsPath; };+ };++in+ nixLib.buildNodePackage+ ( { src = nixLib.removePrefixes [ "node_modules" ] ./.; } //+ nixLib.callTemplate ./npm-package.nix+ (nixLib.buildNodeDeps (pkgs.callPackage ./npm-deps.nix {}))+```++Finally, run `nix-build`, and voilà, in `./result/` you find the project with+all its dependencies correctly linked to their corresponding `node_modules`+folder, recursively. ## Development
− SetupNodePackagePaths.hs
@@ -1,116 +0,0 @@-{-# LANGUAGE RecordWildCards, NoImplicitPrelude, LambdaCase, FlexibleContexts, NamedFieldPuns, OverloadedStrings #-}-import Protolude-import qualified Data.Text.IO as TIO-import qualified Control.Monad.Except as ExcT-import qualified Control.Exception as Exc-import qualified System.IO.Error as IOErr-import qualified Data.ByteString.Lazy as BL-import qualified Data.HashMap.Lazy as HML-import qualified System.FilePath as FP-import qualified System.Directory as Dir-import qualified System.Posix.Files as PosixFiles-import Options.Applicative--import qualified Distribution.Nodejs.Package as NP--data Args- = Args { argMode :: Mode- , argTargetDir :: FilePath- , argPackageDir :: FilePath }--data Mode = BinMode--args :: Parser Args-args = subparser- ( command "bin" (info (modeCommands BinMode)- (progDesc "link package bin files")) )- where- modeCommands mode = Args- <$> pure mode- <*> strOption- ( long "to"- <> metavar "LINK_TARGET"- <> help "folder to link to (absolute or relative from package folder)" )- <*> strOption- ( long "package"- <> metavar "FOLDER"- <> help "folder with the node package" )- --main :: IO ()-main = execParser (info (args <**> helper)- (progDesc "Link various files from npm packages to folders"))- >>= realMain--type ErrorLogger = ExceptT [Char] IO--realMain :: Args -> IO ()-realMain Args{..} = do- let packageJsonPath = argPackageDir FP.</> "package.json"- unlessM (Dir.doesDirectoryExist argTargetDir)- $ die $ argTargetDir <> " is not a directory."- unlessM (Dir.doesFileExist packageJsonPath)- $ die $ packageJsonPath <> " does not exist."-- runExceptT- (tryRead packageJsonPath >>= tryDecode packageJsonPath >>= go)- >>= \case- (Left err) -> die $ "ERROR: " <> err- (Right _) -> pass-- where- tryIOMsg :: ([Char] -> [Char]) -> IO a -> ErrorLogger a- tryIOMsg errAnn = ExcT.withExceptT (errAnn . Exc.displayException) . tryIO-- tryRead :: FilePath -> ErrorLogger BL.ByteString- tryRead fp = tryIOMsg exc $ BL.readFile fp- where exc e = fp <> " cannot be read:\n" <> e- tryDecode :: FilePath -> BL.ByteString -> ExceptT [Char] IO NP.Package- tryDecode fp fileBs = do- (pkg, warnings) <- NP.unLoggingPackage- <$> ExcT.ExceptT (pure $ first exc $ NP.decode fileBs)- warn $ fmap NP.formatWarning warnings- pure pkg- where exc e = fp <> " cannot be decoded\n" <> toS e-- qte s = "\"" <> s <> "\""- warn :: [Text] -> ErrorLogger ()- warn ws = for_ ws $ liftIO . TIO.hPutStrLn stderr-- go :: NP.Package -> ErrorLogger ()- go NP.Package{bin} = case argMode of- BinMode -> traverse_ linkBin =<< case bin of- -- files with names how they should be linked- (NP.BinFiles bs) -> pure $ HML.toList bs- -- a whole folder where everything should be linked- (NP.BinFolder bf) -> do- dirE <- liftIO $ tryJust- (\e -> guard (IOErr.isDoesNotExistError e ||- IOErr.isPermissionError e))- (Dir.listDirectory bf)- case dirE of- (Left _) -> do- warn ["Binary folder " <> toS (qte bf) <> " could not be accessed."]- pure []- (Right dir) ->- pure $ fmap (\f -> (toS f, bf FP.</> f)) dir-- -- | Link a binary file to @targetDir/name@.- -- @relBinPath@ is relative from the package dir.- linkBin :: (Text, FilePath) -> ErrorLogger ()- linkBin (name, relBinPath) = do- let canon fp = tryIOMsg- (\e -> "Couldn’t canonicalize path " <> qte fp <> ": " <> e)- (Dir.canonicalizePath fp)- pkgDir <- canon argPackageDir- binPath <- canon $ argPackageDir FP.</> relBinPath- targetDir <- canon argTargetDir- when (not $ pkgDir `isPrefixOf` binPath)- $ throwError $ mconcat- [ "The link to executable file "- , qte relBinPath- , " lies outside of the package folder!\n"- , "That’s a security risk, aborting." ]- tryIOMsg- (\e -> "Symlink could not be created: " <> e)- (PosixFiles.createSymbolicLink binPath $ targetDir FP.</> toS name)
default.nix view
@@ -1,16 +1,31 @@-(import <nixpkgs> {+{ nixpkgsPath ? ./nixpkgs-pinned.nix }:+(import nixpkgsPath { overlays = [(pkgs: oldpkgs: { haskellPackages = let nix-lib = pkgs.callPackage ./nix-lib {}; in oldpkgs.haskellPackages.override {- overrides = self: super: {- inherit (pkgs.callPackage ./nix-lib/yarn-lock.nix {} self super) yarn-lock;- yarn2nix =- let pkg = self.callPackage ./yarn2nix.nix {};- in oldpkgs.haskell.lib.overrideCabal pkg (old: {- src = nix-lib.removePrefixes [ "nix-lib" "dist" "result" ] ./.;- });- };+ overrides = oldpkgs.lib.composeExtensions+ (pkgs.callPackage ./nix-lib/old-version-dependencies.nix {})+ (self: super: {+ yarn2nix =+ let+ pkg = oldpkgs.haskell.lib.overrideCabal+ (self.callPackage ./yarn2nix.nix {})+ (old: {+ prePatch = old.prePatch or "" + ''+ ${oldpkgs.lib.getBin self.hpack}/bin/hpack+ # we depend on the git prefetcher+ substituteInPlace \+ src/Distribution/Nixpkgs/Nodejs/ResolveLockfile.hs \+ --replace '"nix-prefetch-git"' \+ '"${pkgs.nix-prefetch-scripts}/bin/nix-prefetch-git"'+ '';+ });+ in oldpkgs.haskell.lib.overrideCabal pkg (old: {+ src = nix-lib.removePrefixes+ [ "nix-lib" "dist" "result" "tests/nix-tests" ] ./.;+ });+ }); }; })];
nix-lib/buildNodePackage.nix view
@@ -1,8 +1,8 @@-{ stdenv, linkNodeDeps, nodejs }:-{ name # String+{ stdenv, linkNodeDeps, nodejs, yarn2nix }:+{ key # { scope: String, name: String } , version # String , src # Drv-, nodeBuildInputs # Listof { name : String, drv : Drv }+, nodeBuildInputs # Listof { key: { scope: String, name: String }, drv : Drv } , ... }@args: # since we skip the build phase, pre and post will not work@@ -13,8 +13,15 @@ with stdenv.lib; -stdenv.mkDerivation ((removeAttrs args [ "nodeBuildInputs" ]) // {- name = "${name}-${version}";+let+ # TODO: scope should be more structured somehow. :(+ packageName =+ if key.scope == ""+ then "${key.name}-${version}"+ else "${key.scope}-${key.name}-${version}";++in stdenv.mkDerivation ((removeAttrs args [ "key" "nodeBuildInputs" ]) // {+ name = packageName; inherit version src; buildInputs = [ nodejs ];@@ -33,11 +40,19 @@ # a npm package is just the tarball extracted to $out cp -r . $out + # the binaries should be executable (TODO: always on?)+ ${yarn2nix}/bin/node-package-tool \+ set-bin-exec-flag \+ --package $out+ # then a node_modules folder is created for all its dependencies ${if nodeBuildInputs != [] then '' rm -rf $out/node_modules- ln -sT "${linkNodeDeps name nodeBuildInputs}" $out/node_modules+ ln -sT "${linkNodeDeps {+ name = packageName;+ dependencies = nodeBuildInputs;+ }}" $out/node_modules '' else ""} runHook postInstall
nix-lib/default.nix view
@@ -1,21 +1,49 @@-{ lib, pkgs }:+{ lib, pkgs+# TODO: temporary, to make overwriting yarn2nix easy+# TODO: remove static building once RPATHs are fixed+, yarn2nix ? pkgs.haskell.lib.justStaticExecutables+ pkgs.haskellPackages.yarn2nix+}: let # Build an attrset of node dependencies suitable for the `nodeBuildInputs`- # argument of `buildNodePackage`. The input is the path of a dependency- # file generated by the `yarn2nix` utility from a `yarn.lock` file,- # in turn the output of the `yarn` tool for an npm package.- buildNodeDeps = lockDotNix: lib.fix+ # argument of `buildNodePackage`. The input is an overlay+ # of node packages that call `_buildNodePackage`, like in the+ # files generated by `yarn2nix`.+ # It is possible to just call it with a generated file, like so:+ # `buildNodeDeps (pkgs.callPackage ./npm-deps.nix {})`+ # You can also use `lib.composeExtensions` to override packages+ # in the set:+ # ```+ # buildNodeDeps (lib.composeExtensions+ # (pkgs.callPackage ./npm-deps.nix {})+ # (self: super: { pkg = super.pkg.override {…}; }))+ # ```+ # TODO: should _buildNodePackage be fixed in here?+ buildNodeDeps = nodeDeps: lib.fix (lib.extends- (import lockDotNix { inherit (pkgs) fetchurl fetchgit; })+ nodeDeps (self: {- # wrap the invocation in the fix point, to construct the- # list of { name, drv } needed by buildNodePackage+ # The actual function building our packages.+ # type: { key: String | { scope: String, name: String }+ # , <other arguments of ./buildNodePackage.nix> }+ # Wraps the invocation in the fix point, to construct the+ # list of { key, drv } needed by buildNodePackage # from the templates. # It is basically a manual paramorphism, carrying parts of the- # information of the previous layer (the original package name).- _buildNodePackage = { name, ... }@args:- { inherit name; drv = buildNodePackage args; };+ # information of the previous layer (the original package key).+ # TODO: move that function out of the package set+ # and get nice self/super scoping right+ _buildNodePackage = { key, ... }@args:+ # To keep the generated files shorter, we allow keys to+ # be represented as strings if they have no scopes.+ # This is the only place where this is accepted,+ # but hacky nonetheless. Probably fix with above TODO.+ let key' = if builtins.isString key+ then { scope = ""; name = key; }+ else key;+ in { key = key';+ drv = buildNodePackage (args // { key = key'; }); }; })); # Build a package template generated by the `yarn2nix --template`@@ -23,13 +51,11 @@ # template nix file, the second input is all node dependencies # needed by the template, in the form generated by `buildNodeDeps`. callTemplate = yarn2nixTemplate: allDeps:- pkgs.callPackage yarn2nixTemplate {- inherit buildNodePackage removePrefixes;- } allDeps;+ pkgs.callPackage yarn2nixTemplate {} allDeps; buildNodePackage = import ./buildNodePackage.nix {- inherit linkNodeDeps;+ inherit linkNodeDeps yarn2nix; inherit (pkgs) stdenv nodejs; }; @@ -37,22 +63,40 @@ # by npm’s module system to call dependencies. # Also link executables of all dependencies into `.bin`. # TODO: copy manpages & docs as well- # type: String -> ListOf { name: String, drv : Drv } -> Drv- linkNodeDeps = name: packageDeps:- pkgs.runCommand (name + "-node_modules") {} ''+ # type: { name: String+ # , dependencies: ListOf { key: { scope: String, name: String }+ # , drv : Drv } }+ # -> Drv+ linkNodeDeps = {name, dependencies}:+ pkgs.runCommand ("${name}-node_modules") {+ # This just creates a simple link farm, which should be pretty fast,+ # saving us from additional hydra requests for potentially hundreds+ # of packages.+ allowSubstitutes = false;+ # Also tell Hydra it’s not worth copying to a builder.+ preferLocalBuild = true;+ } '' mkdir -p $out/.bin ${lib.concatMapStringsSep "\n"- (dep: ''- echo "linking node dependency ${dep.name}"- ln -sT ${dep.drv} "$out/${dep.name}"- ${ # TODO: remove static building once RPATHs are fixed- pkgs.haskell.lib.justStaticExecutables- pkgs.haskellPackages.yarn2nix}/bin/setup-node-package-paths \- bin \- --to=$out/.bin \- --package=$out/${dep.name}- '')- packageDeps}+ (dep:+ let+ hasScope = dep.key.scope != "";+ # scoped packages get another subdirectory for their scope (`@scope/`)+ parentfolder = if hasScope+ then "$out/@${dep.key.scope}"+ else "$out";+ subfolder = "${parentfolder}/${dep.key.name}";+ in ''+ echo "linking node dependency ${formatKey dep.key}"+ ${ # we need to create the scope folder, otherwise ln fails+ lib.optionalString hasScope ''mkdir "${parentfolder}"'' }+ ln -sT ${dep.drv} "${subfolder}"+ ${yarn2nix}/bin/node-package-tool \+ link-bin \+ --to=$out/.bin \+ --package=${subfolder}+ '')+ dependencies} ''; # Filter out files/directories with one of the given prefix names@@ -65,6 +109,13 @@ in builtins.filterSource (file: _: ! (hasAnyPrefix file)) path; + # format a package key of { scope: String, name: String }+ formatKey = { scope, name }:+ if scope == ""+ then name+ else "@${scope}/${name}";+ in {- inherit buildNodeDeps callTemplate removePrefixes;+ inherit buildNodeDeps linkNodeDeps buildNodePackage+ callTemplate removePrefixes; }
+ nix-lib/old-version-dependencies.nix view
@@ -0,0 +1,20 @@+{ lib, pkgs }:+self: super: {+ yarn-lock =+ super.mkDerivation rec {+ pname = "yarn-lock";+ version = "0.5.0";+ # src = ../../../haskell/yarn-lock;+ src = pkgs.fetchFromGitHub {+ owner = "Profpatsch";+ repo = "yarn-lock";+ rev = version;+ # rev = "61cc65a858db92e7c0bea861bf279f286c34bb81";+ sha256 = "007a7n1qqa7cp85bbw00yvkg2ikg1yzj1k8i9bav1bnbk6n7xp7s";+ };+ license = lib.licenses.mit;+ buildDepends = with self; [ megaparsec protolude tasty-hunit tasty-th either neat-interpolation tasty-quickcheck quickcheck-instances ];+ buildTools = [ self.hpack ];+ prePatch = ''hpack'';+ };+}
− nix-lib/yarn-lock.nix
@@ -1,19 +0,0 @@-{ lib, pkgs }:-self: super: {- yarn-lock =- super.mkDerivation rec {- pname = "yarn-lock";- version = "0.4.0";- # src = ../../haskell/yarn-lock;- src = pkgs.fetchFromGitHub {- owner = "Profpatsch";- repo = "yarn-lock";- rev = version;- sha256 = "0r5ipyfpdmg9n9zv7q1knrlypv9q2l1x24j1sgibxy411b7r62wv";- };- license = lib.licenses.mit;- buildDepends = with self; [ megaparsec protolude tasty-hunit tasty-th either neat-interpolation tasty-quickcheck ];- buildTools = [ self.hpack ];- preConfigure = ''hpack'';- };-}
shell.nix view
@@ -1,40 +1,39 @@ with import <nixpkgs> {}; (haskellPackages.override {- overrides = (self: super: {- inherit (pkgs.callPackage ./nix-lib/yarn-lock.nix {} self super)- yarn-lock;-- my-pkg = let- buildDepends = with self; [- protolude- yarn-lock- hnix- hpack- aeson- async-pool- ansi-wl-pprint- regex-tdfa- regex-tdfa-text- neat-interpolation- tasty-th- tasty-quickcheck- tasty-hunit- http-client-tls- monad-par- ];- in super.mkDerivation {- pname = "pkg-env";- src = "/dev/null";- version = "none";- license = "none";- inherit buildDepends;- buildTools = with self; [- ghcid- cabal-install- (hoogleLocal {- packages = buildDepends;- })+ overrides = lib.composeExtensions+ (pkgs.callPackage ./nix-lib/old-version-dependencies.nix {})+ (self: super: {+ my-pkg = let+ buildDepends = with self; [+ protolude+ yarn-lock+ hnix+ hpack+ aeson+ async-pool+ ansi-wl-pprint+ regex-tdfa+ regex-tdfa-text+ neat-interpolation+ tasty-th+ tasty-quickcheck+ tasty-hunit+ http-client-tls+ monad-par ];- };- });+ in super.mkDerivation {+ pname = "pkg-env";+ src = "/dev/null";+ version = "none";+ license = "none";+ inherit buildDepends;+ buildTools = with self; [+ ghcid+ cabal-install+ (hoogleLocal {+ packages = buildDepends;+ })+ ];+ };+ }); }).my-pkg.env
src/Distribution/Nixpkgs/Nodejs/Cli.hs view
@@ -77,7 +77,7 @@ Left err -> die' ("Could not parse " <> toS path <> ":\n" <> show err) die' :: Text -> IO a-die' err = putText err *> exitFailure+die' err = putErrText err *> exitFailure dieWithUsage :: Text -> IO () dieWithUsage err = die' (err <> "\n" <> usage)
src/Distribution/Nixpkgs/Nodejs/FromPackage.hs view
@@ -18,21 +18,29 @@ depsToPkgKeys :: NP.Dependencies -> [YLT.PackageKey]-depsToPkgKeys = map (\(k, v) -> YLT.PackageKey k v) . HML.toList+depsToPkgKeys = map toPkgKey . HML.toList+ where+ toPkgKey (k, v) =+ YLT.PackageKey (parsePackageKeyName k) v +parsePackageKeyName :: Text -> YLT.PackageKeyName+parsePackageKeyName k =+ -- we don’t crash on a “wrong” package key to keep this+ -- code pure, but assume it’s a simple key instead.+ maybe (YLT.SimplePackageKey k) identity+ $ YLT.parsePackageKeyName k+ -- | generate a nix expression that translates your package.nix -- -- and can serve as template for manual adjustments genTemplate :: NP.Package -> NExpr genTemplate NP.Package{..} =- simpleParamSet ["stdenv", "buildNodePackage", "filterSourcePrefixes"]+ -- reserved for possible future arguments (to prevent breakage)+ simpleParamSet [] ==> Param nodeDepsSym- -- TODO: devDeps- ==> ("buildNodePackage" @@ mkRecSet+ ==> (mkNonRecSet [ "name" $= nameStr , "version" $= mkStr version- , "src" $= ("filterSourcePrefixes"- @@ mkList [ mkStr "node_modules" ] @@ "./.") , "nodeBuildInputs" $= (letE "a" (mkSym nodeDepsSym) $ mkList (map (pkgDep "a") depPkgKeys)) , "meta" $= (mkNonRecSet@@ -41,8 +49,16 @@ <> may "homepage" homepage) ]) where- depPkgKeys = depsToPkgKeys dependencies+ -- TODO: The devDependencies are only needed for the build+ -- and probably also only from packages not stemming from+ -- a npm registry (e.g. a git package). It would be cool+ -- if these dependencies were gone in the final output.+ -- See https://github.com/Profpatsch/yarn2nix/issues/5+ depPkgKeys = depsToPkgKeys (dependencies <> devDependencies) pkgDep depsSym pk = mkSym depsSym !!. packageKeyToSymbol pk nodeDepsSym = "allDeps"- nameStr = mkStrQ [StrQ name, "-", AntiQ "version"]+ nameStr = mkStrQ [StrQ+ $ pkgKeyToName $ parsePackageKeyName name]+ pkgKeyToName (YLT.SimplePackageKey n) = n+ pkgKeyToName (YLT.ScopedPackageKey s n) = s <> "-" <> n may k v = [k $= mkStr (fromMaybe mempty v)]
src/Distribution/Nixpkgs/Nodejs/OptimizedNixOutput.hs view
@@ -31,8 +31,8 @@ import qualified Data.List as List import qualified Data.List.NonEmpty as NE -import Nix.Expr (NExpr, ($=), (==>), (!.), (@@))-import Nix.Expr.Additions (($$=), (!!.))+import Nix.Expr (NExpr, ($=), (==>), (@@))+import Nix.Expr.Additions (($$=), (!!.), inheritStatic) import qualified Nix.Expr as N import qualified Nix.Expr.Additions as NA @@ -87,38 +87,53 @@ -- | Package definition needed for calling the build function. data PkgData a = PkgData- { pkgDataName :: Text -- ^ package name- , pkgDataVersion :: Text -- ^ package version- , pkgDataUpstream :: a -- ^ points to upstream- , pkgDataHashSum :: Text -- ^ the hash sum of the package- , pkgDataDependencies :: [Text] -- ^ list of dependencies (as resolved nix symbols)+ { pkgDataName :: YLT.PackageKeyName -- ^ package name+ , pkgDataVersion :: Text -- ^ package version+ , pkgDataUpstream :: a -- ^ points to upstream+ , pkgDataHashSum :: Text -- ^ the hash sum of the package+ , pkgDataDependencies :: [Text] -- ^ list of dependencies (as resolved nix symbols) } -+-- | Tuples of prefix string to registry registries :: [(Text, Registry)] registries = [ ( yarnP , Registry "yarn" $ \n v -> [T yarnP, V n, T "/-/", V n, T "-", V v, T ".tgz"] )+ , ( npmjsP+ , Registry "npm"+ $ \n v -> [T npmjsP, V n, T "/-/", V n, T "-", V v, T ".tgz"] )+ ] where yarnP = "https://registry.yarnpkg.com/"+ npmjsP = "https://registry.npmjs.org/" shortcuts :: M.Map [NSym] NSym shortcuts = M.fromList [ (["self"], "s") , (["registries", "yarn"], "y")+ , (["registries", "npm"], "n") , (["nodeFilePackage"], "f") , (["nodeGitPackage"], "g") , (["identityRegistry"], "ir")+ , (["scopedName"], "sc") ] -- | Find out which registry the given 'YLT.Remote' shortens to.-recognizeRegistry :: Text -> Maybe Registry-recognizeRegistry fileUrl = snd <$> filterRegistry fileUrl+recognizeRegistry :: YLT.PackageKeyName -- ^ package name+ -> Text -- ^ url to file+ -> Maybe Registry+-- We don’t shorten scoped key names, because+-- they are handled specially by npm registries and+-- the URLs differ from other packages+recognizeRegistry (YLT.ScopedPackageKey{}) _ = Nothing+recognizeRegistry _ fileUrl = snd <$> foundRegistry where -- | Get registry by the prefix of the registry’s URL.- filterRegistry url = find (\reg -> fst reg `T.isPrefixOf` url) registries+ foundRegistry = find predicate registries+ predicate :: (Text, Registry) -> Bool+ predicate reg = fst reg `T.isPrefixOf` fileUrl -- | Convert a 'Res.ResolvedLockfile' to its final, nix-ready form.@@ -147,7 +162,8 @@ } def = case YLT.remote pkg of YLT.FileRemote{fileUrl} ->- PkgDefFile $ pkgDataGeneric $ note fileUrl $ recognizeRegistry fileUrl+ PkgDefFile $ pkgDataGeneric $ note fileUrl+ $ recognizeRegistry defName fileUrl YLT.GitRemote{gitRepoUrl, gitRev} -> PkgDefGit $ pkgDataGeneric $ Git gitRepoUrl gitRev -- we don’t need another ref indirection@@ -169,8 +185,6 @@ yarn = n: v: "https://registry.yarnpkg.com/${n}/-/${n}-${v}.tgz"; }; - sanitizePackageName = builtins.replaceStrings ["@" "/"] ["-" "-"];- # We want each package definition to be one line, by putting # the boilerplate into these functions for different remotes. nodeFilePackage = …@@ -179,18 +193,23 @@ # an identity function for e.g. git repos or unknown registries identityRegistry = url: _: _: url; + # a way to pass through scoped package names+ scopedName = scope: name: { inherit scope name; }+ # shortcut section s = self; ir = identityRegistry; f = nodeFilePackage; g = nodeGitPackage; y = registries.yarnpkg;+ sc = scopedName; … # the actual package definitions in { "accepts@~1.3.3" = s."accepts@1.3.3"; "accepts@1.3.3" = f "accepts" "1.3.3" y "sha" [];+ "@types/accepts@1.3.3" = f (sc "types" "accepts") "1.3.3" y "sha" []; "babel-core@^6.14.0" = s."babel-core@6.24.1"; "babel-core@6.24.1" = f "babel-core" "6.24.1" y "a0e457c58ebdbae575c9f8cd75127e93756435d8" [ s."accepts@~1.3.3"@@ -210,10 +229,13 @@ ==> N.Param "self" ==> N.Param "super" ==> N.mkLets ( [ "registries" $= N.mkNonRecSet (fmap (mkRegistry . snd) registries)- , "sanitizePackageName" $= sanitizePackageName , "nodeFilePackage" $= buildPkgFn , "nodeGitPackage" $= buildPkgGitFn- , "identityRegistry" $= NA.multiParam ["url", "_", "_"] "url" ]+ , "identityRegistry" $= NA.multiParam ["url", "_", "_"] "url"+ , "scopedName" $=+ (NA.multiParam ["scope", "name"]+ $ N.mkNonRecSet [ inheritStatic ["scope", "name"] ])+ ] <> fmap mkShortcut (M.toList shortcuts) ) (N.mkNonRecSet (map mkPkg $ M.toAscList packages)) where@@ -222,26 +244,18 @@ concatNSyms :: [NSym] -> NExpr concatNSyms [] = panic "non-empty shortcut syms!"- concatNSyms (l:ls) = foldl (!.) (N.mkSym $ unNSym l) (fmap unNSym ls)+ concatNSyms (l:ls) = foldl (!!.) (N.mkSym $ unNSym l) (fmap unNSym ls) mkShortcut :: ([NSym], NSym) -> N.Binding NExpr mkShortcut (nSyms, short) = unNSym short $= concatNSyms nSyms -- | Try to shorten sym, otherwise use input. shorten :: [NSym] -> NExpr shorten s = maybe (concatNSyms s) (N.mkSym . unNSym) $ M.lookup s shortcuts-- -- | remove symbols not allowed in nix derivation names- sanitizePackageName :: NExpr- sanitizePackageName = "builtins" !. "replaceStrings"- @@ N.mkList [N.mkStr "@", N.mkStr "/"]- @@ N.mkList [N.mkStr "-", N.mkStr "-"]- -- | Build function boilerplate the build functions share in common. buildPkgFnGeneric :: [Text] -> NExpr -> NExpr buildPkgFnGeneric additionalArguments srcNExpr =- NA.multiParam (["name", "version"] <> additionalArguments <> ["deps"])+ NA.multiParam (["key", "version"] <> additionalArguments <> ["deps"]) $ ("super" !!. "_buildNodePackage") @@ N.mkNonRecSet- [ "name" $= ("sanitizePackageName" @@ "name")- , N.inherit $ map N.StaticKey ["version"]+ [ inheritStatic ["key", "version"] , "src" $= srcNExpr , "nodeBuildInputs" $= "deps" ] -- | Building a 'YLT.FileRemote' package.@@ -249,20 +263,22 @@ buildPkgFn = buildPkgFnGeneric ["registry", "sha1"] ("fetchurl" @@ N.mkNonRecSet- [ "url" $= ("registry" @@ "name" @@ "version")- , N.inherit $ [N.StaticKey "sha1"] ])+ [ "url" $= ("registry" @@ "key" @@ "version")+ , inheritStatic ["sha1"] ]) -- | Building a 'YLT.GitRemote' package. buildPkgGitFn :: NExpr buildPkgGitFn = buildPkgFnGeneric ["url", "rev", "sha256"] ("fetchgit" @@ N.mkNonRecSet- [ N.inherit $ map N.StaticKey ["url", "rev", "sha256"] ])+ [ inheritStatic ["url", "rev", "sha256"] ]) mkDefGeneric :: PkgData a -> NSym -> [NExpr] -> NExpr mkDefGeneric PkgData{..} buildFnSym additionalArguments = foldl' (@@) (shorten [buildFnSym])- $ [ N.mkStr pkgDataName- , N.mkStr pkgDataVersion ]+ $ [ case pkgDataName of+ YLT.SimplePackageKey n -> N.mkStr n+ YLT.ScopedPackageKey s n -> "sc" @@ N.mkStr s @@ N.mkStr n+ , N.mkStr pkgDataVersion ] <> additionalArguments <> [ N.mkList $ map (N.mkSym selfSym !!.) pkgDataDependencies ]
src/Distribution/Nixpkgs/Nodejs/ResolveLockfile.hs view
@@ -11,7 +11,7 @@ ) where import Protolude-import qualified Control.Monad.Trans.Either as E+import qualified Control.Monad.Trans.Except as E import qualified Data.List.NonEmpty as NE import qualified Data.MultiKeyedMap as MKM import qualified Data.Aeson as Aeson@@ -23,6 +23,9 @@ import qualified Yarn.Lock.Types as YLT +nixPrefetchGitPath :: FilePath+nixPrefetchGitPath = "nix-prefetch-git"+ maxFetchers :: Int maxFetchers = 5 @@ -42,7 +45,7 @@ -> IO (Either (NE.NonEmpty Text) ResolvedLockfile) resolveLockfileStatus msgChan lf = Async.withTaskGroup maxFetchers $ \taskGroup -> do job <- STM.atomically $ Async.mapReduce taskGroup- $ fmap (\(ks, pkg) -> (:[]) <$> (E.runEitherT $ do+ $ fmap (\(ks, pkg) -> (:[]) <$> (E.runExceptT $ do liftIO $ writeChan msgChan (YLT.remote pkg) res <- resolve pkg pure (ks, res)))@@ -53,20 +56,20 @@ (_ , ys) -> pure $ Right $ MKM.fromList YLT.lockfileIkProxy ys where- resolve :: YLT.Package -> E.EitherT Text IO (Resolved YLT.Package)+ resolve :: YLT.Package -> E.ExceptT Text IO (Resolved YLT.Package) resolve pkg = case YLT.remote pkg of YLT.FileRemote{..} -> pure $ r fileSha1 YLT.GitRemote{..} -> r <$> fetchFromGit gitRepoUrl gitRev where r sha = Resolved { hashSum = sha, resolved = pkg } - fetchFromGit :: Text -> Text -> E.EitherT Text IO Text+ fetchFromGit :: Text -> Text -> E.ExceptT Text IO Text fetchFromGit repo rev = do- res <- liftIO $ Process.readProcessWithExitCode "nix-prefetch-git"+ res <- liftIO $ Process.readProcessWithExitCode nixPrefetchGitPath ["--url", toS repo, "--rev", toS rev, "--hash", "sha256"] "" case res of- ((ExitFailure _), _, err) -> E.left $ toS err- (ExitSuccess, out, _) -> E.hoistEither+ ((ExitFailure _), _, err) -> E.throwE $ toS err+ (ExitSuccess, out, _) -> E.ExceptT . pure $ first (\decErr -> "parsing json output failed:\n" <> toS decErr <> "\nThe output was:\n" <> toS out) $ do val <- Aeson.eitherDecode' (toS out)
src/Distribution/Nixpkgs/Nodejs/Utils.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE NoImplicitPrelude, OverloadedStrings, RecordWildCards #-}+{-# LANGUAGE NoImplicitPrelude, OverloadedStrings, RecordWildCards, LambdaCase #-} {-| Description: Misc utils -}@@ -8,4 +8,14 @@ -- | Representation of a PackageKey as nix attribute name. packageKeyToSymbol :: YLT.PackageKey -> Text-packageKeyToSymbol (YLT.PackageKey{..}) = name <> "@" <> npmVersionSpec+packageKeyToSymbol (YLT.PackageKey{..}) =+ packageKeyNameToSymbol name <> "@" <> npmVersionSpec+{-# INLINABLE packageKeyToSymbol #-}++-- | Representation of a PackageKeyName as nix attribute name.+packageKeyNameToSymbol :: YLT.PackageKeyName -> Text+packageKeyNameToSymbol = \case+ YLT.SimplePackageKey n -> n+ YLT.ScopedPackageKey scope n -> "@" <> scope <> "/" <> n+{-# INLINABLE packageKeyNameToSymbol #-}+
src/Distribution/Nodejs/Package.hs view
@@ -13,7 +13,7 @@ , Bin(..), Man(..), Dependencies ) where -import Protolude+import Protolude hiding (packageName) import Control.Monad (fail) import qualified Control.Monad.Writer.Lazy as WL import qualified Data.ByteString.Lazy as BL@@ -50,8 +50,9 @@ -- | Possible warnings from parsing. data Warning = WrongType- { wrongTypeField :: Text- , wrongTypeDefault :: Text }+ { wrongTypeField :: Text -- ^ the field which has a wrong type+ , wrongTypeDefault :: Maybe Text -- ^ the default value, if used+ } | PlainWarning Text -- | The package’s executable files.@@ -71,23 +72,29 @@ -- | Dependencies of a package. type Dependencies = HML.HashMap Text Text +type Warn = WL.WriterT [Warning] AT.Parser+putWarning :: a -> Warning -> Warn a+putWarning a w = WL.writer (a, [w])+ -- | See https://github.com/npm/normalize-package-data for -- normalization steps used by npm itself. instance A.FromJSON LoggingPackage where parseJSON = A.withObject "Package" $ \v -> fmap LoggingPackage . WL.runWriterT $ do let- l :: AT.Parser a -> WL.WriterT [Warning] AT.Parser a+ l :: AT.Parser a -> Warn a l = WL.WriterT . fmap (\a -> (a, [])) tryWarn :: (AT.FromJSON a, Show a)- => Text -> a -> WL.WriterT [Warning] AT.Parser a- tryWarn field def = lift (v .:? field .!= def)- <|> WL.writer (def, [WrongType field (show def)])+ => Text -> a -> Warn a+ tryWarn field def =+ lift (v .:? field .!= def)+ <|> putWarning def (WrongType { wrongTypeField = field+ , wrongTypeDefault = Just (show def) }) name <- l $ v .: "name" version <- l $ v .: "version" description <- tryWarn "description" Nothing homepage <- tryWarn "homepage" Nothing private <- tryWarn "private" False- scripts <- l $ v .:? "scripts" .!= mempty+ scripts <- (parseMapText "scripts" =<< (tryWarn "scripts" mempty)) bin <- parseBin name v man <- l $ parseMan name v license <- tryWarn "license" Nothing@@ -96,7 +103,17 @@ pure Package{..} where - parseBin :: Text -> AT.Object -> WL.WriterT [Warning] AT.Parser Bin+ parseMapText :: Text -> HML.HashMap Text AT.Value+ -> Warn (HML.HashMap Text Text)+ parseMapText fieldPath val =+ HML.mapMaybe identity <$> HML.traverseWithKey tryParse val+ where+ tryParse :: Text -> A.Value -> Warn (Maybe Text)+ tryParse key el = lift (Just <$> AT.parseJSON el)+ <|> putWarning Nothing+ (WrongType { wrongTypeField = fieldPath <> "." <> key+ , wrongTypeDefault = Nothing })+ parseBin :: Text -> AT.Object -> Warn Bin parseBin packageName v = do -- check for existence of these fields binVal <- lift $ optional $ v .: "bin"@@ -105,8 +122,8 @@ -- see npm documentation for more case (binVal, dirBinVal) of (Just _ , Just _) ->- WL.writer (BinFiles mempty, [PlainWarning- "`bin` and `directories.bin` must not exist at the same time."])+ putWarning (BinFiles mempty) $ PlainWarning+ "`bin` and `directories.bin` must not exist at the same time, skipping." -- either "bin" is a direct path, then it’s linked to the package name (Just (A.String path), _) -> pure $ BinFiles $ HML.singleton packageName (toS path)@@ -141,7 +158,13 @@ -- | Convert a @package.json@ parsing warning to plain text. formatWarning :: Warning -> Text-formatWarning = ("Warning: " <>) . \case- (WrongType field def) ->- "Field \"" <> field <> "\" has the wrong type. Defaulting to " <> def <> "."+formatWarning = \case+ WrongType{..} ->+ "Field \""+ <> wrongTypeField+ <> "\" has the wrong type. "+ <> (case wrongTypeDefault of+ Just def -> "Defaulting to " <> def+ Nothing -> "Leaving it out")+ <> "." (PlainWarning t) -> t
src/Nix/Expr/Additions.hs view
@@ -5,7 +5,7 @@ Nix generation helpers. No guarantee of stability (internal!). -} module Nix.Expr.Additions-( stringKey, ($$=), dynamicKey+( stringKey, ($$=), dynamicKey, inheritStatic , simpleParamSet, multiParam , (!!.) , StrQ(..), mkStrQ, mkStrQI@@ -24,7 +24,7 @@ -- | Make a binding, but have the key be a string, not symbol. stringKey :: Text -> NExpr -> Binding NExpr-stringKey k v = NamedVar [dynamicKey k] v+stringKey k v = NamedVar (pure $ dynamicKey k) v nullPos -- | Infix version of 'stringKey'. ($$=) :: Text -> NExpr -> Binding NExpr ($$=) = stringKey@@ -34,9 +34,13 @@ dynamicKey :: Text -> NKeyName NExpr dynamicKey k = DynamicKey $ Plain $ DoubleQuoted [Plain k] +-- | Inherit the given list of symbols.+inheritStatic :: [Text] -> Binding e+inheritStatic names = inherit (map StaticKey names) nullPos+ -- | shortcut to create a list of closed params, like @{ foo, bar, baz }:@ simpleParamSet :: [Text] -> Params NExpr-simpleParamSet = mkParamset . fmap (, Nothing)+simpleParamSet prms = mkParamset (fmap (, Nothing) prms) False -- | shortcut to create a list of multiple params, like @a: b: c:@ multiParam :: [Text] -> NExpr -> NExpr@@ -49,7 +53,7 @@ (!!.) :: NExpr -> Text -> NExpr aset !!. k = Fix $ NSelect aset- [(if isPlainSymbol k then StaticKey else dynamicKey) k] Nothing+ (pure $ (if isPlainSymbol k then StaticKey else dynamicKey) k) Nothing where -- the nix lexer regex for IDs (symbols) is -- [a-zA-Z\_][a-zA-Z0-9\_\'\-]*@@ -73,4 +77,4 @@ -- | Create a double-quoted string from a list of antiquotes/plain strings. mkStrQ = mkStrQtmpl DoubleQuoted -- | Create a single-quoted string from a list of antiquotes/plain strings.-mkStrQI = mkStrQtmpl Indented+mkStrQI = mkStrQtmpl (Indented 2)
tests/TestNpmjsPackage.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE OverloadedStrings, TemplateHaskell, QuasiQuotes, NoImplicitPrelude #-}+{-# LANGUAGE OverloadedStrings, TemplateHaskell, QuasiQuotes, NoImplicitPrelude, LambdaCase #-} module TestNpmjsPackage (tests) where import Protolude@@ -21,34 +21,77 @@ case_binPaths :: Assertion case_binPaths = do- parseSuccess bin (baseAnd [ ("bin", "./abc") ])- >>= assertEqual "bin path" (NP.BinFiles $ HML.fromList- [ ("foopkg", "./abc") ])- parseSuccess bin (baseAnd [ ("directories", A.object [("bin", "./abc")]) ])- >>= assertEqual "bin path" (NP.BinFolder "./abc")- parseSuccess bin (baseAnd [ ("bin", A.object- [ ("one", "./bin/one")- , ("two", "imhere") ]) ])- >>= assertEqual "bin multiple" (NP.BinFiles $ HML.fromList- [ ("one", "./bin/one")- , ("two", "imhere") ])- -- TODO: succeeds with warning; test whether the correct warnings are generated- -- parseFailure bin (baseAnd [ ("bin", "foo")- -- , ("directories", A.object [("bin", "foo")]) ])- parseSuccess bin (baseAnd [])- >>= assertEqual "no bins" (NP.BinFiles mempty)- where- bin = fmap (NP.bin . fst . NP.unLoggingPackage) . A.parseJSON+ let+ parseWithWarningsZoom :: (Eq a, Show a)+ => Text -> A.Value -> (NP.Package -> a) -> a+ -> ([NP.Warning] -> Assertion)+ -> Assertion+ parseWithWarningsZoom name got zoom want warningPred =+ NP.unLoggingPackage <$> parseSuccess got+ >>= \(val, warnings) -> do+ assertEqual (toS name) want (zoom val)+ warningPred warnings+ parseZoom name got zoom want =+ parseWithWarningsZoom name got zoom want (const $ pure ()) -parseSuccess :: (A.Value -> AT.Parser a) -> A.Value -> IO a-parseSuccess p v = case AT.parse p v of+ hasWarning :: (NP.Warning -> Bool) -> [NP.Warning] -> Assertion+ hasWarning warningPred = assertBool "no such warning!" . any warningPred+ wrongType field def = \case+ (NP.WrongType f d) -> field == f && def == d+ _ -> False+ plainWarning = \case+ (NP.PlainWarning _) -> True+ _ -> False++ parseZoom ".bin exists with files"+ (baseAnd [ ("bin", "./abc") ])+ NP.bin+ (NP.BinFiles $ HML.fromList [ ("foopkg", "./abc") ])++ parseZoom ".directories.bin exists with path"+ (baseAnd [ ("directories", A.object [("bin", "./abc")]) ])+ NP.bin+ (NP.BinFolder "./abc")++ parseZoom "multiple .bin files are parsed"+ (baseAnd [ ("bin", A.object+ [ ("one", "./bin/one")+ , ("two", "imhere") ]) ])+ NP.bin+ (NP.BinFiles $ HML.fromList+ [ ("one", "./bin/one")+ , ("two", "imhere") ])++ parseWithWarningsZoom "bin and directories.bin both exist"+ (baseAnd [ ("bin", "foo")+ , ("directories", A.object+ [ ("bin", "foo") ]) ])+ NP.bin+ (NP.BinFiles mempty)+ (hasWarning plainWarning)++ parseZoom "neither .bin nor .directories.bin exis"+ (baseAnd [])+ NP.bin+ (NP.BinFiles mempty)++ parseWithWarningsZoom ".scripts field has a wrong type"+ (baseAnd [ ("scripts", A.object+ [ ("foo", A.object [])+ , ("bar", "imascript") ]) ])+ NP.scripts+ (HML.fromList [ ("bar", "imascript") ])+ (hasWarning (wrongType "scripts.foo" Nothing))++parseSuccess :: (A.FromJSON a) => A.Value -> IO a+parseSuccess v = case A.fromJSON v of (AT.Error err) -> assertFailure err >> panic "not reached" (AT.Success a) -> pure a -parseFailure :: Show a => (A.Value -> AT.Parser a) -> A.Value -> IO ()-parseFailure p v = case AT.parse p v of- (AT.Error _) -> pass- (AT.Success a) -> assertFailure $ "no parse" <> show a+-- parseFailure :: Show a => (A.Value -> AT.Parser a) -> A.Value -> IO ()+-- parseFailure p v = case AT.parse p v of+-- (AT.Error _) -> pass+-- (AT.Success a) -> assertFailure $ "no parse" <> show a tests :: TestTree tests = $(testGroupGenerator)
yarn2nix.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: 9f5e62ecebb812a5457199df157d56a8529dd64b80a8ea3c7fbbbf22d86627f3 name: yarn2nix-version: 0.5.0+version: 0.7.0 synopsis: Convert yarn.lock files to nix expressions description: Convert @yarn.lock@ files to nix expressions. See @yarn2nix@ executable. Contains a nix library to call the generated nix files in @nix-lib/@. Library functions and module names might be restructured in the future. category: Distribution, Nix@@ -21,7 +23,7 @@ LICENSE nix-lib/buildNodePackage.nix nix-lib/default.nix- nix-lib/yarn-lock.nix+ nix-lib/old-version-dependencies.nix README.md shell.nix yarn2nix.nix@@ -31,30 +33,6 @@ location: https://github.com/Profpatsch/yarn2nix library- hs-source-dirs:- src- ghc-options: -Wall- build-depends:- aeson >= 1.0 && < 1.3- , ansi-wl-pprint == 0.6.*- , async-pool == 0.9.*- , base == 4.*- , bytestring == 0.10.*- , containers >= 0.5 && < 0.7- , data-fix >= 0.0.7 && < 0.1- , directory == 1.3.*- , either == 4.4.*- , filepath == 1.4.*- , hnix == 0.3.*- , mtl == 2.2.*- , process == 1.4.*- , protolude == 0.1.*- , regex-tdfa == 1.2.*- , regex-tdfa-text == 1.0.0.*- , stm == 2.4.*- , text == 1.2.*- , unordered-containers == 0.2.*- , yarn-lock == 0.4.* exposed-modules: Distribution.Nixpkgs.Nodejs.Cli Distribution.Nixpkgs.Nodejs.FromPackage@@ -65,98 +43,126 @@ Nix.Expr.Additions other-modules: Paths_yarn2nix+ hs-source-dirs:+ src+ ghc-options: -Wall+ build-depends:+ aeson >=1.0 && <1.3+ , ansi-wl-pprint ==0.6.*+ , async-pool ==0.9.*+ , base ==4.*+ , bytestring ==0.10.*+ , containers >=0.5 && <0.7+ , data-fix ==0.0.7 || ==0.2.0+ , directory ==1.3.*+ , filepath ==1.4.*+ , hnix ==0.5.*+ , mtl ==2.2.*+ , process >=1.4+ , protolude ==0.2.*+ , regex-tdfa ==1.2.*+ , regex-tdfa-text ==1.0.0.*+ , stm ==2.4.*+ , text ==1.2.*+ , transformers ==0.5.*+ , unordered-containers ==0.2.*+ , yarn-lock ==0.5.* default-language: Haskell2010 -executable setup-node-package-paths- main-is: SetupNodePackagePaths.hs+executable node-package-tool+ main-is: NodePackageTool.hs+ other-modules:+ Paths_yarn2nix ghc-options: -Wall build-depends:- aeson >= 1.0 && < 1.3- , ansi-wl-pprint == 0.6.*- , async-pool == 0.9.*- , base == 4.*- , bytestring == 0.10.*- , containers >= 0.5 && < 0.7- , data-fix >= 0.0.7 && < 0.1- , directory == 1.3.*- , either == 4.4.*- , filepath == 1.4.*- , hnix == 0.3.*- , mtl == 2.2.*- , process == 1.4.*- , protolude == 0.1.*- , regex-tdfa == 1.2.*- , regex-tdfa-text == 1.0.0.*- , stm == 2.4.*- , text == 1.2.*- , unordered-containers == 0.2.*- , yarn-lock == 0.4.*- , optparse-applicative == 0.13.*- , unix == 2.7.*+ aeson >=1.0 && <1.3+ , ansi-wl-pprint ==0.6.*+ , async-pool ==0.9.*+ , base ==4.*+ , bytestring ==0.10.*+ , containers >=0.5 && <0.7+ , data-fix ==0.0.7 || ==0.2.0+ , directory ==1.3.*+ , filepath ==1.4.*+ , hnix ==0.5.*+ , mtl ==2.2.*+ , optparse-applicative >=0.13 && <0.15+ , process >=1.4+ , protolude ==0.2.*+ , regex-tdfa ==1.2.*+ , regex-tdfa-text ==1.0.0.*+ , stm ==2.4.*+ , text ==1.2.*+ , transformers ==0.5.*+ , unix ==2.7.*+ , unordered-containers ==0.2.*+ , yarn-lock ==0.5.* , yarn2nix default-language: Haskell2010 executable yarn2nix main-is: Main.hs+ other-modules:+ Paths_yarn2nix ghc-options: -Wall build-depends:- aeson >= 1.0 && < 1.3- , ansi-wl-pprint == 0.6.*- , async-pool == 0.9.*- , base == 4.*- , bytestring == 0.10.*- , containers >= 0.5 && < 0.7- , data-fix >= 0.0.7 && < 0.1- , directory == 1.3.*- , either == 4.4.*- , filepath == 1.4.*- , hnix == 0.3.*- , mtl == 2.2.*- , process == 1.4.*- , protolude == 0.1.*- , regex-tdfa == 1.2.*- , regex-tdfa-text == 1.0.0.*- , stm == 2.4.*- , text == 1.2.*- , unordered-containers == 0.2.*- , yarn-lock == 0.4.*+ aeson >=1.0 && <1.3+ , ansi-wl-pprint ==0.6.*+ , async-pool ==0.9.*+ , base ==4.*+ , bytestring ==0.10.*+ , containers >=0.5 && <0.7+ , data-fix ==0.0.7 || ==0.2.0+ , directory ==1.3.*+ , filepath ==1.4.*+ , hnix ==0.5.*+ , mtl ==2.2.*+ , process >=1.4+ , protolude ==0.2.*+ , regex-tdfa ==1.2.*+ , regex-tdfa-text ==1.0.0.*+ , stm ==2.4.*+ , text ==1.2.*+ , transformers ==0.5.*+ , unordered-containers ==0.2.*+ , yarn-lock ==0.5.* , yarn2nix default-language: Haskell2010 test-suite yarn2nix-tests type: exitcode-stdio-1.0 main-is: Test.hs+ other-modules:+ TestNpmjsPackage+ Paths_yarn2nix hs-source-dirs: tests ghc-options: -Wall build-depends:- aeson >= 1.0 && < 1.3- , ansi-wl-pprint == 0.6.*- , async-pool == 0.9.*- , base == 4.*- , bytestring == 0.10.*- , containers >= 0.5 && < 0.7- , data-fix >= 0.0.7 && < 0.1- , directory == 1.3.*- , either == 4.4.*- , filepath == 1.4.*- , hnix == 0.3.*- , mtl == 2.2.*- , process == 1.4.*- , protolude == 0.1.*- , regex-tdfa == 1.2.*- , regex-tdfa-text == 1.0.0.*- , stm == 2.4.*- , text == 1.2.*- , unordered-containers == 0.2.*- , yarn-lock == 0.4.*- , neat-interpolation == 0.3.*- , protolude == 0.1.*- , tasty == 0.11.*- , tasty-hunit == 0.9.*- , tasty-quickcheck == 0.8.*- , tasty-th == 0.1.7.*+ aeson >=1.0 && <1.3+ , ansi-wl-pprint ==0.6.*+ , async-pool ==0.9.*+ , base ==4.*+ , bytestring ==0.10.*+ , containers >=0.5 && <0.7+ , data-fix ==0.0.7 || ==0.2.0+ , directory ==1.3.*+ , filepath ==1.4.*+ , hnix ==0.5.*+ , mtl ==2.2.*+ , neat-interpolation ==0.3.*+ , process >=1.4+ , protolude ==0.2.*+ , regex-tdfa ==1.2.*+ , regex-tdfa-text ==1.0.0.*+ , stm ==2.4.*+ , tasty >=0.11 && <1.2+ , tasty-hunit >=0.9 && <0.11+ , tasty-quickcheck ==0.8.* || ==0.9.*+ , tasty-th ==0.1.7.*+ , text ==1.2.*+ , transformers ==0.5.*+ , unordered-containers ==0.2.*+ , yarn-lock ==0.5.* , yarn2nix- other-modules:- TestNpmjsPackage default-language: Haskell2010
yarn2nix.nix view
@@ -8,7 +8,7 @@ }: mkDerivation { pname = "yarn2nix";- version = "0.1.0";+ version = "0.6.0"; src = ./.; isLibrary = true; isExecutable = true;