nixfromnpm-0.10.1: nix-libs/nodeLib/default.nix
/*
A set of tools for generating node packages, such as to be imported by
default.nix files generated by nixfromnpm.
*/
{
# Self-reference so that we can pass through to downstream libraries
self
}:
{
# Base set of packages, i.e. nixpkgs.
pkgs,
# nodejs derivation.
nodejs ? pkgs.nodejs-4_x,
# Whether to use npm3 (requires a prebuilt tarball of npm3).
npm3 ? true
} @ args:
let
# Function to replace dots with something
replaceDots = c: replaceChars ["."] [c];
inherit (builtins) readDir removeAttrs length getEnv elemAt hasAttr;
inherit (pkgs.lib) attrNames attrValues filterAttrs flip foldl
hasSuffix hasPrefix removeSuffix replaceChars
optional optionals stringToCharacters
concatStrings tail splitString;
inherit (pkgs.stdenv) isLinux;
# Function to remove the first character of a string.
dropFirstChar = str: concatStrings (tail (stringToCharacters str));
# Concatenate a list of sets.
joinSets = foldl (a: b: a // b) {};
# Builds the extracted nix file. Since of course it can't use npm3,
# being that it hasn't been built yet, we disable npm3 for this.
_npm3 = ((self (args // {npm3 = false;}))
.generatePackages {nodePackagesPath = ../nodePackages;})
.nodePackages.npm;
# Parse the `NPM_AUTH_TOKENS` environment variable to discover
# namespace-token associations and turn them into an attribute set
# which we can use as an input to the fetchPrivateNpm function.
# Split the variable on ':', then turn each k=v element in
# the list into an attribute set and join all of those sets.
namespaceTokens = joinSets (
flip map (splitString ":" (getEnv "NPM_AUTH_TOKENS")) (kvPair:
let kv = splitString "=" kvPair; in
if length kv != 2 then {}
else {"${elemAt kv 0}" = elemAt kv 1;}));
# A function similar to fetchUrl but allows setting of custom headers.
fetchUrlWithHeaders = pkgs.callPackage ./fetchUrlWithHeaders.nix {};
# Uses the parsed namespace tokens to create a function that can
# fetch a private package from an npm repo.
fetchPrivateNpm = {namespace, headers ? {}, ...}@args:
if !(hasAttr namespace namespaceTokens)
then throw "NPM_AUTH_TOKENS does not contain namespace ${namespace}"
else let
Authorization = "Bearer ${namespaceTokens.${namespace}}";
headers = {inherit Authorization;} // headers;
in
fetchUrlWithHeaders (removeAttrs args ["namespace"] // {inherit headers;});
in
rec {
inherit nodejs;
buildNodePackage = import ./buildNodePackage.nix ({
inherit pkgs nodejs buildNodePackage;
neededNatives = [pkgs.python] ++ optionals isLinux [pkgs.utillinux];
} // (if npm3 then {npm = _npm3;} else {}));
# A generic package that will fail to build. This is used to indicate
# packages that are broken, without failing the entire generation of
# a package expression.
brokenPackage = {name, reason}:
let
deriv = pkgs.stdenv.mkDerivation {
name = "BROKEN-${name}";
buildCommand = ''
echo "Package ${name} is broken: ${reason}"
exit 1
'';
passthru.withoutTests = deriv;
passthru.pkgName = name;
passthru.basicName = "BROKEN";
passthru.uniqueName = "BROKEN";
passthru.overrideNodePackage = (_: deriv);
passthru.namespace = null;
passthru.version = "BROKEN";
passthru.override = _: deriv;
passthru.recursiveDeps = [];
passthru.peerDependencies = {};
};
in
deriv;
# The function that a default.nix can call into which will scan its
# directory for all of the package files and generate a big attribute set
# for all of them. Re-exports the `callPackage` function and all of the
# attribute sets, as well as the nodeLib.
generatePackages = {
# Path to find node packages in.
nodePackagesPath,
# Extensions are other node libraries which will be folded into the
# generated one.
extensions ? [],
# If any additional arguments should be made available to callPackage
# (for example for packages which require additional arguments), they
# can be passed in here. Those packages can declare an `extras` argument
# which will contain whatever is passed in here.
extras ? {}
}:
let
callPackageWith = pkgSet: path: overridingArgs: let
inherit (builtins) intersectAttrs functionArgs;
inherit (pkgs.lib) filterAttrs;
# The path must be a function; import it here.
func = import path;
# Get the arguments to the function; e.g. "{a=false; b=true;}", where
# a false value is an argument that has no default.
funcArgs = functionArgs func;
# Take only the arguments that don't have a default.
noDefaults = filterAttrs (_: v: v == false) funcArgs;
# Intersect this set with the package set to create the arguments to
# the function.
satisfyingArgs = intersectAttrs noDefaults pkgSet;
# Override these arguments with whatever's passed in.
actualArgs = satisfyingArgs // overridingArgs;
# Call the function with these args to get a derivation.
deriv = func actualArgs;
in deriv;
nodePackages = joinSets (map (e: e.nodePackages) extensions) //
(import nodePackagesPath {inherit callPackage;});
callPackage = callPackageWith {
inherit fetchUrlWithHeaders namespaceTokens;
inherit pkgs nodePackages buildNodePackage brokenPackage;
inherit extras;
inherit (nodePackages) namespaces;
};
in
{
inherit callPackage namespaceTokens pkgs;
nodePackages = nodePackages // {inherit nodejs;};
nodeLib = self args;
npm3 = _npm3;
};
}