Source: apc-abstract/task/worker/install_node_modules.js

/**
 * Install node modules. Uses npm install command.
 * @see {@link http://npmjs.org/ | npm}
 * @function task.worker.installNodeModules
 * @param {object} config - Work configuration.
 * @param {object} config.packages - Packages data. Same as npm dependencies.
 * @param {function} callback - Callback when done.
 * @author Taka Okunishi
 *
 */
var spawn = require('child_process').spawn,
    HOME = require('../../lib/env/home'),
    debug = require('./_debug');

exports = module.exports = function (config, callback) {
    var packages = exports._packages(config.packages || {}),
        args = ['install'].concat(packages)
            .concat('--no-bin-links')
            .concat(['--prefix', HOME || config.prefix]);
    if (!packages.length) {
        callback(new Error('No packages to install.'));
        return;
    }
    debug('Packages to install:', JSON.stringify(packages, null, 4));
    var npm = exports._npm(args);
    npm.stdout.pipe(process.stdout);
    var buffer = '';
    npm.stderr.on('data', function (chunk) {
        var data = chunk.toString();
        if (!data) return;
        buffer += data;
        var lines = buffer.split('\n');
        if (lines.length) {
            buffer = lines.pop() || '';
            var line = lines.join('\n');
            if (!line.length) return;
            exports._log(line);
        }
    });
    npm.on('close', function () {
        if (buffer) exports._log(buffer);
        callback();
    });
};

exports._npm = function (args) {
    return spawn('npm', args);
};

/**
 * Format packages data.
 * @param {object} data - Package data.
 * @returns {string[]} - Package names to install
 * @protected
 * @ignore
 */
exports._packages = function (data) {
    return Object.keys(data).map(function (key) {
        var value = data[key];
        return [key, value].join('@');
    });
};

/**
 * Print log
 * @param {string} msg - Message to print
 * @protected
 * @ignore
 */
exports._log = function (msg) {
    var isError = msg.match('ERR!');
    if (isError) {
        console.error(msg);
    } else {
        console.log(msg);
    }
};