nixfromnpm 0.1.0.6 → 0.1.0.7
raw patch · 4 files changed
+120/−75 lines, 4 filesdep +containersdep +hnixdep −simple-nixPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
Dependencies added: containers, hnix
Dependencies removed: simple-nix
API changes (from Hackage documentation)
- NixFromNpm: mkDefault :: Record (HashMap SemVer a) -> NixExpr
+ NixFromNpm: callPackageWith :: [Binding NExpr] -> NExpr -> NExpr
+ NixFromNpm: callPackageWithRec :: [Binding NExpr] -> NExpr -> NExpr
+ NixFromNpm: mkDefaultNix :: Record (HashMap SemVer a) -> NExpr
+ NixFromNpm: str :: Text -> NExpr
- NixFromNpm: NpmFetcherState :: [URI] -> Maybe Text -> Record (HashMap SemVer (Either NixExpr ResolvedPkg)) -> Record PackageInfo -> HashSet (Name, SemVer) -> Int -> HashSet Name -> Bool -> NpmFetcherState
+ NixFromNpm: NpmFetcherState :: [URI] -> Maybe Text -> Record (HashMap SemVer (Either NExpr ResolvedPkg)) -> Record PackageInfo -> HashSet (Name, SemVer) -> Int -> HashSet Name -> Bool -> NpmFetcherState
- NixFromNpm: [resolved] :: NpmFetcherState -> Record (HashMap SemVer (Either NixExpr ResolvedPkg))
+ NixFromNpm: [resolved] :: NpmFetcherState -> Record (HashMap SemVer (Either NExpr ResolvedPkg))
- NixFromNpm: _startingExpr :: NixExpr
+ NixFromNpm: _startingExpr :: NExpr
- NixFromNpm: _startingSrc :: Text
+ NixFromNpm: _startingSrc :: String
- NixFromNpm: callPackage :: NixExpr -> NixExpr
+ NixFromNpm: callPackage :: NExpr -> NExpr
- NixFromNpm: distInfoToNix :: DistInfo -> NixExpr
+ NixFromNpm: distInfoToNix :: DistInfo -> NExpr
- NixFromNpm: dumpPkgs :: MonadIO m => String -> Record (HashMap SemVer (Either NixExpr ResolvedPkg)) -> m ()
+ NixFromNpm: dumpPkgs :: MonadIO m => String -> Record (HashMap SemVer (Either NExpr ResolvedPkg)) -> m ()
- NixFromNpm: findExisting :: String -> IO (Record (HashMap SemVer NixExpr))
+ NixFromNpm: findExisting :: String -> IO (Record (HashMap SemVer NExpr))
- NixFromNpm: getPkg :: Name -> Record (HashMap SemVer NixExpr) -> IO (Record (HashMap SemVer (Either NixExpr ResolvedPkg)))
+ NixFromNpm: getPkg :: Name -> Record (HashMap SemVer NExpr) -> IO (Record (HashMap SemVer (Either NExpr ResolvedPkg)))
- NixFromNpm: metaToNix :: PackageMeta -> NixExpr
+ NixFromNpm: metaToNix :: PackageMeta -> NExpr
- NixFromNpm: parseVersion :: String -> IO (Maybe (SemVer, NixExpr))
+ NixFromNpm: parseVersion :: String -> IO (Maybe (SemVer, NExpr))
- NixFromNpm: resolvedPkgToNix :: ResolvedPkg -> NixExpr
+ NixFromNpm: resolvedPkgToNix :: ResolvedPkg -> NExpr
- NixFromNpm: startState :: Record (HashMap SemVer NixExpr) -> [Text] -> Maybe Text -> NpmFetcherState
+ NixFromNpm: startState :: Record (HashMap SemVer NExpr) -> [Text] -> Maybe Text -> NpmFetcherState
Files
- nixfromnpm.cabal +17/−6
- src/Main.hs +5/−6
- src/NixFromNpm/ConvertToNix.hs +93/−58
- src/NixFromNpm/NpmLookup.hs +5/−5
nixfromnpm.cabal view
@@ -2,7 +2,7 @@ -- documentation, see http://haskell.org/cabal/users-guide/ name: nixfromnpm-version: 0.1.0.6+version: 0.1.0.7 synopsis: Generate nix expressions from npm packages. -- description: license: MIT@@ -31,6 +31,7 @@ , mtl , bytestring , unordered-containers+ , containers , parsec , aeson , data-default@@ -42,7 +43,7 @@ , filepath , network-uri , directory- , simple-nix >=0.1.0.4+ , hnix hs-source-dirs: src default-language: Haskell2010 @@ -50,14 +51,23 @@ executable nixfromnpm main-is: Main.hs--- other-modules: NixFromNpm+ other-modules: NixFromNpm.Common+ , NixFromNpm.ConvertToNix+ , NixFromNpm.NpmLookup+ , NixFromNpm.NpmTypes+ , NixFromNpm.NpmVersion+ , NixFromNpm.SemVer+ , NixFromNpm.Parsers.Common+ , NixFromNpm.Parsers.NpmVersion+ , NixFromNpm.Parsers.SemVer other-extensions: NoImplicitPrelude, OverloadedStrings- build-depends: base >=4.8 && <4.9+ build-depends: base >=4.8 && <4.9 , classy-prelude , text , mtl , bytestring , unordered-containers+ , containers , parsec , aeson , data-default@@ -69,7 +79,7 @@ , filepath , network-uri , directory- , simple-nix >=0.1.0.4+ , hnix , docopt hs-source-dirs: src default-language: Haskell2010@@ -86,6 +96,7 @@ , text , bytestring , unordered-containers+ , containers , parsec , io-streams , http-client-streams@@ -102,4 +113,4 @@ , filepath , network-uri , directory- , simple-nix+ , hnix
src/Main.hs view
@@ -23,15 +23,14 @@ \ package\n\ \ --node-version VERSION Use this version of node\n" -getFlag args f = isPresent args (longOption f)- main :: IO () main = do patterns <- parseUsageOrExit usageString args <- parseArgsOrExit patterns =<< getArgs- let args ?? arg = getArgOrExitWith patterns args (argument arg)- pkgName <- map pack $ args ?? "packagename"- path <- map pack $ args ?? "outputpath"+ let getText = map pack . getArgOrExitWith patterns args+ getFlag = isPresent args . longOption+ pkgName <- getText (argument "packagename")+ path <- getText (longOption "output") dumpPkgFromOptions $ (defaultOptions pkgName path) {- nfnoNoCache = args `getFlag` "no-cache"+ nfnoNoCache = getFlag "no-cache" }
src/NixFromNpm/ConvertToNix.hs view
@@ -6,123 +6,158 @@ import Data.HashMap.Strict (HashMap) import qualified Data.HashMap.Strict as H+import Data.Map (Map)+import qualified Data.Map as M import Data.Text (Text) import qualified Data.Text as T import NixFromNpm.Common-import Nix.Expr+import Nix.Types import Nix.Parser+import Nix.Pretty (prettyNix) import NixFromNpm.NpmTypes import NixFromNpm.SemVer import NixFromNpm.Parsers.SemVer import NixFromNpm.NpmLookup -_startingSrc :: Text-_startingSrc = unlines [- "{nixpkgs ? import <nixpkgs> {}}:",- "let",- " allPkgs = nixpkgs // nodePkgs // {inherit (nixpkgs.nodePackages)",- " buildNodePackage;};",- " callPackage = pth: overrides: let",- " f = import pth;",- " in",- " f ((builtins.intersectAttrs (builtins.functionArgs f) allPkgs)",- " // overrides);",- " nodePkgs = byVersion // defaults;",- "in",- "nodePkgs"- ]+_startingSrc :: String+_startingSrc = "\+ \{nixpkgs ? import <nixpkgs> {}}:\n\+ \let\n\+ \ allPkgs = nixpkgs // nodePkgs // {inherit (nixpkgs.nodePackages)\n\+ \ buildNodePackage;};\n\+ \ callPackage = pth: overrides: let\n\+ \ f = import pth;\n\+ \ in\n\+ \ f ((builtins.intersectAttrs (builtins.functionArgs f) allPkgs)\n\+ \ // overrides);\n\+ \ nodePkgs = byVersion // defaults;\n\+ \in\n\+ \nodePkgs" -_startingExpr :: NixExpr-_startingExpr = fromRight $ parseNix _startingSrc+_startingExpr :: NExpr+_startingExpr = case parseNixString _startingSrc of+ Success e -> e+ Failure e -> error $ unlines ["Starting source failed to parse:", show e] -callPackage :: NixExpr -> NixExpr-callPackage e = Apply (Apply (Var "callPackage") e) (Set False [])+callPackage :: NExpr -> NExpr+callPackage = callPackageWith [] +callPackageWith :: [Binding NExpr] -> NExpr -> NExpr+callPackageWith args e = mkApp (mkApp (mkSym "callPackage") e)+ (mkNonRecSet args)++callPackageWithRec :: [Binding NExpr] -> NExpr -> NExpr+callPackageWithRec args e = mkApp (mkApp (mkSym "callPackage") e)+ (mkRecSet args)++-- | Turns a string into one that can be used as an identifier. fixName :: Name -> Name fixName = T.replace "." "-" +-- | Converts a package name and semver into an identifier. toDepName :: Name -> SemVer -> Name toDepName name (a, b, c) = concat [fixName name, "_", pack $ intercalate "-" $ map show [a, b, c]] +-- | Gets the .nix filename of a semver. E.g. (0, 1, 2) -> 0.1.2.nix toDotNix :: SemVer -> Text toDotNix (a,b,c) = pack $ intercalate "." (map show [a,b,c]) <> ".nix" -distInfoToNix :: DistInfo -> NixExpr-distInfoToNix DistInfo{..} = Apply (Var "fetchurl") $ Set False- [ "url" =$= str diUrl,- "sha1" =$= str diShasum ]+-- | Creates a doublequoted string from some text.+str :: Text -> NExpr+str = mkStr DoubleQuoted +-- | Converts distinfo into a nix fetchurl call.+distInfoToNix :: DistInfo -> NExpr+distInfoToNix DistInfo{..} = mkApp (mkSym "fetchurl") $ mkNonRecSet+ [ "url" `bindTo` str diUrl,+ "sha1" `bindTo` str diShasum ]++-- | Tests if there is information in the package meta. metaNotEmpty :: PackageMeta -> Bool metaNotEmpty PackageMeta{..} = isJust pmDescription -metaToNix :: PackageMeta -> NixExpr+-- | Converts package meta to a nix expression.+metaToNix :: PackageMeta -> NExpr metaToNix PackageMeta{..} = case pmDescription of- Nothing -> Set False []- Just d -> Set False ["description" =$= str d]+ Nothing -> mkNonRecSet []+ Just d -> mkNonRecSet ["description" `bindTo` str d] -resolvedPkgToNix :: ResolvedPkg -> NixExpr+-- | Converts a resolved package object into a nix expression. The expresion+-- will be a function where the arguments are its dependencies, and its result+-- is a call to `buildNodePackage`.+resolvedPkgToNix :: ResolvedPkg -> NExpr resolvedPkgToNix ResolvedPkg{..} = do- let deps = map (Var . uncurry toDepName) $ H.toList rpDependencies+ let -- Get a string representation of each dependency in name-version format.+ deps = map (mkSym . uncurry toDepName) $ H.toList rpDependencies+ -- Get the parameters of the package function (deps + utility functions). _funcParams = map (uncurry toDepName) (H.toList rpDependencies) <> ["buildNodePackage", "fetchurl"]- funcParams = toKwargs $ map (\x -> (x, Nothing)) _funcParams- let args = Set False $ catMaybes [- Just $ "name" =$= str rpName,- Just $ "version" =$= (str $ renderSV rpVersion),- Just $ "src" =$= distInfoToNix rpDistInfo,- maybeIf (length deps > 0) $ "deps" =$= List deps,- maybeIf (metaNotEmpty rpMeta) $ "meta" =$= metaToNix rpMeta+ -- None of these have defaults, so put them into pairs with Nothing.+ funcParams = mkFormalSet $ map (\x -> (x, Nothing)) _funcParams+ let args = mkNonRecSet $ catMaybes [+ Just $ "name" `bindTo` str rpName,+ Just $ "version" `bindTo` (str $ renderSV rpVersion),+ Just $ "src" `bindTo` distInfoToNix rpDistInfo,+ maybeIf (length deps > 0) $ "deps" `bindTo` mkList deps,+ maybeIf (metaNotEmpty rpMeta) $ "meta" `bindTo` metaToNix rpMeta ]- Function funcParams $ Apply (Var "buildNodePackage") args+ mkFunction funcParams $ mkApp (mkSym "buildNodePackage") args -mkDefault :: Record (HashMap SemVer a) -> NixExpr-mkDefault rec = do- let mkPath name ver = fromText $ concat ["./", name, "/", toDotNix ver]- mkAssign name ver = Assign [Plain $ toDepName name ver]- (callPackage $ Path $ mkPath name ver)- mkAssigns name vers = map (mkAssign name) vers+-- | Creates the `default.nix` file that is the top-level expression we are+-- generating.+mkDefaultNix :: Record (HashMap SemVer a) -> NExpr+mkDefaultNix rec = do+ let mkPath' = mkPath False . unpack+ toPath name ver = mkPath' $ concat ["./", name, "/", toDotNix ver]+ mkBinding name ver = toDepName name ver + `bindTo` callPackage (toPath name ver)+ mkBindings name vers = map (mkBinding name) vers mkDefVer name vers = do- Assign [Plain $ fixName name] (Var $ toDepName name $ maximum vers)+ fixName name `bindTo` mkSym (toDepName name $ maximum vers) -- This bit of map gymnastics will create a list of pairs of names -- with all of the versions of that name that we have. versOnly = map (map (map fst)) $ H.toList $ map H.toList rec- byVersion = Set False $ concatMap (uncurry mkAssigns) versOnly- defaults = With (Var "byVersion") $- Set False $ map (uncurry mkDefVer) versOnly- newAssigns = ["byVersion" =$= byVersion, "defaults" =$= defaults]- Function params (Let assigns e) = _startingExpr- Function params (Let (assigns <> newAssigns) e)+ byVersion = mkNonRecSet $ concatMap (uncurry mkBindings) versOnly+ defaults = mkWith (mkSym "byVersion") $+ mkNonRecSet $ map (uncurry mkDefVer) versOnly+ newBindings = ["byVersion" `bindTo` byVersion, + "defaults" `bindTo` defaults]+ modifyFunctionBody (appendBindings newBindings) _startingExpr dumpPkgs :: MonadIO m => String- -> Record (HashMap SemVer (Either NixExpr ResolvedPkg))+ -> Record (HashMap SemVer (Either NExpr ResolvedPkg)) -> m () dumpPkgs path rPkgs = liftIO $ do createDirectoryIfMissing True path withDir path $ forM_ (H.toList rPkgs) $ \(pkgName, pkgVers) -> do- writeFile "default.nix" $ renderIndented 2 $ mkDefault rPkgs+ writeFile "default.nix" $ show $ prettyNix $ mkDefaultNix rPkgs let subdir = path </> unpack pkgName createDirectoryIfMissing False subdir withDir subdir $ forM_ (H.toList pkgVers) $ \(ver, rpkg) -> do let nixexpr = case rpkg of+ -- A left value means it had already existed; we can simply+ -- dump out the package we had before. Left e -> e+ -- A right value means it's a ResolvedPkg object; in this case+ -- we convert it to a nix expression. Right r -> resolvedPkgToNix r- writeFile (unpack $ toDotNix ver) $ renderIndented 2 nixexpr+ writeFile (unpack $ toDotNix ver) $ show $ prettyNix nixexpr -parseVersion :: String -> IO (Maybe (SemVer, NixExpr))+parseVersion :: String -> IO (Maybe (SemVer, NExpr)) parseVersion pth = do case parseSemVer . pack $ dropSuffix ".nix" $ takeBaseName pth of Left _ -> return Nothing -- not a version file- Right version -> parseNix . pack <$> readFile pth >>= \case- Left err -> do+ Right version -> parseNixString . pack <$> readFile pth >>= \case+ Failure err -> do putStrsLn [pack pth, " failed to parse: ", pack $ show err] return Nothing -- invalid nix, should overwrite- Right expr -> return $ Just (version, expr)+ Success expr -> return $ Just (version, expr) -findExisting :: String -> IO (Record (HashMap SemVer NixExpr))+findExisting :: String -> IO (Record (HashMap SemVer NExpr)) findExisting path = do putStrLn "Searching for existing expressions..." doesDirectoryExist path >>= \case
src/NixFromNpm/NpmLookup.hs view
@@ -29,13 +29,13 @@ import NixFromNpm.Parsers.SemVer import NixFromNpm.NpmVersion import NixFromNpm.Parsers.NpmVersion-import Nix.Expr+import Nix.Types -------------------------------------------------------------------------- data NpmFetcherState = NpmFetcherState { registries :: [URI], githubAuthToken :: Maybe Text,- resolved :: Record (HashMap SemVer (Either NixExpr ResolvedPkg)),+ resolved :: Record (HashMap SemVer (Either NExpr ResolvedPkg)), pkgInfos :: Record PackageInfo, -- For cycle detection. currentlyResolving :: HashSet (Name, SemVer),@@ -379,7 +379,7 @@ Nothing -> errorC ["Invalid URI: ", txt] Just uri -> uri -startState :: Record (HashMap SemVer NixExpr)+startState :: Record (HashMap SemVer NExpr) -> [Text] -> Maybe Text -> NpmFetcherState@@ -421,8 +421,8 @@ (Right x, state) -> return (x, state) getPkg :: Name- -> Record (HashMap SemVer NixExpr)- -> IO (Record (HashMap SemVer (Either NixExpr ResolvedPkg)))+ -> Record (HashMap SemVer NExpr)+ -> IO (Record (HashMap SemVer (Either NExpr ResolvedPkg))) getPkg name existing = do let range = Gt (0, 0, 0) state <- startState existing <$> getRegistries <*> getToken