extra-child-process
Version:
Useful additions to inbuilt child_process module.
296 lines (293 loc) • 10.4 kB
JavaScript
import { EOL } from 'os';
import * as C from 'child_process';
export { ChildProcess, execFileSync, execSync, fork, spawn, spawnSync } from 'child_process';
import * as F from 'fs';
import * as P from 'path';
class ExecAsyncHandler {
constructor() {
this.resolve = null;
this.reject = null;
this.callback = (err, stdout, stderr) => {
if (err != null)
this.reject(Object.assign(err, { stdout, stderr }));
else
this.resolve({ stdout, stderr });
};
this.promise = new Promise((resolve, reject) => {
this.resolve = resolve;
this.reject = reject;
});
}
}
function execAsync(command, options) {
var e = new ExecAsyncHandler();
var child = C.exec(command, options, e.callback);
return Object.assign(e.promise, { child });
}
function exec(...args) {
if (typeof args[args.length - 1] === "function")
C.exec.apply(null, args);
else
return execAsync.apply(null, args);
}
function execFileAsync(file, args, options) {
var e = new ExecAsyncHandler();
var _args = args instanceof Array ? args : null;
var _options = args instanceof Array ? options : args;
var child = C.execFile(file, _args, _options, e.callback);
return Object.assign(e.promise, { child });
}
function execFile(...args) {
if (typeof args[args.length - 1] === "function")
C.execFile.apply(null, args);
else
return execFileAsync.apply(null, args);
}
const MAX_BUFFER = 1024 * 1024;
class StreamBuffer {
constructor(stream, encoding = "buffer", maxBuffer = MAX_BUFFER) {
this.size = 0;
this.data = [];
this.encoding = encoding;
if (encoding && encoding !== "buffer")
stream.setEncoding(encoding);
stream.on("data", chunk => {
if (this.size + chunk.length > maxBuffer)
return;
this.data.push(chunk);
this.size += chunk.length;
});
}
value() {
if (!this.encoding || this.encoding === "buffer")
return Buffer.concat(this.data);
return this.data.join("");
}
}
function spawnAsync(command, args, options) {
var _args = args instanceof Array ? args : null;
var _options = args instanceof Array ? options : args;
var encoding = (_options === null || _options === void 0 ? void 0 : _options.encoding) || "buffer";
var maxBuffer = (_options === null || _options === void 0 ? void 0 : _options.maxBuffer) || MAX_BUFFER;
var child = C.spawn(command, _args, _options);
var outbuf = new StreamBuffer(child.stdout, encoding, maxBuffer);
var errbuf = new StreamBuffer(child.stderr, encoding, maxBuffer);
var promise = new Promise((resolve, reject) => {
child.on("error", err => {
var { pid, exitCode, signalCode } = child;
var stdout = outbuf.value();
var stderr = errbuf.value();
var output = [null, stdout, stderr];
reject(Object.assign(err, { pid, output, stdout, stderr, status: exitCode, signal: signalCode }));
});
child.on("close", (code, signal) => {
var { pid } = child;
var stdout = outbuf.value();
var stderr = errbuf.value();
var output = [null, stdout, stderr];
resolve({ pid, output, stdout, stderr, status: code, signal });
});
});
return Object.assign(promise, { child });
}
function whichWin32(dir, entries, exts, ft) {
var apath = null, afile = null;
var aprio = Number.MAX_SAFE_INTEGER;
for (var e of entries) {
if (!e.isFile())
continue;
var ext = P.extname(e.name);
var file = P.basename(e.name, ext);
if (afile != null && file !== afile)
break;
if (!ft(file))
continue;
var prio = exts.indexOf((ext || ".?").toLowerCase());
if (prio < 0 || prio >= aprio)
continue;
apath = P.join(dir, e.name);
afile = file;
aprio = prio;
}
return apath;
}
function whichAllWin32(dir, entries, exts, ft) {
var a = [];
for (var e of entries) {
if (!e.isFile())
continue;
var ext = P.extname(e.name);
var file = P.basename(e.name, ext);
if (!ft(file))
continue;
var prio = exts.indexOf((ext || ".?").toLowerCase());
if (prio >= 0)
a.push({ file, prio, path: P.join(dir, e.name) });
}
a.sort((a, b) => a.file.localeCompare(b.file) || a.prio - b.prio);
return a.map(x => x.path);
}
function whichLinuxSync(dir, entries, ft) {
for (var e of entries) {
if (!e.isFile())
continue;
if (!ft(e.name))
continue;
var path = P.join(dir, e.name);
try {
F.accessSync(path, F.constants.X_OK);
}
catch (_a) {
continue;
}
return path;
}
return null;
}
function whichAllLinuxSync(dir, entries, ft) {
var a = [];
for (var e of entries) {
if (!e.isFile())
continue;
if (!ft(e.name))
continue;
var path = P.join(dir, e.name);
try {
F.accessSync(path, F.constants.X_OK);
a.push(path);
}
catch (_a) { }
}
return a.sort();
}
function whichLinuxAsync(dir, entries, ft) {
return new Promise(resolve => {
var n = 0, paths = [];
var best = Number.MAX_SAFE_INTEGER;
for (var e of entries) {
if (!e.isFile())
continue;
if (!ft(e.name))
continue;
let i = paths.push(null) - 1;
let path = P.join(dir, e.name);
F.access(path, F.constants.X_OK, err => {
paths[i] = err ? "" : path;
--n;
if (!err)
best = Math.min(best, i);
if (best === Number.MAX_SAFE_INTEGER) {
if (n === 0)
resolve(null);
}
else if (!paths.slice(0, best).includes(null))
resolve(paths[best]);
});
++n;
}
});
}
function whichAllLinuxAsync(dir, entries, ft) {
return new Promise(resolve => {
var a = [], n = 0;
for (var e of entries) {
if (!e.isFile())
continue;
if (!ft(e.name))
continue;
let path = P.join(dir, e.name);
F.access(path, F.constants.X_OK, err => {
if (!err)
a.push(path);
if (--n === 0)
resolve(a.sort());
});
++n;
}
});
}
function whichFnSync(dirs, exts, ft) {
for (var dir of dirs) {
var entries = F.readdirSync(dir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
var path = EOL === "\n" ? whichLinuxSync(dir, entries, ft) : whichWin32(dir, entries, exts, ft);
if (path)
return path;
}
return null;
}
function whichAllFnSync(dirs, exts, ft) {
var a = [];
for (var dir of dirs) {
var entries = F.readdirSync(dir, { withFileTypes: true });
var paths = EOL === "\n" ? whichAllLinuxSync(dir, entries, ft) : whichAllWin32(dir, entries, exts, ft);
a.push(...paths);
}
return a;
}
async function whichFnAsync(dirs, exts, ft) {
var pathps = [];
for (let dir of dirs) {
var p = F.promises.readdir(dir, { withFileTypes: true }).then(entries => entries.sort((a, b) => a.name.localeCompare(b.name)));
pathps.push(p.then(entries => EOL === "\n" ? whichLinuxAsync(dir, entries, ft) : whichWin32(dir, entries, exts, ft)));
}
for (var pathp of pathps) {
var path = await pathp;
if (path)
return path;
}
return null;
}
async function whichAllFnAsync(dirs, exts, ft) {
var pathps = [], a = [];
for (let dir of dirs) {
var p = F.promises.readdir(dir, { withFileTypes: true });
pathps.push(p.then(entries => EOL === "\n" ? whichAllLinuxAsync(dir, entries, ft) : whichAllWin32(dir, entries, exts, ft)));
}
for (var pathp of pathps)
a.push(...(await pathp));
return a;
}
function whichDirs(options) {
var sep = EOL === "\n" ? ":" : ";";
var dirs = (options === null || options === void 0 ? void 0 : options.paths) || process.env.PATH.split(sep);
return EOL !== "\n" && (options === null || options === void 0 ? void 0 : options.cwd) ? new Set([options.cwd, ...dirs]) : new Set(dirs);
}
function whichExts(options) {
return (options === null || options === void 0 ? void 0 : options.extnames) || (process.env.PATHEXT || "").toLowerCase().split(";");
}
function whichTestFunction(cmd) {
if (typeof cmd === "function")
return cmd;
if (typeof cmd === "string")
return file => file === cmd;
return file => cmd.test(file);
}
function whichSync(cmd, options) {
return whichFnSync(whichDirs(options), whichExts(options), whichTestFunction(cmd));
}
function whichAllSync(cmd, options) {
return whichAllFnSync(whichDirs(options), whichExts(options), whichTestFunction(cmd));
}
function whichAsync(cmd, options) {
return whichFnAsync(whichDirs(options), whichExts(options), whichTestFunction(cmd));
}
function whichAllAsync(cmd, options) {
return whichAllFnAsync(whichDirs(options), whichExts(options), whichTestFunction(cmd));
}
function which(cmd, options, callback) {
if (typeof callback === "function")
whichAsync(cmd, options).then(bin => callback(null, bin), callback);
else if (typeof options === "function")
whichAsync(cmd).then(bin => options(null, bin), options);
else
return whichAsync(cmd, options);
}
function whichAll(cmd, options, callback) {
if (typeof callback === "function")
whichAllAsync(cmd, options).then(bin => callback(null, bin), callback);
else if (typeof options === "function")
whichAllAsync(cmd).then(bin => options(null, bin), options);
else
return whichAllAsync(cmd, options);
}
export { exec, execAsync, execFile, execFileAsync, spawnAsync, which, whichAll, whichAllAsync, whichAllSync, whichAsync, whichSync };