Source: cp/cp.js


const path = require('path');
const { spawn } = require('child_process');
const templateParser = require('child-process-template-parser');

/**
 * Node's child_process wrapped in a promise with buffered stdio.
 * @namespace cp
 * @example
 * const { cp } = require('dev-env-lib');
 */
class ChildProcess {

  /**
   * Aggregated output from a child process.
   * @typedef {Object} cp~AggregatedOutput
   * @property {string} output Aggregated stdout and stderr
   * @property {string} stdout Aggregated stdout
   * @property {string} stderr Aggregated stderr
   */

  /**
   * A single path or a sequence of path segments.
   * @typedef {string|string[]} cp~PathSegments
   * @example
   * const singlePath = __dirname;
   * const pathSegments = [__dirname, 'relative/path']
   */

  /**
   * Spawn a new process. Wrap in a promise. Buffer output.
   * @memberOf cp
   * @function spawn
   * @param {cp~PathSegments} options.cwd
   * @param {string} options.command Same as Node's child_process.spawn
   * @param {string[]} [options.args] Same as Node's child_process.spawn
   * @param {Object} [options.nodeSpawnOptions] Node's child_process.spawn options
   * @returns {Promise.<cp~AggregatedOutput>} aggregated output
   * @see [Node's child_process.spawn]{@link https://nodejs.org/dist/latest-v6.x/docs/api/child_process.html#child_process_child_process_spawn_command_args_options}
   */
  spawn(options) {
    options = options || {};
    const args = options.args || [];
    const command = options.command;
    if (!command) return Promise.reject(
      new Error('Missing required option: command'));
    let nodeSpawnOptions = options.nodeSpawnOptions;
    let cwd = options.cwd;
    if (!cwd) return Promise.reject(new Error('Missing required option: cwd'));
    if (cwd instanceof Array) {
      cwd = cwd.reduce((agg, value) => path.resolve(agg, value));
    }
    nodeSpawnOptions = Object.assign({ cwd }, nodeSpawnOptions);
    return new Promise((resolve, reject) => {
      let cp;
      if (args === undefined) cp = spawn(command, nodeSpawnOptions);
      else cp = spawn(command, args, nodeSpawnOptions);
      let output = '';
      let stdout = '';
      let stderr = '';
      cp.stdout.on('data', data => {
        output += data;
        stdout += data;
      });
      cp.stderr.on('data', data => {
        output += data;
        stderr += data;
      });
      cp.on('error', error => {
        reject(error);
      });
      cp.on('close', code => {
        const response = { code, output, stdout, stderr };
        if (code === 0) resolve(response);
        else reject(response);
      });
    });
  }


  /**
   * Spawn a new process using the given template for the command and args.
   * @memberOf cp
   * @function spawnTemplate
   * @param {cp~PathSegments} options.cwd
   * @param {cp~PathSegments} options.templatePath path to template.
   * @param {Object} [options.model] Model object passed into template.
   * @param {Object} [options.nodeSpawnOptions] Node's child_process.spawn options
   * @returns {Promise.<cp~AggregatedOutput>} aggregated output
   * @see [Node's child_process.spawn]{@link https://nodejs.org/dist/latest-v6.x/docs/api/child_process.html#child_process_child_process_spawn_command_args_options}
   */
  spawnTemplate(options) {
    options = options || {};
    let templatePath = options.templatePath;
    if (templatePath instanceof Array) {
      templatePath = templatePath.reduce((agg, value) => {
        return path.resolve(agg, value);
      });
    }
    const model = options.model;
    const nodeSpawnOptions = options.nodeSpawnOptions;
    let cwd = options.cwd;
    if (!cwd) return Promise.reject(
      new Error('Missing required option: cwd'));
    if (cwd instanceof Array) {
      cwd = cwd.reduce((agg, value) => {
        return path.resolve(agg, value);
      });
    }
    const { command, args } = templateParser.parse(templatePath, model);
    return this.spawn({ cwd, command, args, nodeSpawnOptions });
  }

}

module.exports = new ChildProcess();
module.exports.ChildProcess = ChildProcess;