Source: node2nix.js

/**
 * Contains utility functions that generate Nix expressions from NPM package specifications
 * @module node2nix
 */
var fs = require('fs');
var path = require('path');
var slasp = require('slasp');
var nijs = require('nijs');

var CollectionExpression = require('./expressions/CollectionExpression.js').CollectionExpression;
var PackageExpression = require('./expressions/PackageExpression.js').PackageExpression;
var CompositionExpression = require('./expressions/CompositionExpression.js').CompositionExpression;
var DeploymentConfig = require('./DeploymentConfig.js').DeploymentConfig;

function copyNodeEnvExpr(nodeEnvNix, callback) {
    /* Compose a read stream that reads the build expression */
    var rs = fs.createReadStream(path.join(path.dirname(module.filename), "..", "nix", "node-env.nix"));
    rs.on("error", function(err) {
        callback(err);
    });

    /* Compose a write stream that writes the build expression */
    var ws = fs.createWriteStream(nodeEnvNix);
    ws.on("error", function(err) {
        callback(err);
    });
    ws.on("close", function() {
        callback(null);
    });

    /* Pipe the data to actually copy stuff */
    rs.pipe(ws);
}

/**
 * Writes a copy of node-env.nix to a specified path.
 *
 * @function
 * @param {String} nodeEnvNix Path to which the NPM package build expression is written
 * @param {function(String)} callback Callback function that gets invoked if the operation is done.
 *     If an error has occured, the error parameter is set to the error message.
 */
exports.copyNodeEnvExpr = copyNodeEnvExpr;

function npmToNix(inputJSON, outputNix, compositionNix, nodeEnvNix, lockJSON, supplementJSON, supplementNix, production, includePeerDependencies, flatten, nodePackage, registries, noCopyNodeEnv, bypassCache, useFetchGitPrivate, stripOptionalDependencies, callback) {
    var obj = JSON.parse(fs.readFileSync(inputJSON));
    var version = JSON.parse(fs.readFileSync(path.join(__dirname, "..", "package.json"))).version;
    var disclaimer = "# This file has been generated by node2nix " + version + ". Do not edit!\n\n";
    var outputDir = path.dirname(outputNix);
    var baseDir = path.dirname(inputJSON);

    var lock;

    if(lockJSON !== undefined) {
        lock = JSON.parse(fs.readFileSync(lockJSON));
    }

    var deploymentConfig = new DeploymentConfig(registries, production, includePeerDependencies, flatten, nodePackage, outputDir, bypassCache, stripOptionalDependencies);
    var expr;

    var displayLockWarning = false;

    slasp.sequence([
        /* Generate a Nix expression */
        function(callback) {
            if(typeof obj == "object" && obj !== null) {
                if(Array.isArray(obj)) {
                    expr = new CollectionExpression(deploymentConfig, baseDir, obj);
                } else {
                    // Display error if mandatory package.json attributes are not set
                    if(!obj.name) {
                        return callback("Mandatory name attribute is missing in package.json");
                    }

                    // Parse package.json
                    expr = new PackageExpression(deploymentConfig, lock, baseDir, obj.name, baseDir);

                    // Display a warning if we expect a lock file to be used, but the user does not specify it
                    displayLockWarning = bypassCache && !lockJSON && fs.existsSync(path.join(path.dirname(inputJSON), path.basename(inputJSON, ".json")) + "-lock.json");
                }

                expr.resolveDependencies(callback);
            } else {
                callback("The provided JSON file must consist of an object or an array");
            }
        },

        /* Write the output Nix expression to the specified output file */
        function(callback) {
            fs.writeFile(outputNix, disclaimer + nijs.jsToNix(expr, true) + "\n", callback);
        },

        function(callback) {
            /* Generate the supplement Nix expression, if specified */
            if(supplementJSON) {
                var obj = JSON.parse(fs.readFileSync(supplementJSON));

                if(Array.isArray(obj)) {
                    expr = new CollectionExpression(deploymentConfig, baseDir, obj);
                    expr.resolveDependencies(callback);
                } else {
                    callback("The supplement JSON file should be an array");
                }
            } else {
                expr = undefined;
                callback();
            }
        },

        function(callback) {
            if(expr === undefined) {
                callback();
            } else {
                /* Write the supplement Nix expression to the specified output file */
                fs.writeFile(supplementNix, disclaimer + nijs.jsToNix(expr, true) + "\n", callback);
            }
        },

        function(callback) {
            if(noCopyNodeEnv) {
                callback();
            } else {
                /* Copy the node-env.nix expression */
                copyNodeEnvExpr(nodeEnvNix, callback);
            }
        },

        /* Generate and write a Nix composition expression to the specified output file */
        function(callback) {
            expr = new CompositionExpression(compositionNix, nodePackage, nodeEnvNix, outputNix, supplementNix, (supplementJSON !== undefined), useFetchGitPrivate);
            fs.writeFile(compositionNix, disclaimer + nijs.jsToNix(expr, true) + "\n", callback);
        },

        function(callback) {
            /* Display warnings that helps the user with some common mistakes */
            if(displayLockWarning) {
                console.log("\nWARNING: A lock file exists in the repository, yet it is not used in the generation process!");
                console.log("As a result, the deployment of the project may fail.");
                console.log("You probably want to run node2nix with the -l option to use the lock file!");
            }

            if(!Array.isArray(obj) && fs.existsSync(path.join(baseDir, "node_modules"))) {
                console.log("\nWARNING: There is a node_modules/ folder in the root directory of the project!");
                console.log("These packages will be included in the Nix build and influence the outcome.");
                console.log("If you don't want this to happen, then you should remove it before running any");
                console.log("of the Nix commands!");
            }
            callback();
        }
    ], callback);
}

/**
 * Generates a Nix expression from a JSON file representing a Node.js package
 * configuration or an array of NPM dependencies.
 *
 * @function
 * @param {String} inputJSON Path to a package.json or arbitrary JSON file
 * @param {String} outputNix Path to which the generated registry expression is written
 * @param {String} compositionNix Path to which the generated composition expression is written
 * @param {String} nodeEnvNix Path to which the NPM package build expression is written
 * @param {String} supplementJSON Path to a supplement JSON file
 * @param {String} supplementNix Path to which the generated supplement expression is written
 * @param {Boolean} production Indicates whether to deploy the package in production mode
 * @param {Boolean} includePeerDependencies Indicates whether to include peer dependencies with the package
 * @param {Boolean} flatten Indicates whether to create a flat dependency structure in which dependencies are as high as possible in the graph
 * @param {String} nodePackage Name of the Node.js package to use from Nixpkgs
 * @param {Registry[]} registries URL and AuthToken
 * @param {Boolean} noCopyNodeEnv Indicates that no copy of the NPM package build expression should be made
 * @param {Boolean} bypassCache Indicates that the content addressable cache should be bypassed
 * @param {Boolean} useFetchGitPrivate Indicates that the fetchgitPrivate function should be used instead of fetchgit
 * @param {Boolean} stripOptionalDependencies When enabled, the optional dependencies are stripped from the regular dependencies in the NPM registry
 * @param {function(String, String)} callback Callback function that gets invoked when the work is done.
 *     If an error occurs, the error parameter is set to contain the error
 *     If the operation succeeds, it returns a string containing the registry expression containing the packages and all its dependencies
 */
exports.npmToNix = npmToNix;