util/esm/run.mjs
'use strict';
import {
EOL
} from 'os';
import spawn from 'cross-spawn';
import when from 'when';
import {
normalize,
join,
sep
} from 'path';
import utilSwitches from './switches.mjs';
import _7zPath from './path.mjs';
/**
* @promise Run
* @param {string} command The command to run.
* @param {Array} switches Options for 7-Zip as an array.
* @progress {string} stdout message.
* @reject {Error} The error issued by 7-Zip.
* @reject {number} Exit code issued by 7-Zip.
*/
export default function (command, switches, override = false) {
return when.promise(function (fulfill, reject, progress) {
// Parse the command variable. If the command is not a string reject the
// Promise. Otherwise transform the command into two variables: the command
// name and the arguments.
if (typeof command !== 'string') {
return reject(new Error('Command must be a string'));
}
// add platform binary to command
let pathTo7z = _7zPath({}, override);
let tmpCmd = command.split(' ')[0];
let cmd = join(pathTo7z.path, tmpCmd);
let args = [command.split(' ')[1]];
// Parse and add command (non-switches parameters) to `args`.
let regexpCommands = /"((?:\\.|[^"\\])*)"/g;
let commands = command.match(regexpCommands);
if (commands) {
commands.forEach(function (c) {
c = c.replace(/\//g, sep);
c = c.replace(/\\/g, sep);
c = normalize(c);
args.push(c);
});
}
// Special treatment for the output switch because it is exposed as a
// parameter in the API and not as a option. Plus wildcards can be passed.
let regexpOutput = /-o"((?:\\.|[^"\\])*)"/g;
let output = command.match(regexpOutput);
if (output) {
args.pop();
let o = output[0];
o = o.replace(/\//g, sep);
o = o.replace(/\\/g, sep);
o = o.replace(/"/g, '');
o = normalize(o);
args.push(o);
}
// Add switches to the `args` array.
let switchesArray = utilSwitches(switches);
switchesArray.forEach(function (s) {
args.push(s);
});
// Remove now double quotes. If present in the spawned process 7-Zip will
// read them as part of the paths (e.g.: create a `"archive.7z"` with
// quotes in the file-name);
args.forEach(function (e, i) {
if (typeof e !== 'string') {
return;
}
if (e.substr(0, 1) !== '-') {
e = e.replace(/^"/, '');
e = e.replace(/"$/, '');
args[i] = e;
}
});
// Add bb2 to args array so we get file info
args.push('-bb2');
// When an stdout is emitted, parse it. If an error is detected in the body
// of the stdout create an new error with the 7-Zip error message as the
// error's message. Otherwise progress with stdout message.
let err;
let reg = new RegExp('Error:(' + EOL + '|)?(.*)', 'i');
let res = {
cmd: cmd,
args: args,
options: {
stdio: 'pipe'
}
};
//console.log('>> ', res.cmd, res.args.join(' '), res.options,' <<');
let run = spawn(res.cmd, res.args, res.options);
run.stderr.on('data', function (data) {
let res = reg.exec(data.toString());
if (res) {
err = new Error(res[2].substr(0, res[2].length - 1));
}
});
run.stdout.on('data', function (data) {
return progress(data.toString());
});
run.on('error', function (err) {
reject(err);
});
run.on('close', function (code) {
if (code === 0) {
return fulfill(args);
}
return reject(err, code);
});
});
};