child process | | test child process | Search

The code utilizes the child_process and importer modules to execute external Bash commands and handle errors, with functions such as bashToExec and execCmd to prepare and run the commands. The execCmd function creates a promise that resolves with the output of the executed command and logs the command and its output if not in quiet mode.

Run example

npm run import -- "spawn child process"

spawn child process

var {exec} = require('child_process');
var path = require('path');
var importer = require('../Core');

var multiline = process.platform === 'win32' ? '^\n ' : '\\\n ';
var multicmd = process.platform === 'win32' ? ' ^\n&& ' : ' \\\n&& ';

// read cmd template and execute each line?
function bashToExec(code) {
    return code
        .split(/\n/ig)
        .map(l => '{EXEC} ' + l)
        .join('\n')
        .replace(/\\((\s*\n)+\{EXEC\})+\s*&&/ig, multicmd)
        .replace(/\\((\s*\n)+\{EXEC\})+\s*/ig, multiline);
}

function execCmd(script, options) {
    if(typeof options == 'undefined') options = {}
    // TODO: fix current working directory matching project name in the cwd: option
    // add some run commands to the bash script
    return importer.runAllPromises(
        bashToExec(script)
            .split(/\s*\{EXEC\}\s*/ig)
            .filter(r => r.trim() !== '')
            .map(r => (resolve, reject) => {
                if(!options.quiet) console.log('spawning process ' + r)
                const ps = exec(r.replace(/\$[0-9]+/ig, i => process.argv[parseInt(i.substr(1))+1] || ''), Object.assign(options || {}, {maxBuffer: 1024 * 50000}),  (err, result) => {
                    if (err) {
                        return reject(err);
                    }
                    return resolve(result);
                });
                let errors = '';
                let data = '';
                ps.stderr.on('data', d => {
                    if(!options.quiet) console.error(d);
                    data += d;
                });
                ps.stdout.on('data', d => {
                    if(!options.quiet) console.log(d);
                    errors += d;
                });
                ps.on('exit', (code, signal) => {
                    if(code > 0) {
                        const err = new Error(`child process exited with code ${code} and signal ${signal}`);
                        err.data = data + errors;
                        reject(err);
                    }
                });
            })
    );
};
module.exports = execCmd;

//        "test": "concurrently --kill-others --success first \"mocha **/*.spec.js\" \"node ./zuora-eloqua-express-mock.js\" ",
 

What the code could have been:

const { spawn } = require('child_process');
const path = require('path');
const { runAllPromises } = require('../Core');

const MULTILINE = process.platform === 'win32'? '^\n': '\\\n ';
const MULTICMD = process.platform === 'win32'?'^\n&&':'\\\n&& ';

function bashToExec(code) {
  return code
   .split('\n')
   .map((line) => ({ type: 'line', content: line }))
   .map((line) => ({ type: 'cmd', content: `{EXEC} ${line.content}` }));
}

function resolveVariables(script) {
  return script
   .split(/\n/)
   .map((line) => ({ type: 'line', content: line }))
   .map((line) => {
      const variables = line.content.match(/\$([0-9]+)/g);
      if (variables) {
        variables.forEach((variable) => {
          line.content = line.content.replace(variable, process.argv[parseInt(variable.substr(1)) + 1] || '');
        });
      }
      return line;
    })
   .map((line) => line.content)
   .join('\n');
}

function execCmd(script, options = {}) {
  const commands = bashToExec(script)
   .map((cmd) => cmd.content)
   .filter((cmd) => cmd.trim()!== '');

  const promises = commands.map((cmd) => {
    const command = resolveVariables(cmd);
    if (!options.quiet) console.log(`spawning process: ${command}`);
    return new Promise((resolve, reject) => {
      const childProcess = spawn(command, {
        maxBuffer: 1024 * 50000,
      });

      let errors = '';
      let data = '';
      childProcess.stderr.on('data', (d) => {
        if (!options.quiet) console.error(d);
        data += d;
      });
      childProcess.stdout.on('data', (d) => {
        if (!options.quiet) console.log(d);
        errors += d;
      });
      childProcess.on('exit', (code, signal) => {
        if (code > 0) {
          const error = new Error(`child process exited with code ${code} and signal ${signal}`);
          error.data = data + errors;
          reject(error);
        } else {
          resolve(data);
        }
      });
    });
  });

  return runAllPromises(promises).catch((err) => {
    if (err.data) console.error(err.data);
    throw err;
  });
}

module.exports = execCmd;

Code Breakdown

Requirements and Variables

The code requires the following modules:

Variables are defined:

Functions

bashToExec(code)

execCmd(script, options)

Execution and Error Handling