lib: self:

let

    fetcherGenerators = { repo ? null
                        , url ? null
                        , ... }:
                        { sha256
                        , commit
                        , ...}: {
      github = self.callPackage ({ fetchFromGitHub }:
        fetchFromGitHub {
          owner = lib.head (lib.splitString "/" repo);
          repo = lib.head (lib.tail (lib.splitString "/" repo));
          rev = commit;
          inherit sha256;
        }
      ) {};
      gitlab = self.callPackage ({ fetchFromGitLab }:
        fetchFromGitLab {
          owner = lib.head (lib.splitString "/" repo);
          repo = lib.head (lib.tail (lib.splitString "/" repo));
          rev = commit;
          inherit sha256;
        }
      ) {};
      git = self.callPackage ({ fetchgit }:
        (fetchgit {
          rev = commit;
          inherit sha256 url;
        }).overrideAttrs(_: {
          GIT_SSL_NO_VERIFY = true;
        })
      ) {};
      bitbucket = self.callPackage ({ fetchhg }:
        fetchhg {
          rev = commit;
          url = "https://bitbucket.com/${repo}";
          inherit sha256;
        }
      ) {};
      hg = self.callPackage ({ fetchhg }:
        fetchhg {
          rev = commit;
          inherit sha256 url;
        }
      ) {};
    };

in {

  melpaDerivation = variant:
                      { ename, fetcher
                      , commit ? null
                      , sha256 ? null
                      , ... }@args:
      let
        sourceArgs = args.${variant};
        version = sourceArgs.version or null;
        deps = sourceArgs.deps or null;
        error = sourceArgs.error or args.error or null;
        hasSource = lib.hasAttr variant args;
        pname = builtins.replaceStrings [ "@" ] [ "at" ] ename;
        broken = ! isNull error;
      in
      if hasSource then
        lib.nameValuePair ename (
          self.callPackage ({ melpaBuild, fetchurl, ... }@pkgargs:
          melpaBuild {
            inherit pname ename commit;
            version = if isNull version then "" else
              lib.concatStringsSep "." (map toString version);
            # TODO: Broken should not result in src being null (hack to avoid eval errors)
            src = if (isNull sha256 || broken) then null else
              lib.getAttr fetcher (fetcherGenerators args sourceArgs);
            recipe = if isNull commit then null else
              fetchurl {
                name = pname + "-recipe";
                url = "https://raw.githubusercontent.com/melpa/melpa/${commit}/recipes/${ename}";
                inherit sha256;
              };
            packageRequires = lib.optionals (! isNull deps)
              (map (dep: pkgargs.${dep} or self.${dep} or null)
                   deps);
            meta = (sourceArgs.meta or {}) // {
              inherit broken;
            };
          }
        ) {}
      )
    else
      null;

}
