/**
* 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;